|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 #include <kernel/OS.h> |
|
7 |
|
8 #include "primpl.h" |
|
9 |
|
10 /* |
|
11 ** Create a new monitor. Monitors are re-entrant locks with a single built-in |
|
12 ** condition variable. |
|
13 ** |
|
14 ** This may fail if memory is tight or if some operating system resource |
|
15 ** is low. |
|
16 */ |
|
17 PR_IMPLEMENT(PRMonitor*) |
|
18 PR_NewMonitor (void) |
|
19 { |
|
20 PRMonitor *mon; |
|
21 PRCondVar *cvar; |
|
22 PRLock *lock; |
|
23 |
|
24 mon = PR_NEWZAP( PRMonitor ); |
|
25 if( mon ) |
|
26 { |
|
27 lock = PR_NewLock(); |
|
28 if( !lock ) |
|
29 { |
|
30 PR_DELETE( mon ); |
|
31 return( 0 ); |
|
32 } |
|
33 |
|
34 cvar = PR_NewCondVar( lock ); |
|
35 if( !cvar ) |
|
36 { |
|
37 PR_DestroyLock( lock ); |
|
38 PR_DELETE( mon ); |
|
39 return( 0 ); |
|
40 } |
|
41 |
|
42 mon->cvar = cvar; |
|
43 mon->name = NULL; |
|
44 } |
|
45 |
|
46 return( mon ); |
|
47 } |
|
48 |
|
49 PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) |
|
50 { |
|
51 PRMonitor* mon = PR_NewMonitor(); |
|
52 if( mon ) |
|
53 { |
|
54 mon->name = name; |
|
55 } |
|
56 return mon; |
|
57 } |
|
58 |
|
59 /* |
|
60 ** Destroy a monitor. The caller is responsible for guaranteeing that the |
|
61 ** monitor is no longer in use. There must be no thread waiting on the |
|
62 ** monitor's condition variable and that the lock is not held. |
|
63 ** |
|
64 */ |
|
65 PR_IMPLEMENT(void) |
|
66 PR_DestroyMonitor (PRMonitor *mon) |
|
67 { |
|
68 PR_DestroyLock( mon->cvar->lock ); |
|
69 PR_DestroyCondVar( mon->cvar ); |
|
70 PR_DELETE( mon ); |
|
71 } |
|
72 |
|
73 /* |
|
74 ** Enter the lock associated with the monitor. If the calling thread currently |
|
75 ** is in the monitor, the call to enter will silently succeed. In either case, |
|
76 ** it will increment the entry count by one. |
|
77 */ |
|
78 PR_IMPLEMENT(void) |
|
79 PR_EnterMonitor (PRMonitor *mon) |
|
80 { |
|
81 if( mon->cvar->lock->owner == find_thread( NULL ) ) |
|
82 { |
|
83 mon->entryCount++; |
|
84 |
|
85 } else |
|
86 { |
|
87 PR_Lock( mon->cvar->lock ); |
|
88 mon->entryCount = 1; |
|
89 } |
|
90 } |
|
91 |
|
92 /* |
|
93 ** Decrement the entry count associated with the monitor. If the decremented |
|
94 ** entry count is zero, the monitor is exited. Returns PR_FAILURE if the |
|
95 ** calling thread has not entered the monitor. |
|
96 */ |
|
97 PR_IMPLEMENT(PRStatus) |
|
98 PR_ExitMonitor (PRMonitor *mon) |
|
99 { |
|
100 if( mon->cvar->lock->owner != find_thread( NULL ) ) |
|
101 { |
|
102 return( PR_FAILURE ); |
|
103 } |
|
104 if( --mon->entryCount == 0 ) |
|
105 { |
|
106 return( PR_Unlock( mon->cvar->lock ) ); |
|
107 } |
|
108 return( PR_SUCCESS ); |
|
109 } |
|
110 |
|
111 /* |
|
112 ** Wait for a notify on the monitor's condition variable. Sleep for "ticks" |
|
113 ** amount of time (if "ticks" is PR_INTERVAL_NO_TIMEOUT then the sleep is |
|
114 ** indefinite). |
|
115 ** |
|
116 ** While the thread is waiting it exits the monitor (as if it called |
|
117 ** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When |
|
118 ** the wait has finished the thread regains control of the monitors lock |
|
119 ** with the same entry count as before the wait began. |
|
120 ** |
|
121 ** The thread waiting on the monitor will be resumed when the monitor is |
|
122 ** notified (assuming the thread is the next in line to receive the |
|
123 ** notify) or when the "ticks" timeout elapses. |
|
124 ** |
|
125 ** Returns PR_FAILURE if the caller has not entered the monitor. |
|
126 */ |
|
127 PR_IMPLEMENT(PRStatus) |
|
128 PR_Wait (PRMonitor *mon, PRIntervalTime ticks) |
|
129 { |
|
130 PRUint32 entryCount; |
|
131 PRUintn status; |
|
132 PRThread *meThread; |
|
133 thread_id me = find_thread( NULL ); |
|
134 meThread = PR_GetCurrentThread(); |
|
135 |
|
136 if( mon->cvar->lock->owner != me ) return( PR_FAILURE ); |
|
137 |
|
138 entryCount = mon->entryCount; |
|
139 mon->entryCount = 0; |
|
140 |
|
141 status = PR_WaitCondVar( mon->cvar, ticks ); |
|
142 |
|
143 mon->entryCount = entryCount; |
|
144 |
|
145 return( status ); |
|
146 } |
|
147 |
|
148 /* |
|
149 ** Notify a thread waiting on the monitor's condition variable. If a thread |
|
150 ** is waiting on the condition variable (using PR_Wait) then it is awakened |
|
151 ** and attempts to reenter the monitor. |
|
152 */ |
|
153 PR_IMPLEMENT(PRStatus) |
|
154 PR_Notify (PRMonitor *mon) |
|
155 { |
|
156 if( mon->cvar->lock->owner != find_thread( NULL ) ) |
|
157 { |
|
158 return( PR_FAILURE ); |
|
159 } |
|
160 |
|
161 PR_NotifyCondVar( mon->cvar ); |
|
162 return( PR_SUCCESS ); |
|
163 } |
|
164 |
|
165 /* |
|
166 ** Notify all of the threads waiting on the monitor's condition variable. |
|
167 ** All of threads waiting on the condition are scheduled to reenter the |
|
168 ** monitor. |
|
169 */ |
|
170 PR_IMPLEMENT(PRStatus) |
|
171 PR_NotifyAll (PRMonitor *mon) |
|
172 { |
|
173 if( mon->cvar->lock->owner != find_thread( NULL ) ) |
|
174 { |
|
175 return( PR_FAILURE ); |
|
176 } |
|
177 |
|
178 PR_NotifyAllCondVar( mon->cvar ); |
|
179 return( PR_SUCCESS ); |
|
180 } |
|
181 |
|
182 /* |
|
183 ** Return the number of times that the current thread has entered the |
|
184 ** lock. Returns zero if the current thread has not entered the lock. |
|
185 */ |
|
186 PR_IMPLEMENT(PRIntn) |
|
187 PR_GetMonitorEntryCount(PRMonitor *mon) |
|
188 { |
|
189 return( (mon->cvar->lock->owner == find_thread( NULL )) ? |
|
190 mon->entryCount : 0 ); |
|
191 } |
|
192 |
|
193 /* |
|
194 ** If the current thread is in |mon|, this assertion is guaranteed to |
|
195 ** succeed. Otherwise, the behavior of this function is undefined. |
|
196 */ |
|
197 PR_IMPLEMENT(void) |
|
198 PR_AssertCurrentThreadInMonitor(PRMonitor *mon) |
|
199 { |
|
200 PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mon->cvar->lock); |
|
201 } |