Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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/. */
6 /*
7 * A test for the pollable events.
8 *
9 * A number of threads are in a ring configuration, each waiting on
10 * a pollable event that is set by its upstream neighbor.
11 */
13 #include "prinit.h"
14 #include "prio.h"
15 #include "prthread.h"
16 #include "prerror.h"
17 #include "prmem.h"
18 #include "prlog.h"
19 #include "prprf.h"
21 #include "plgetopt.h"
23 #include <stdlib.h>
25 #define DEFAULT_THREADS 10
26 #define DEFAULT_LOOPS 100
28 PRIntn numThreads = DEFAULT_THREADS;
29 PRIntn numIterations = DEFAULT_LOOPS;
30 PRIntervalTime dally = PR_INTERVAL_NO_WAIT;
31 PRFileDesc *debug_out = NULL;
32 PRBool debug_mode = PR_FALSE;
33 PRBool verbosity = PR_FALSE;
35 typedef struct ThreadData {
36 PRFileDesc *event;
37 int index;
38 struct ThreadData *next;
39 } ThreadData;
41 void ThreadRoutine(void *arg)
42 {
43 ThreadData *data = (ThreadData *) arg;
44 PRIntn i;
45 PRPollDesc pd;
46 PRInt32 rv;
48 pd.fd = data->event;
49 pd.in_flags = PR_POLL_READ;
51 for (i = 0; i < numIterations; i++) {
52 rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
53 if (rv == -1) {
54 PR_fprintf(PR_STDERR, "PR_Poll failed\n");
55 exit(1);
56 }
57 if (verbosity) {
58 PR_fprintf(debug_out, "thread %d awakened\n", data->index);
59 }
60 PR_ASSERT(rv != 0);
61 PR_ASSERT(pd.out_flags & PR_POLL_READ);
62 if (PR_WaitForPollableEvent(data->event) == PR_FAILURE) {
63 PR_fprintf(PR_STDERR, "consume event failed\n");
64 exit(1);
65 }
66 if (dally != PR_INTERVAL_NO_WAIT) {
67 PR_Sleep(dally);
68 }
69 if (verbosity) {
70 PR_fprintf(debug_out, "thread %d posting event\n", data->index);
71 }
72 if (PR_SetPollableEvent(data->next->event) == PR_FAILURE) {
73 PR_fprintf(PR_STDERR, "post event failed\n");
74 exit(1);
75 }
76 }
77 }
79 static void Help(void)
80 {
81 debug_out = PR_STDOUT;
83 PR_fprintf(
84 debug_out, "Usage: pollable [-c n] [-t n] [-d] [-v] [-G] [-C n] [-D n]\n");
85 PR_fprintf(
86 debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS);
87 PR_fprintf(
88 debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS);
89 PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
90 PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
91 PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n");
92 PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
93 PR_fprintf(debug_out, "-D n\tdally setting (msecs) (default: 0)\n");
94 } /* Help */
96 int main(int argc, char **argv)
97 {
98 ThreadData selfData;
99 ThreadData *data;
100 PRThread **thread;
101 void *block;
102 PRIntn i;
103 PRIntervalTime timeStart, timeEnd;
104 PRPollDesc pd;
105 PRInt32 rv;
106 PRThreadScope thread_scope = PR_LOCAL_THREAD;
107 PRBool help = PR_FALSE;
108 PRUintn concurrency = 1;
109 PRUintn average;
110 PLOptStatus os;
111 PLOptState *opt;
113 PR_STDIO_INIT();
115 opt = PL_CreateOptState(argc, argv, "hdvc:t:C:GD:");
116 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
117 if (PL_OPT_BAD == os) {
118 continue;
119 }
120 switch (opt->option) {
121 case 'v': /* verbose mode */
122 verbosity = PR_TRUE;
123 case 'd': /* debug mode */
124 debug_mode = PR_TRUE;
125 break;
126 case 'c': /* loop counter */
127 numIterations = atoi(opt->value);
128 break;
129 case 't': /* thread limit */
130 numThreads = atoi(opt->value);
131 break;
132 case 'C': /* Concurrency limit */
133 concurrency = atoi(opt->value);
134 break;
135 case 'G': /* global threads only */
136 thread_scope = PR_GLOBAL_THREAD;
137 break;
138 case 'D': /* dally */
139 dally = PR_MillisecondsToInterval(atoi(opt->value));
140 break;
141 case 'h': /* help message */
142 Help();
143 help = PR_TRUE;
144 break;
145 default:
146 break;
147 }
148 }
149 PL_DestroyOptState(opt);
151 if (help) {
152 return 1;
153 }
155 if (concurrency > 1) {
156 PR_SetConcurrency(concurrency);
157 }
159 if (PR_TRUE == debug_mode) {
160 debug_out = PR_STDOUT;
161 PR_fprintf(debug_out, "Test parameters\n");
162 PR_fprintf(debug_out, "\tThreads involved: %d\n", numThreads);
163 PR_fprintf(debug_out, "\tIteration limit: %d\n", numIterations);
164 PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
165 PR_fprintf(debug_out, "\tThread type: %s\n",
166 (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
167 }
169 /*
170 * Malloc a block of memory and divide it into data and thread.
171 */
172 block = PR_MALLOC(numThreads * (sizeof(ThreadData) + sizeof(PRThread *)));
173 if (block == NULL) {
174 PR_fprintf(PR_STDERR, "cannot malloc, failed\n");
175 exit(1);
176 }
177 data = (ThreadData *) block;
178 thread = (PRThread **) &data[numThreads];
180 /* Pollable event */
181 selfData.event = PR_NewPollableEvent();
182 if (selfData.event == NULL) {
183 PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n",
184 PR_GetError(), PR_GetOSError());
185 exit(1);
186 }
187 selfData.next = &data[0];
188 for (i = 0; i < numThreads; i++) {
189 data[i].event = PR_NewPollableEvent();
190 if (data[i].event == NULL) {
191 PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n",
192 PR_GetError(), PR_GetOSError());
193 exit(1);
194 }
195 data[i].index = i;
196 if (i != numThreads - 1) {
197 data[i].next = &data[i + 1];
198 } else {
199 data[i].next = &selfData;
200 }
202 thread[i] = PR_CreateThread(PR_USER_THREAD,
203 ThreadRoutine, &data[i], PR_PRIORITY_NORMAL,
204 thread_scope, PR_JOINABLE_THREAD, 0);
205 if (thread[i] == NULL) {
206 PR_fprintf(PR_STDERR, "cannot create thread\n");
207 exit(1);
208 }
209 }
211 timeStart = PR_IntervalNow();
212 pd.fd = selfData.event;
213 pd.in_flags = PR_POLL_READ;
214 for (i = 0; i < numIterations; i++) {
215 if (dally != PR_INTERVAL_NO_WAIT) {
216 PR_Sleep(dally);
217 }
218 if (verbosity) {
219 PR_fprintf(debug_out, "main thread posting event\n");
220 }
221 if (PR_SetPollableEvent(selfData.next->event) == PR_FAILURE) {
222 PR_fprintf(PR_STDERR, "set event failed\n");
223 exit(1);
224 }
225 rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
226 if (rv == -1) {
227 PR_fprintf(PR_STDERR, "wait failed\n");
228 exit(1);
229 }
230 PR_ASSERT(rv != 0);
231 PR_ASSERT(pd.out_flags & PR_POLL_READ);
232 if (verbosity) {
233 PR_fprintf(debug_out, "main thread awakened\n");
234 }
235 if (PR_WaitForPollableEvent(selfData.event) == PR_FAILURE) {
236 PR_fprintf(PR_STDERR, "consume event failed\n");
237 exit(1);
238 }
239 }
240 timeEnd = PR_IntervalNow();
242 if (debug_mode) {
243 average = PR_IntervalToMicroseconds(timeEnd - timeStart)
244 / (numIterations * numThreads);
245 PR_fprintf(debug_out, "Average switch times %d usecs for %d threads\n",
246 average, numThreads);
247 }
249 for (i = 0; i < numThreads; i++) {
250 if (PR_JoinThread(thread[i]) == PR_FAILURE) {
251 PR_fprintf(PR_STDERR, "join thread failed\n");
252 exit(1);
253 }
254 PR_DestroyPollableEvent(data[i].event);
255 }
256 PR_DELETE(block);
257 PR_DestroyPollableEvent(selfData.event);
259 PR_fprintf(PR_STDOUT, "PASSED\n");
260 return 0;
261 }