1 /* Copyright © 1998 Netscape Communications Corporation */ 2 3 #include "prinit.h" 4 #include "prlock.h" 5 #include "prcvar.h" 6 #include "prmem.h" 7 #include "prinrval.h" 8 #include "prlog.h" 9 #include "prthread.h" 10 #include "prprf.h" 11 #include "plerror.h" 12 13 #define INNER_LOOPS 100 14 #define DEFAULT_LOOPS 100 15 #define DEFAULT_THREADS 10 16 17 typedef struct Shared 18 { 19 PRLock *ml; 20 PRCondVar *cv; 21 PRBool twiddle; 22 PRThread *thread; 23 struct Shared *next; 24 } Shared; 25 26 static PRFileDesc *debug_out = NULL; 27 static PRBool debug_mode = PR_FALSE, verbosity = PR_TRUE, failed = PR_FALSE; 28 static Shared home; 29 30 static void Help(void); 31 static void PR_CALLBACK Notified(void *arg); 32 static PRIntn PR_CALLBACK Switch(PRIntn argc, char **argv); 33 34 /* Root function for the threads in this sample (except primordial thread, which runs main) */ 35 static void PR_CALLBACK Notified(void *arg) 36 { 37 Shared *shared = arg; 38 PRStatus status = PR_SUCCESS; 39 while (PR_SUCCESS == status) 40 { 41 PR_Lock(shared->ml); 42 while (shared->twiddle && (PR_SUCCESS == status)) 43 status = PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT); 44 if (verbosity) PR_fprintf(debug_out, "+"); 45 shared->twiddle = PR_TRUE; 46 shared->next->twiddle = PR_FALSE; 47 PR_NotifyCondVar(shared->next->cv); 48 PR_Unlock(shared->ml); 49 } 50 } /* Notified */ 51 52 static PRIntn PR_CALLBACK Switch(PRIntn argc, char **argv) 53 { 54 PRStatus status; 55 PRBool help = PR_FALSE; 56 PRUintn concurrency = 1; 57 Shared *shared, *link; 58 PRIntervalTime timein, timeout; 59 PRBool global_threads = PR_FALSE; 60 PRThreadScope thread_scope = PR_LOCAL_THREAD; 61 PRUintn thread_count, inner_count, loop_count, average; 62 PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS; 63 64 if (help) return -1; 65 66 if (PR_TRUE == debug_mode) 67 { 68 debug_out = PR_STDOUT; 69 PR_fprintf(debug_out, "Test parameters\n"); 70 PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit); 71 PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit); 72 PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency); 73 PR_fprintf( 74 debug_out, "\tThread type: %s\n", 75 (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); 76 } 77 78 PR_SetConcurrency(concurrency); 79 80 /*'home' is "Shared" object for the primordial thread, at the end of the chain. */ 81 link = &home; 82 home.ml = PR_NewLock(); 83 home.cv = PR_NewCondVar(home.ml); 84 home.twiddle = PR_TRUE; 85 home.next = NULL; 86 timeout = 0; 87 88 /* Create "thread_limit" number of additional threads, each with its own "Shared" object. */ 89 for (thread_count = 1; thread_count <= thread_limit; ++thread_count) 90 { 91 shared = PR_NEWZAP(Shared); 92 shared->ml = home.ml; 93 shared->cv = PR_NewCondVar(home.ml); 94 shared->twiddle = PR_TRUE; 95 shared->next = link; 96 link = shared; 97 shared->thread = PR_CreateThread( 98 PR_USER_THREAD, Notified, shared, 99 PR_PRIORITY_HIGH, thread_scope, 100 PR_JOINABLE_THREAD, 0); 101 PR_ASSERT(shared->thread != NULL); 102 if (NULL == shared->thread) 103 failed = PR_TRUE; 104 } 105 106 /* "Shared" now points to the head of the chain, and "home" is the tail of the chain. */ 107 for (loop_count = 1; loop_count <= loop_limit; ++loop_count) 108 { 109 timein = PR_IntervalNow(); 110 for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count) 111 { 112 PR_Lock(home.ml); 113 home.twiddle = PR_TRUE; 114 shared->twiddle = PR_FALSE; 115 PR_NotifyCondVar(shared->cv); 116 while (home.twiddle) 117 { 118 status = PR_WaitCondVar(home.cv, PR_INTERVAL_NO_TIMEOUT); 119 if (PR_FAILURE == status) 120 failed = PR_TRUE; 121 } 122 PR_Unlock(home.ml); 123 } 124 timeout += (PR_IntervalNow() - timein); 125 } 126 127 if (debug_mode) 128 { 129 average = PR_IntervalToMicroseconds(timeout) 130 / (INNER_LOOPS * loop_limit * thread_count); 131 PR_fprintf( 132 debug_out, "Average switch times %d usecs for %d threads\n", 133 average, thread_limit); 134 } 135 /* Test completed. Remainder of sample cleanly shuts down the test. */ 136 link = shared; 137 for (thread_count = 1; thread_count <= thread_limit; ++thread_count) 138 { 139 if (&home == link) break; 140 status = PR_Interrupt(link->thread); 141 if (PR_SUCCESS != status) 142 { 143 failed = PR_TRUE; 144 if (debug_mode) 145 PL_FPrintError(debug_out, "Failed to interrupt"); 146 } 147 link = link->next; 148 } 149 150 for (thread_count = 1; thread_count <= thread_limit; ++thread_count) 151 { 152 link = shared->next; 153 status = PR_JoinThread(shared->thread); 154 if (PR_SUCCESS != status) 155 { 156 failed = PR_TRUE; 157 if (debug_mode) 158 PL_FPrintError(debug_out, "Failed to join"); 159 } 160 PR_DestroyCondVar(shared->cv); 161 PR_DELETE(shared); 162 if (&home == link) break; 163 shared = link; 164 } 165 PR_DestroyCondVar(home.cv); 166 PR_DestroyLock(home.ml); 167 168 PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n")); 169 return ((failed) ? 1 : 0); 170 } /* Switch */ 171 172 PRIntn main(PRIntn argc, char **argv) 173 { 174 PRIntn result; 175 PR_STDIO_INIT(); 176 result = PR_Initialize(Switch, argc, argv, 0); 177 return result; 178 } /* main */ 179 180 /* switch.c */