| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* |
| * A test for the pollable events. |
| * |
| * A number of threads are in a ring configuration, each waiting on |
| * a pollable event that is set by its upstream neighbor. |
| */ |
| |
| #include "prinit.h" |
| #include "prio.h" |
| #include "prthread.h" |
| #include "prerror.h" |
| #include "prmem.h" |
| #include "prlog.h" |
| #include "prprf.h" |
| |
| #include "plgetopt.h" |
| |
| #include <stdlib.h> |
| |
| #define DEFAULT_THREADS 10 |
| #define DEFAULT_LOOPS 100 |
| |
| PRIntn numThreads = DEFAULT_THREADS; |
| PRIntn numIterations = DEFAULT_LOOPS; |
| PRIntervalTime dally = PR_INTERVAL_NO_WAIT; |
| PRFileDesc *debug_out = NULL; |
| PRBool debug_mode = PR_FALSE; |
| PRBool verbosity = PR_FALSE; |
| |
| typedef struct ThreadData { |
| PRFileDesc *event; |
| int index; |
| struct ThreadData *next; |
| } ThreadData; |
| |
| void ThreadRoutine(void *arg) |
| { |
| ThreadData *data = (ThreadData *) arg; |
| PRIntn i; |
| PRPollDesc pd; |
| PRInt32 rv; |
| |
| pd.fd = data->event; |
| pd.in_flags = PR_POLL_READ; |
| |
| for (i = 0; i < numIterations; i++) { |
| rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); |
| if (rv == -1) { |
| PR_fprintf(PR_STDERR, "PR_Poll failed\n"); |
| exit(1); |
| } |
| if (verbosity) { |
| PR_fprintf(debug_out, "thread %d awakened\n", data->index); |
| } |
| PR_ASSERT(rv != 0); |
| PR_ASSERT(pd.out_flags & PR_POLL_READ); |
| if (PR_WaitForPollableEvent(data->event) == PR_FAILURE) { |
| PR_fprintf(PR_STDERR, "consume event failed\n"); |
| exit(1); |
| } |
| if (dally != PR_INTERVAL_NO_WAIT) { |
| PR_Sleep(dally); |
| } |
| if (verbosity) { |
| PR_fprintf(debug_out, "thread %d posting event\n", data->index); |
| } |
| if (PR_SetPollableEvent(data->next->event) == PR_FAILURE) { |
| PR_fprintf(PR_STDERR, "post event failed\n"); |
| exit(1); |
| } |
| } |
| } |
| |
| static void Help(void) |
| { |
| debug_out = PR_STDOUT; |
| |
| PR_fprintf( |
| debug_out, "Usage: pollable [-c n] [-t n] [-d] [-v] [-G] [-C n] [-D n]\n"); |
| PR_fprintf( |
| debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS); |
| PR_fprintf( |
| debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS); |
| PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n"); |
| PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n"); |
| PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n"); |
| PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n"); |
| PR_fprintf(debug_out, "-D n\tdally setting (msecs) (default: 0)\n"); |
| } /* Help */ |
| |
| int main(int argc, char **argv) |
| { |
| ThreadData selfData; |
| ThreadData *data; |
| PRThread **thread; |
| void *block; |
| PRIntn i; |
| PRIntervalTime timeStart, timeEnd; |
| PRPollDesc pd; |
| PRInt32 rv; |
| PRThreadScope thread_scope = PR_LOCAL_THREAD; |
| PRBool help = PR_FALSE; |
| PRUintn concurrency = 1; |
| PRUintn average; |
| PLOptStatus os; |
| PLOptState *opt; |
| |
| PR_STDIO_INIT(); |
| |
| opt = PL_CreateOptState(argc, argv, "hdvc:t:C:GD:"); |
| while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { |
| if (PL_OPT_BAD == os) { |
| continue; |
| } |
| switch (opt->option) { |
| case 'v': /* verbose mode */ |
| verbosity = PR_TRUE; |
| case 'd': /* debug mode */ |
| debug_mode = PR_TRUE; |
| break; |
| case 'c': /* loop counter */ |
| numIterations = atoi(opt->value); |
| break; |
| case 't': /* thread limit */ |
| numThreads = atoi(opt->value); |
| break; |
| case 'C': /* Concurrency limit */ |
| concurrency = atoi(opt->value); |
| break; |
| case 'G': /* global threads only */ |
| thread_scope = PR_GLOBAL_THREAD; |
| break; |
| case 'D': /* dally */ |
| dally = PR_MillisecondsToInterval(atoi(opt->value)); |
| break; |
| case 'h': /* help message */ |
| Help(); |
| help = PR_TRUE; |
| break; |
| default: |
| break; |
| } |
| } |
| PL_DestroyOptState(opt); |
| |
| if (help) { |
| return 1; |
| } |
| |
| if (concurrency > 1) { |
| PR_SetConcurrency(concurrency); |
| } |
| |
| if (PR_TRUE == debug_mode) { |
| debug_out = PR_STDOUT; |
| PR_fprintf(debug_out, "Test parameters\n"); |
| PR_fprintf(debug_out, "\tThreads involved: %d\n", numThreads); |
| PR_fprintf(debug_out, "\tIteration limit: %d\n", numIterations); |
| PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency); |
| PR_fprintf(debug_out, "\tThread type: %s\n", |
| (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); |
| } |
| |
| /* |
| * Malloc a block of memory and divide it into data and thread. |
| */ |
| block = PR_MALLOC(numThreads * (sizeof(ThreadData) + sizeof(PRThread *))); |
| if (block == NULL) { |
| PR_fprintf(PR_STDERR, "cannot malloc, failed\n"); |
| exit(1); |
| } |
| data = (ThreadData *) block; |
| thread = (PRThread **) &data[numThreads]; |
| |
| /* Pollable event */ |
| selfData.event = PR_NewPollableEvent(); |
| if (selfData.event == NULL) { |
| PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n", |
| PR_GetError(), PR_GetOSError()); |
| exit(1); |
| } |
| selfData.next = &data[0]; |
| for (i = 0; i < numThreads; i++) { |
| data[i].event = PR_NewPollableEvent(); |
| if (data[i].event == NULL) { |
| PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n", |
| PR_GetError(), PR_GetOSError()); |
| exit(1); |
| } |
| data[i].index = i; |
| if (i != numThreads - 1) { |
| data[i].next = &data[i + 1]; |
| } else { |
| data[i].next = &selfData; |
| } |
| |
| thread[i] = PR_CreateThread(PR_USER_THREAD, |
| ThreadRoutine, &data[i], PR_PRIORITY_NORMAL, |
| thread_scope, PR_JOINABLE_THREAD, 0); |
| if (thread[i] == NULL) { |
| PR_fprintf(PR_STDERR, "cannot create thread\n"); |
| exit(1); |
| } |
| } |
| |
| timeStart = PR_IntervalNow(); |
| pd.fd = selfData.event; |
| pd.in_flags = PR_POLL_READ; |
| for (i = 0; i < numIterations; i++) { |
| if (dally != PR_INTERVAL_NO_WAIT) { |
| PR_Sleep(dally); |
| } |
| if (verbosity) { |
| PR_fprintf(debug_out, "main thread posting event\n"); |
| } |
| if (PR_SetPollableEvent(selfData.next->event) == PR_FAILURE) { |
| PR_fprintf(PR_STDERR, "set event failed\n"); |
| exit(1); |
| } |
| rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); |
| if (rv == -1) { |
| PR_fprintf(PR_STDERR, "wait failed\n"); |
| exit(1); |
| } |
| PR_ASSERT(rv != 0); |
| PR_ASSERT(pd.out_flags & PR_POLL_READ); |
| if (verbosity) { |
| PR_fprintf(debug_out, "main thread awakened\n"); |
| } |
| if (PR_WaitForPollableEvent(selfData.event) == PR_FAILURE) { |
| PR_fprintf(PR_STDERR, "consume event failed\n"); |
| exit(1); |
| } |
| } |
| timeEnd = PR_IntervalNow(); |
| |
| if (debug_mode) { |
| average = PR_IntervalToMicroseconds(timeEnd - timeStart) |
| / (numIterations * numThreads); |
| PR_fprintf(debug_out, "Average switch times %d usecs for %d threads\n", |
| average, numThreads); |
| } |
| |
| for (i = 0; i < numThreads; i++) { |
| if (PR_JoinThread(thread[i]) == PR_FAILURE) { |
| PR_fprintf(PR_STDERR, "join thread failed\n"); |
| exit(1); |
| } |
| PR_DestroyPollableEvent(data[i].event); |
| } |
| PR_DELETE(block); |
| PR_DestroyPollableEvent(selfData.event); |
| |
| PR_fprintf(PR_STDOUT, "PASSED\n"); |
| return 0; |
| } |