1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /*
7 ** File:            switch.cpp
8 ** Description:     trying to time context switches
9 */
10 
11 #include "rccv.h"
12 #include "rcinrval.h"
13 #include "rclock.h"
14 #include "rcthread.h"
15 
16 #include <prio.h>
17 #include <prlog.h>
18 #include <prprf.h>
19 #include <plerror.h>
20 #include <plgetopt.h>
21 
22 #include <stdlib.h>
23 
24 #define INNER_LOOPS 100
25 #define DEFAULT_LOOPS 100
26 #define DEFAULT_THREADS 10
27 
28 static PRFileDesc *debug_out = NULL;
29 static PRBool debug_mode = PR_FALSE, verbosity = PR_FALSE, failed = PR_FALSE;
30 
31 class Home: public RCCondition
32 {
33 public:
34     virtual ~Home();
35     Home(Home *link, RCLock* ml);
36 
37 public:
38     Home *next;
39     RCLock* ml;
40     PRBool twiddle;
41 };  /* Home */
42 
~Home()43 Home::~Home() { }
44 
Home(Home * link,RCLock * lock)45 Home::Home(Home *link, RCLock* lock): RCCondition(lock)
46 {
47     ml = lock;
48     next = link;
49     twiddle = PR_FALSE;
50 }  /* Home::Home */
51 
52 class Shared: public Home, public RCThread
53 {
54 public:
55     Shared(RCThread::Scope scope, Home* link, RCLock* ml);
56 
57 private:
58     ~Shared();
59     void RootFunction();
60 };  /* Shared */
61 
Shared(RCThread::Scope scope,Home * link,RCLock * lock)62 Shared::Shared(RCThread::Scope scope, Home* link, RCLock* lock):
63     Home(link, lock), RCThread(scope, RCThread::joinable) { }
64 
~Shared()65 Shared::~Shared() { }
66 
RootFunction()67 void Shared::RootFunction()
68 {
69     PRStatus status = PR_SUCCESS;
70     while (PR_SUCCESS == status)
71     {
72         RCEnter entry(ml);
73         while (twiddle && (PR_SUCCESS == status)) {
74             status = Wait();
75         }
76         if (verbosity) {
77             PR_fprintf(debug_out, "+");
78         }
79         twiddle = PR_TRUE;
80         next->twiddle = PR_FALSE;
81         next->Notify();
82     }
83 }  /* Shared::RootFunction */
84 
Help(void)85 static void Help(void)
86 {
87     debug_out = PR_STDOUT;
88 
89     PR_fprintf(
90         debug_out, "Usage: >./switch [-d] [-c n] [-t n] [-T n] [-G]\n");
91     PR_fprintf(
92         debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS);
93     PR_fprintf(
94         debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS);
95     PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
96     PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
97     PR_fprintf(debug_out, "-G n\tglobal threads only (default: FALSE)\n");
98     PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
99 }  /* Help */
100 
main(PRIntn argc,char ** argv)101 PRIntn main(PRIntn argc, char **argv)
102 {
103     PLOptStatus os;
104     PRStatus status;
105     PRBool help = PR_FALSE;
106     PRUintn concurrency = 1;
107     RCThread::Scope thread_scope = RCThread::local;
108     PRUintn thread_count, inner_count, loop_count, average;
109     PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS;
110     PLOptState *opt = PL_CreateOptState(argc, argv, "hdvc:t:C:G");
111     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
112     {
113         if (PL_OPT_BAD == os) {
114             continue;
115         }
116         switch (opt->option)
117         {
118             case 'v':  /* verbose mode */
119                 verbosity = PR_TRUE;
120             case 'd':  /* debug mode */
121                 debug_mode = PR_TRUE;
122                 break;
123             case 'c':  /* loop counter */
124                 loop_limit = atoi(opt->value);
125                 break;
126             case 't':  /* thread limit */
127                 thread_limit = atoi(opt->value);
128                 break;
129             case 'C':  /* Concurrency limit */
130                 concurrency = atoi(opt->value);
131                 break;
132             case 'G':  /* global threads only */
133                 thread_scope = RCThread::global;
134                 break;
135             case 'h':  /* help message */
136                 Help();
137                 help = PR_TRUE;
138                 break;
139             default:
140                 break;
141         }
142     }
143     PL_DestroyOptState(opt);
144 
145     if (help) {
146         return -1;
147     }
148 
149     if (PR_TRUE == debug_mode)
150     {
151         debug_out = PR_STDOUT;
152         PR_fprintf(debug_out, "Test parameters\n");
153         PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit);
154         PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit);
155         PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
156         PR_fprintf(
157             debug_out, "\tThread type: %s\n",
158             (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
159     }
160 
161     /*
162     ** The interesting part starts here
163     */
164     RCLock lock;
165     Shared* shared;
166     Home home(NULL, &lock);
167     Home* link = &home;
168     RCInterval timein, timeout = 0;
169 
170     /* Build up the string of objects */
171     for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
172     {
173         shared = new Shared(thread_scope, link, &lock);
174         shared->Start();  /* make it run */
175         link = (Home*)shared;
176     }
177 
178     /* Pass the message around the horn a few times */
179     for (loop_count = 1; loop_count <= loop_limit; ++loop_count)
180     {
181         timein.SetToNow();
182         for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count)
183         {
184             RCEnter entry(&lock);
185             home.twiddle = PR_TRUE;
186             shared->twiddle = PR_FALSE;
187             shared->Notify();
188             while (home.twiddle)
189             {
190                 failed = (PR_FAILURE == home.Wait()) ? PR_TRUE : PR_FALSE;
191             }
192         }
193         timeout += (RCInterval(RCInterval::now) - timein);
194     }
195 
196     /* Figure out how well we did */
197     if (debug_mode)
198     {
199         average = timeout.ToMicroseconds()
200                   / (INNER_LOOPS * loop_limit * thread_count);
201         PR_fprintf(
202             debug_out, "Average switch times %d usecs for %d threads\n",
203             average, thread_limit);
204     }
205 
206     /* Start reclamation process */
207     link = shared;
208     for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
209     {
210         if (&home == link) {
211             break;
212         }
213         status = ((Shared*)link)->Interrupt();
214         if (PR_SUCCESS != status)
215         {
216             failed = PR_TRUE;
217             if (debug_mode) {
218                 PL_FPrintError(debug_out, "Failed to interrupt");
219             }
220         }
221         link = link->next;
222     }
223 
224     for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
225     {
226         link = shared->next;
227         status = shared->Join();
228         if (PR_SUCCESS != status)
229         {
230             failed = PR_TRUE;
231             if (debug_mode) {
232                 PL_FPrintError(debug_out, "Failed to join");
233             }
234         }
235         if (&home == link) {
236             break;
237         }
238         shared = (Shared*)link;
239     }
240 
241     PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n"));
242 
243     failed |= (PR_SUCCESS == RCPrimordialThread::Cleanup());
244 
245     return ((failed) ? 1 : 0);
246 }  /* main */
247 
248 /* switch.c */
249