|
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 |
|
43 Home::~Home() { } |
|
44 |
|
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 |
|
62 Shared::Shared(RCThread::Scope scope, Home* link, RCLock* lock): |
|
63 Home(link, lock), RCThread(scope, RCThread::joinable) { } |
|
64 |
|
65 Shared::~Shared() { } |
|
66 |
|
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)) status = Wait(); |
|
74 if (verbosity) PR_fprintf(debug_out, "+"); |
|
75 twiddle = PR_TRUE; |
|
76 next->twiddle = PR_FALSE; |
|
77 next->Notify(); |
|
78 } |
|
79 } /* Shared::RootFunction */ |
|
80 |
|
81 static void Help(void) |
|
82 { |
|
83 debug_out = PR_STDOUT; |
|
84 |
|
85 PR_fprintf( |
|
86 debug_out, "Usage: >./switch [-d] [-c n] [-t n] [-T n] [-G]\n"); |
|
87 PR_fprintf( |
|
88 debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS); |
|
89 PR_fprintf( |
|
90 debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS); |
|
91 PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n"); |
|
92 PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n"); |
|
93 PR_fprintf(debug_out, "-G n\tglobal threads only (default: FALSE)\n"); |
|
94 PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n"); |
|
95 } /* Help */ |
|
96 |
|
97 PRIntn main(PRIntn argc, char **argv) |
|
98 { |
|
99 PLOptStatus os; |
|
100 PRStatus status; |
|
101 PRBool help = PR_FALSE; |
|
102 PRUintn concurrency = 1; |
|
103 RCThread::Scope thread_scope = RCThread::local; |
|
104 PRUintn thread_count, inner_count, loop_count, average; |
|
105 PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS; |
|
106 PLOptState *opt = PL_CreateOptState(argc, argv, "hdvc:t:C:G"); |
|
107 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
|
108 { |
|
109 if (PL_OPT_BAD == os) continue; |
|
110 switch (opt->option) |
|
111 { |
|
112 case 'v': /* verbose mode */ |
|
113 verbosity = PR_TRUE; |
|
114 case 'd': /* debug mode */ |
|
115 debug_mode = PR_TRUE; |
|
116 break; |
|
117 case 'c': /* loop counter */ |
|
118 loop_limit = atoi(opt->value); |
|
119 break; |
|
120 case 't': /* thread limit */ |
|
121 thread_limit = atoi(opt->value); |
|
122 break; |
|
123 case 'C': /* Concurrency limit */ |
|
124 concurrency = atoi(opt->value); |
|
125 break; |
|
126 case 'G': /* global threads only */ |
|
127 thread_scope = RCThread::global; |
|
128 break; |
|
129 case 'h': /* help message */ |
|
130 Help(); |
|
131 help = PR_TRUE; |
|
132 break; |
|
133 default: |
|
134 break; |
|
135 } |
|
136 } |
|
137 PL_DestroyOptState(opt); |
|
138 |
|
139 if (help) return -1; |
|
140 |
|
141 if (PR_TRUE == debug_mode) |
|
142 { |
|
143 debug_out = PR_STDOUT; |
|
144 PR_fprintf(debug_out, "Test parameters\n"); |
|
145 PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit); |
|
146 PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit); |
|
147 PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency); |
|
148 PR_fprintf( |
|
149 debug_out, "\tThread type: %s\n", |
|
150 (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); |
|
151 } |
|
152 |
|
153 /* |
|
154 ** The interesting part starts here |
|
155 */ |
|
156 RCLock lock; |
|
157 Shared* shared; |
|
158 Home home(NULL, &lock); |
|
159 Home* link = &home; |
|
160 RCInterval timein, timeout = 0; |
|
161 |
|
162 /* Build up the string of objects */ |
|
163 for (thread_count = 1; thread_count <= thread_limit; ++thread_count) |
|
164 { |
|
165 shared = new Shared(thread_scope, link, &lock); |
|
166 shared->Start(); /* make it run */ |
|
167 link = (Home*)shared; |
|
168 } |
|
169 |
|
170 /* Pass the message around the horn a few times */ |
|
171 for (loop_count = 1; loop_count <= loop_limit; ++loop_count) |
|
172 { |
|
173 timein.SetToNow(); |
|
174 for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count) |
|
175 { |
|
176 RCEnter entry(&lock); |
|
177 home.twiddle = PR_TRUE; |
|
178 shared->twiddle = PR_FALSE; |
|
179 shared->Notify(); |
|
180 while (home.twiddle) |
|
181 { |
|
182 failed = (PR_FAILURE == home.Wait()) ? PR_TRUE : PR_FALSE; |
|
183 } |
|
184 } |
|
185 timeout += (RCInterval(RCInterval::now) - timein); |
|
186 } |
|
187 |
|
188 /* Figure out how well we did */ |
|
189 if (debug_mode) |
|
190 { |
|
191 average = timeout.ToMicroseconds() |
|
192 / (INNER_LOOPS * loop_limit * thread_count); |
|
193 PR_fprintf( |
|
194 debug_out, "Average switch times %d usecs for %d threads\n", |
|
195 average, thread_limit); |
|
196 } |
|
197 |
|
198 /* Start reclamation process */ |
|
199 link = shared; |
|
200 for (thread_count = 1; thread_count <= thread_limit; ++thread_count) |
|
201 { |
|
202 if (&home == link) break; |
|
203 status = ((Shared*)link)->Interrupt(); |
|
204 if (PR_SUCCESS != status) |
|
205 { |
|
206 failed = PR_TRUE; |
|
207 if (debug_mode) |
|
208 PL_FPrintError(debug_out, "Failed to interrupt"); |
|
209 } |
|
210 link = link->next; |
|
211 } |
|
212 |
|
213 for (thread_count = 1; thread_count <= thread_limit; ++thread_count) |
|
214 { |
|
215 link = shared->next; |
|
216 status = shared->Join(); |
|
217 if (PR_SUCCESS != status) |
|
218 { |
|
219 failed = PR_TRUE; |
|
220 if (debug_mode) |
|
221 PL_FPrintError(debug_out, "Failed to join"); |
|
222 } |
|
223 if (&home == link) break; |
|
224 shared = (Shared*)link; |
|
225 } |
|
226 |
|
227 PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n")); |
|
228 |
|
229 failed |= (PR_SUCCESS == RCPrimordialThread::Cleanup()); |
|
230 |
|
231 return ((failed) ? 1 : 0); |
|
232 } /* main */ |
|
233 |
|
234 /* switch.c */ |