prmon.c (9105B)
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 #include "primpl.h" 7 8 /************************************************************************/ 9 10 /* 11 * Notifies just get posted to the monitor. The actual notification is done 12 * when the monitor is fully exited so that MP systems don't contend for a 13 * monitor that they can't enter. 14 */ 15 static void _PR_PostNotifyToMonitor(PRMonitor* mon, PRBool broadcast) { 16 PR_ASSERT(mon != NULL); 17 PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon); 18 19 /* mon->notifyTimes is protected by the monitor, so we don't need to 20 * acquire mon->lock. 21 */ 22 if (broadcast) { 23 mon->notifyTimes = -1; 24 } else if (mon->notifyTimes != -1) { 25 mon->notifyTimes += 1; 26 } 27 } 28 29 static void _PR_PostNotifiesFromMonitor(PRCondVar* cv, PRIntn times) { 30 PRStatus rv; 31 32 /* 33 * Time to actually notify any waits that were affected while the monitor 34 * was entered. 35 */ 36 PR_ASSERT(cv != NULL); 37 PR_ASSERT(times != 0); 38 if (times == -1) { 39 rv = PR_NotifyAllCondVar(cv); 40 PR_ASSERT(rv == PR_SUCCESS); 41 } else { 42 while (times-- > 0) { 43 rv = PR_NotifyCondVar(cv); 44 PR_ASSERT(rv == PR_SUCCESS); 45 } 46 } 47 } 48 49 /* 50 ** Create a new monitor. 51 */ 52 PR_IMPLEMENT(PRMonitor*) PR_NewMonitor() { 53 PRMonitor* mon; 54 PRStatus rv; 55 56 if (!_pr_initialized) { 57 _PR_ImplicitInitialization(); 58 } 59 60 mon = PR_NEWZAP(PRMonitor); 61 if (mon == NULL) { 62 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 63 return NULL; 64 } 65 66 rv = _PR_InitLock(&mon->lock); 67 PR_ASSERT(rv == PR_SUCCESS); 68 if (rv != PR_SUCCESS) { 69 goto error1; 70 } 71 72 mon->owner = NULL; 73 74 rv = _PR_InitCondVar(&mon->entryCV, &mon->lock); 75 PR_ASSERT(rv == PR_SUCCESS); 76 if (rv != PR_SUCCESS) { 77 goto error2; 78 } 79 80 rv = _PR_InitCondVar(&mon->waitCV, &mon->lock); 81 PR_ASSERT(rv == PR_SUCCESS); 82 if (rv != PR_SUCCESS) { 83 goto error3; 84 } 85 86 mon->notifyTimes = 0; 87 mon->entryCount = 0; 88 mon->name = NULL; 89 return mon; 90 91 error3: 92 _PR_FreeCondVar(&mon->entryCV); 93 error2: 94 _PR_FreeLock(&mon->lock); 95 error1: 96 PR_Free(mon); 97 return NULL; 98 } 99 100 PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) { 101 PRMonitor* mon = PR_NewMonitor(); 102 if (mon) { 103 mon->name = name; 104 } 105 return mon; 106 } 107 108 /* 109 ** Destroy a monitor. There must be no thread waiting on the monitor's 110 ** condition variable. The caller is responsible for guaranteeing that the 111 ** monitor is no longer in use. 112 */ 113 PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor* mon) { 114 PR_ASSERT(mon != NULL); 115 _PR_FreeCondVar(&mon->waitCV); 116 _PR_FreeCondVar(&mon->entryCV); 117 _PR_FreeLock(&mon->lock); 118 #if defined(DEBUG) 119 memset(mon, 0xaf, sizeof(PRMonitor)); 120 #endif 121 PR_Free(mon); 122 } 123 124 /* 125 ** Enter the lock associated with the monitor. 126 */ 127 PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor* mon) { 128 PRThread* me = _PR_MD_CURRENT_THREAD(); 129 PRStatus rv; 130 131 PR_ASSERT(mon != NULL); 132 PR_Lock(&mon->lock); 133 if (mon->entryCount != 0) { 134 if (mon->owner == me) { 135 goto done; 136 } 137 while (mon->entryCount != 0) { 138 rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT); 139 PR_ASSERT(rv == PR_SUCCESS); 140 } 141 } 142 /* and now I have the monitor */ 143 PR_ASSERT(mon->notifyTimes == 0); 144 PR_ASSERT(mon->owner == NULL); 145 mon->owner = me; 146 147 done: 148 mon->entryCount += 1; 149 rv = PR_Unlock(&mon->lock); 150 PR_ASSERT(rv == PR_SUCCESS); 151 } 152 153 /* 154 ** Test and then enter the lock associated with the monitor if it's not 155 ** already entered by some other thread. Return PR_FALSE if some other 156 ** thread owned the lock at the time of the call. 157 */ 158 PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor* mon) { 159 PRThread* me = _PR_MD_CURRENT_THREAD(); 160 PRStatus rv; 161 162 PR_ASSERT(mon != NULL); 163 PR_Lock(&mon->lock); 164 if (mon->entryCount != 0) { 165 if (mon->owner == me) { 166 goto done; 167 } 168 rv = PR_Unlock(&mon->lock); 169 PR_ASSERT(rv == PR_SUCCESS); 170 return PR_FALSE; 171 } 172 /* and now I have the monitor */ 173 PR_ASSERT(mon->notifyTimes == 0); 174 PR_ASSERT(mon->owner == NULL); 175 mon->owner = me; 176 177 done: 178 mon->entryCount += 1; 179 rv = PR_Unlock(&mon->lock); 180 PR_ASSERT(rv == PR_SUCCESS); 181 return PR_TRUE; 182 } 183 184 /* 185 ** Exit the lock associated with the monitor once. 186 */ 187 PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor* mon) { 188 PRThread* me = _PR_MD_CURRENT_THREAD(); 189 PRStatus rv; 190 191 PR_ASSERT(mon != NULL); 192 PR_Lock(&mon->lock); 193 /* the entries should be > 0 and we'd better be the owner */ 194 PR_ASSERT(mon->entryCount > 0); 195 PR_ASSERT(mon->owner == me); 196 if (mon->entryCount == 0 || mon->owner != me) { 197 rv = PR_Unlock(&mon->lock); 198 PR_ASSERT(rv == PR_SUCCESS); 199 return PR_FAILURE; 200 } 201 202 mon->entryCount -= 1; /* reduce by one */ 203 if (mon->entryCount == 0) { 204 /* and if it transitioned to zero - notify an entry waiter */ 205 /* make the owner unknown */ 206 mon->owner = NULL; 207 if (mon->notifyTimes != 0) { 208 _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); 209 mon->notifyTimes = 0; 210 } 211 rv = PR_NotifyCondVar(&mon->entryCV); 212 PR_ASSERT(rv == PR_SUCCESS); 213 } 214 rv = PR_Unlock(&mon->lock); 215 PR_ASSERT(rv == PR_SUCCESS); 216 return PR_SUCCESS; 217 } 218 219 /* 220 ** Return the number of times that the current thread has entered the 221 ** lock. Returns zero if the current thread has not entered the lock. 222 */ 223 PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor* mon) { 224 PRThread* me = _PR_MD_CURRENT_THREAD(); 225 PRStatus rv; 226 PRIntn count = 0; 227 228 PR_Lock(&mon->lock); 229 if (mon->owner == me) { 230 count = mon->entryCount; 231 } 232 rv = PR_Unlock(&mon->lock); 233 PR_ASSERT(rv == PR_SUCCESS); 234 return count; 235 } 236 237 PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor* mon) { 238 #if defined(DEBUG) || defined(FORCE_PR_ASSERT) 239 PRStatus rv; 240 241 PR_Lock(&mon->lock); 242 PR_ASSERT(mon->entryCount != 0 && mon->owner == _PR_MD_CURRENT_THREAD()); 243 rv = PR_Unlock(&mon->lock); 244 PR_ASSERT(rv == PR_SUCCESS); 245 #endif 246 } 247 248 /* 249 ** Wait for a notify on the condition variable. Sleep for "ticks" amount 250 ** of time (if "tick" is 0 then the sleep is indefinite). While 251 ** the thread is waiting it exits the monitors lock (as if it called 252 ** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When 253 ** the wait has finished the thread regains control of the monitors lock 254 ** with the same entry count as before the wait began. 255 ** 256 ** The thread waiting on the monitor will be resumed when the monitor is 257 ** notified (assuming the thread is the next in line to receive the 258 ** notify) or when the "ticks" elapses. 259 ** 260 ** Returns PR_FAILURE if the caller has not locked the lock associated 261 ** with the condition variable. 262 ** This routine can return PR_PENDING_INTERRUPT_ERROR if the waiting thread 263 ** has been interrupted. 264 */ 265 PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor* mon, PRIntervalTime ticks) { 266 PRStatus rv; 267 PRUint32 saved_entries; 268 PRThread* saved_owner; 269 270 PR_ASSERT(mon != NULL); 271 PR_Lock(&mon->lock); 272 /* the entries better be positive */ 273 PR_ASSERT(mon->entryCount > 0); 274 /* and it better be owned by us */ 275 PR_ASSERT(mon->owner == _PR_MD_CURRENT_THREAD()); /* XXX return failure */ 276 277 /* tuck these away 'till later */ 278 saved_entries = mon->entryCount; 279 mon->entryCount = 0; 280 saved_owner = mon->owner; 281 mon->owner = NULL; 282 /* If we have pending notifies, post them now. */ 283 if (mon->notifyTimes != 0) { 284 _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); 285 mon->notifyTimes = 0; 286 } 287 rv = PR_NotifyCondVar(&mon->entryCV); 288 PR_ASSERT(rv == PR_SUCCESS); 289 290 rv = PR_WaitCondVar(&mon->waitCV, ticks); 291 PR_ASSERT(rv == PR_SUCCESS); 292 293 while (mon->entryCount != 0) { 294 rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT); 295 PR_ASSERT(rv == PR_SUCCESS); 296 } 297 PR_ASSERT(mon->notifyTimes == 0); 298 /* reinstate the interesting information */ 299 mon->entryCount = saved_entries; 300 mon->owner = saved_owner; 301 302 rv = PR_Unlock(&mon->lock); 303 PR_ASSERT(rv == PR_SUCCESS); 304 return rv; 305 } 306 307 /* 308 ** Notify the highest priority thread waiting on the condition 309 ** variable. If a thread is waiting on the condition variable (using 310 ** PR_Wait) then it is awakened and begins waiting on the monitor's lock. 311 */ 312 PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor* mon) { 313 _PR_PostNotifyToMonitor(mon, PR_FALSE); 314 return PR_SUCCESS; 315 } 316 317 /* 318 ** Notify all of the threads waiting on the condition variable. All of 319 ** threads are notified in turn. The highest priority thread will 320 ** probably acquire the monitor first when the monitor is exited. 321 */ 322 PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor* mon) { 323 _PR_PostNotifyToMonitor(mon, PR_TRUE); 324 return PR_SUCCESS; 325 } 326 327 /************************************************************************/ 328 329 PRUint32 _PR_MonitorToString(PRMonitor* mon, char* buf, PRUint32 buflen) { 330 PRUint32 nb; 331 332 if (mon->owner) { 333 nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld", mon, 334 mon->owner->id, mon->owner, mon->entryCount); 335 } else { 336 nb = PR_snprintf(buf, buflen, "[%p]", mon); 337 } 338 return nb; 339 }