You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



Thread Synchronization Sample

Declarations and Notified and Switch show a single sample program, switch.c, that illustrates basic techniques of NSPR thread synchronization. This program is also available in the Samples directory.

Declarations and Notified function

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_TRUE, verbosity = PR_FALSE, 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   

The root function, Notified, for the threads in this sample (except for the primordial thread, which runs main) starts after line 34 of Declarations and Notified. The threads are chained together, with the primordial thread at the end of the chain.

Each thread blocks on a condition variable (shared-cv), waiting for the Boolean expression shared->twiddle to become PR_FALSE. Notice that the condition variable wait is repeated while the Boolean expression shared->twiddle is PR_TRUE. This while loop around a condition variable wait is a common idiom.

The condition variable wait fails with PR_PENDING_INTERRRUPT_ERROR if the thread is interrupted, in which case the thread breaks out of the nested while loops and exits.

When the thread is notified and shared->twiddle has become PR_FALSE, it sets shared->next->twiddle to PR_FALSE and notifies the next thread down in the chain. It also sets its own shared->twiddle back to PR_TRUE, in preparation for the next iteration.

The Switch function shown in Switch is really the main function of this program. The main function simply passes Switch to PR_Initialize.

Switch and main functions

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 */

The home object defined after line 80 in Switch is the Shared object for the primordial thread. It is at the end of the chain.

The Switch function creates thread_limit number of additional threads after line 87, each with its own Shared object. The Shared objects are chained together. The shared->twiddle value for these objects is initialized to PR_TRUE, so that the threads are all blocked initially.

By line 106, Shared points to the head of the chain, and home is the tail of the chain. The primordial thread then sets shared->twiddle to PR_FALSE and notifies shared->cv to kick off the domino effect. The primordial thread then blocks on its own condition variable home.cv at the end of the chain. This process is repeated by a nested for loop.

By line 135, the test is finished. The code that follows cleanly shuts down the test. The primordial thread interrupts all the threads so that they know it's time to exit. Then the primordial thread joins all the threads to synchronize with their termination.


Last Updated: Mon Jul 13 15:31:58 PDT 1998


Copyright 1998 Netscape Communications Corporation