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