Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | ** File: ptsynch.c |
michael@0 | 8 | ** Descritpion: Implemenation for thread synchronization using pthreads |
michael@0 | 9 | ** Exports: prlock.h, prcvar.h, prmon.h, prcmon.h |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | #if defined(_PR_PTHREADS) |
michael@0 | 13 | |
michael@0 | 14 | #include "primpl.h" |
michael@0 | 15 | #include "obsolete/prsem.h" |
michael@0 | 16 | |
michael@0 | 17 | #include <string.h> |
michael@0 | 18 | #include <pthread.h> |
michael@0 | 19 | #include <sys/time.h> |
michael@0 | 20 | |
michael@0 | 21 | static pthread_mutexattr_t _pt_mattr; |
michael@0 | 22 | static pthread_condattr_t _pt_cvar_attr; |
michael@0 | 23 | |
michael@0 | 24 | #if defined(DEBUG) |
michael@0 | 25 | extern PTDebug pt_debug; /* this is shared between several modules */ |
michael@0 | 26 | |
michael@0 | 27 | #if defined(_PR_DCETHREADS) |
michael@0 | 28 | static pthread_t pt_zero_tid; /* a null pthread_t (pthread_t is a struct |
michael@0 | 29 | * in DCE threads) to compare with */ |
michael@0 | 30 | #endif /* defined(_PR_DCETHREADS) */ |
michael@0 | 31 | #endif /* defined(DEBUG) */ |
michael@0 | 32 | |
michael@0 | 33 | #if defined(FREEBSD) |
michael@0 | 34 | /* |
michael@0 | 35 | * On older versions of FreeBSD, pthread_mutex_trylock returns EDEADLK. |
michael@0 | 36 | * Newer versions return EBUSY. We still need to support both. |
michael@0 | 37 | */ |
michael@0 | 38 | static int |
michael@0 | 39 | pt_pthread_mutex_is_locked(pthread_mutex_t *m) |
michael@0 | 40 | { |
michael@0 | 41 | int rv = pthread_mutex_trylock(m); |
michael@0 | 42 | return (EBUSY == rv || EDEADLK == rv); |
michael@0 | 43 | } |
michael@0 | 44 | #endif |
michael@0 | 45 | |
michael@0 | 46 | /**************************************************************/ |
michael@0 | 47 | /**************************************************************/ |
michael@0 | 48 | /*****************************LOCKS****************************/ |
michael@0 | 49 | /**************************************************************/ |
michael@0 | 50 | /**************************************************************/ |
michael@0 | 51 | |
michael@0 | 52 | void _PR_InitLocks(void) |
michael@0 | 53 | { |
michael@0 | 54 | int rv; |
michael@0 | 55 | rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr); |
michael@0 | 56 | PR_ASSERT(0 == rv); |
michael@0 | 57 | |
michael@0 | 58 | #ifdef LINUX |
michael@0 | 59 | #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) |
michael@0 | 60 | rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP); |
michael@0 | 61 | PR_ASSERT(0 == rv); |
michael@0 | 62 | #endif |
michael@0 | 63 | #endif |
michael@0 | 64 | |
michael@0 | 65 | rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr); |
michael@0 | 66 | PR_ASSERT(0 == rv); |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | static void pt_PostNotifies(PRLock *lock, PRBool unlock) |
michael@0 | 70 | { |
michael@0 | 71 | PRIntn index, rv; |
michael@0 | 72 | _PT_Notified post; |
michael@0 | 73 | _PT_Notified *notified, *prev = NULL; |
michael@0 | 74 | /* |
michael@0 | 75 | * Time to actually notify any conditions that were affected |
michael@0 | 76 | * while the lock was held. Get a copy of the list that's in |
michael@0 | 77 | * the lock structure and then zero the original. If it's |
michael@0 | 78 | * linked to other such structures, we own that storage. |
michael@0 | 79 | */ |
michael@0 | 80 | post = lock->notified; /* a safe copy; we own the lock */ |
michael@0 | 81 | |
michael@0 | 82 | #if defined(DEBUG) |
michael@0 | 83 | memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */ |
michael@0 | 84 | #else |
michael@0 | 85 | lock->notified.length = 0; /* these are really sufficient */ |
michael@0 | 86 | lock->notified.link = NULL; |
michael@0 | 87 | #endif |
michael@0 | 88 | |
michael@0 | 89 | /* should (may) we release lock before notifying? */ |
michael@0 | 90 | if (unlock) |
michael@0 | 91 | { |
michael@0 | 92 | rv = pthread_mutex_unlock(&lock->mutex); |
michael@0 | 93 | PR_ASSERT(0 == rv); |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | notified = &post; /* this is where we start */ |
michael@0 | 97 | do |
michael@0 | 98 | { |
michael@0 | 99 | for (index = 0; index < notified->length; ++index) |
michael@0 | 100 | { |
michael@0 | 101 | PRCondVar *cv = notified->cv[index].cv; |
michael@0 | 102 | PR_ASSERT(NULL != cv); |
michael@0 | 103 | PR_ASSERT(0 != notified->cv[index].times); |
michael@0 | 104 | if (-1 == notified->cv[index].times) |
michael@0 | 105 | { |
michael@0 | 106 | rv = pthread_cond_broadcast(&cv->cv); |
michael@0 | 107 | PR_ASSERT(0 == rv); |
michael@0 | 108 | } |
michael@0 | 109 | else |
michael@0 | 110 | { |
michael@0 | 111 | while (notified->cv[index].times-- > 0) |
michael@0 | 112 | { |
michael@0 | 113 | rv = pthread_cond_signal(&cv->cv); |
michael@0 | 114 | PR_ASSERT(0 == rv); |
michael@0 | 115 | } |
michael@0 | 116 | } |
michael@0 | 117 | #if defined(DEBUG) |
michael@0 | 118 | pt_debug.cvars_notified += 1; |
michael@0 | 119 | if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) |
michael@0 | 120 | { |
michael@0 | 121 | pt_debug.delayed_cv_deletes += 1; |
michael@0 | 122 | PR_DestroyCondVar(cv); |
michael@0 | 123 | } |
michael@0 | 124 | #else /* defined(DEBUG) */ |
michael@0 | 125 | if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) |
michael@0 | 126 | PR_DestroyCondVar(cv); |
michael@0 | 127 | #endif /* defined(DEBUG) */ |
michael@0 | 128 | } |
michael@0 | 129 | prev = notified; |
michael@0 | 130 | notified = notified->link; |
michael@0 | 131 | if (&post != prev) PR_DELETE(prev); |
michael@0 | 132 | } while (NULL != notified); |
michael@0 | 133 | } /* pt_PostNotifies */ |
michael@0 | 134 | |
michael@0 | 135 | PR_IMPLEMENT(PRLock*) PR_NewLock(void) |
michael@0 | 136 | { |
michael@0 | 137 | PRIntn rv; |
michael@0 | 138 | PRLock *lock; |
michael@0 | 139 | |
michael@0 | 140 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 141 | |
michael@0 | 142 | lock = PR_NEWZAP(PRLock); |
michael@0 | 143 | if (lock != NULL) |
michael@0 | 144 | { |
michael@0 | 145 | rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr); |
michael@0 | 146 | PR_ASSERT(0 == rv); |
michael@0 | 147 | } |
michael@0 | 148 | #if defined(DEBUG) |
michael@0 | 149 | pt_debug.locks_created += 1; |
michael@0 | 150 | #endif |
michael@0 | 151 | return lock; |
michael@0 | 152 | } /* PR_NewLock */ |
michael@0 | 153 | |
michael@0 | 154 | PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) |
michael@0 | 155 | { |
michael@0 | 156 | PRIntn rv; |
michael@0 | 157 | PR_ASSERT(NULL != lock); |
michael@0 | 158 | PR_ASSERT(PR_FALSE == lock->locked); |
michael@0 | 159 | PR_ASSERT(0 == lock->notified.length); |
michael@0 | 160 | PR_ASSERT(NULL == lock->notified.link); |
michael@0 | 161 | rv = pthread_mutex_destroy(&lock->mutex); |
michael@0 | 162 | PR_ASSERT(0 == rv); |
michael@0 | 163 | #if defined(DEBUG) |
michael@0 | 164 | memset(lock, 0xaf, sizeof(PRLock)); |
michael@0 | 165 | pt_debug.locks_destroyed += 1; |
michael@0 | 166 | #endif |
michael@0 | 167 | PR_Free(lock); |
michael@0 | 168 | } /* PR_DestroyLock */ |
michael@0 | 169 | |
michael@0 | 170 | PR_IMPLEMENT(void) PR_Lock(PRLock *lock) |
michael@0 | 171 | { |
michael@0 | 172 | /* Nb: PR_Lock must not call PR_GetCurrentThread to access the |id| or |
michael@0 | 173 | * |tid| field of the current thread's PRThread structure because |
michael@0 | 174 | * _pt_root calls PR_Lock before setting thred->id and thred->tid. */ |
michael@0 | 175 | PRIntn rv; |
michael@0 | 176 | PR_ASSERT(lock != NULL); |
michael@0 | 177 | rv = pthread_mutex_lock(&lock->mutex); |
michael@0 | 178 | PR_ASSERT(0 == rv); |
michael@0 | 179 | PR_ASSERT(0 == lock->notified.length); |
michael@0 | 180 | PR_ASSERT(NULL == lock->notified.link); |
michael@0 | 181 | PR_ASSERT(PR_FALSE == lock->locked); |
michael@0 | 182 | /* Nb: the order of the next two statements is not critical to |
michael@0 | 183 | * the correctness of PR_AssertCurrentThreadOwnsLock(), but |
michael@0 | 184 | * this particular order makes the assertion more likely to |
michael@0 | 185 | * catch errors. */ |
michael@0 | 186 | lock->owner = pthread_self(); |
michael@0 | 187 | lock->locked = PR_TRUE; |
michael@0 | 188 | #if defined(DEBUG) |
michael@0 | 189 | pt_debug.locks_acquired += 1; |
michael@0 | 190 | #endif |
michael@0 | 191 | } /* PR_Lock */ |
michael@0 | 192 | |
michael@0 | 193 | PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) |
michael@0 | 194 | { |
michael@0 | 195 | pthread_t self = pthread_self(); |
michael@0 | 196 | PRIntn rv; |
michael@0 | 197 | |
michael@0 | 198 | PR_ASSERT(lock != NULL); |
michael@0 | 199 | PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex)); |
michael@0 | 200 | PR_ASSERT(PR_TRUE == lock->locked); |
michael@0 | 201 | PR_ASSERT(pthread_equal(lock->owner, self)); |
michael@0 | 202 | |
michael@0 | 203 | if (!lock->locked || !pthread_equal(lock->owner, self)) |
michael@0 | 204 | return PR_FAILURE; |
michael@0 | 205 | |
michael@0 | 206 | lock->locked = PR_FALSE; |
michael@0 | 207 | if (0 == lock->notified.length) /* shortcut */ |
michael@0 | 208 | { |
michael@0 | 209 | rv = pthread_mutex_unlock(&lock->mutex); |
michael@0 | 210 | PR_ASSERT(0 == rv); |
michael@0 | 211 | } |
michael@0 | 212 | else pt_PostNotifies(lock, PR_TRUE); |
michael@0 | 213 | |
michael@0 | 214 | #if defined(DEBUG) |
michael@0 | 215 | pt_debug.locks_released += 1; |
michael@0 | 216 | #endif |
michael@0 | 217 | return PR_SUCCESS; |
michael@0 | 218 | } /* PR_Unlock */ |
michael@0 | 219 | |
michael@0 | 220 | PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock) |
michael@0 | 221 | { |
michael@0 | 222 | /* Nb: the order of the |locked| and |owner==me| checks is not critical |
michael@0 | 223 | * to the correctness of PR_AssertCurrentThreadOwnsLock(), but |
michael@0 | 224 | * this particular order makes the assertion more likely to |
michael@0 | 225 | * catch errors. */ |
michael@0 | 226 | PR_ASSERT(lock->locked && pthread_equal(lock->owner, pthread_self())); |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | /**************************************************************/ |
michael@0 | 230 | /**************************************************************/ |
michael@0 | 231 | /***************************CONDITIONS*************************/ |
michael@0 | 232 | /**************************************************************/ |
michael@0 | 233 | /**************************************************************/ |
michael@0 | 234 | |
michael@0 | 235 | |
michael@0 | 236 | /* |
michael@0 | 237 | * This code is used to compute the absolute time for the wakeup. |
michael@0 | 238 | * It's moderately ugly, so it's defined here and called in a |
michael@0 | 239 | * couple of places. |
michael@0 | 240 | */ |
michael@0 | 241 | #define PT_NANOPERMICRO 1000UL |
michael@0 | 242 | #define PT_BILLION 1000000000UL |
michael@0 | 243 | |
michael@0 | 244 | static PRIntn pt_TimedWait( |
michael@0 | 245 | pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout) |
michael@0 | 246 | { |
michael@0 | 247 | int rv; |
michael@0 | 248 | struct timeval now; |
michael@0 | 249 | struct timespec tmo; |
michael@0 | 250 | PRUint32 ticks = PR_TicksPerSecond(); |
michael@0 | 251 | |
michael@0 | 252 | tmo.tv_sec = (PRInt32)(timeout / ticks); |
michael@0 | 253 | tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks)); |
michael@0 | 254 | tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec); |
michael@0 | 255 | |
michael@0 | 256 | /* pthreads wants this in absolute time, off we go ... */ |
michael@0 | 257 | (void)GETTIMEOFDAY(&now); |
michael@0 | 258 | /* that one's usecs, this one's nsecs - grrrr! */ |
michael@0 | 259 | tmo.tv_sec += now.tv_sec; |
michael@0 | 260 | tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec); |
michael@0 | 261 | tmo.tv_sec += tmo.tv_nsec / PT_BILLION; |
michael@0 | 262 | tmo.tv_nsec %= PT_BILLION; |
michael@0 | 263 | |
michael@0 | 264 | rv = pthread_cond_timedwait(cv, ml, &tmo); |
michael@0 | 265 | |
michael@0 | 266 | /* NSPR doesn't report timeouts */ |
michael@0 | 267 | #ifdef _PR_DCETHREADS |
michael@0 | 268 | if (rv == -1) return (errno == EAGAIN) ? 0 : errno; |
michael@0 | 269 | else return rv; |
michael@0 | 270 | #else |
michael@0 | 271 | return (rv == ETIMEDOUT) ? 0 : rv; |
michael@0 | 272 | #endif |
michael@0 | 273 | } /* pt_TimedWait */ |
michael@0 | 274 | |
michael@0 | 275 | |
michael@0 | 276 | /* |
michael@0 | 277 | * Notifies just get posted to the protecting mutex. The |
michael@0 | 278 | * actual notification is done when the lock is released so that |
michael@0 | 279 | * MP systems don't contend for a lock that they can't have. |
michael@0 | 280 | */ |
michael@0 | 281 | static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast) |
michael@0 | 282 | { |
michael@0 | 283 | PRIntn index = 0; |
michael@0 | 284 | _PT_Notified *notified = &cvar->lock->notified; |
michael@0 | 285 | |
michael@0 | 286 | PR_ASSERT(PR_TRUE == cvar->lock->locked); |
michael@0 | 287 | PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); |
michael@0 | 288 | PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); |
michael@0 | 289 | |
michael@0 | 290 | while (1) |
michael@0 | 291 | { |
michael@0 | 292 | for (index = 0; index < notified->length; ++index) |
michael@0 | 293 | { |
michael@0 | 294 | if (notified->cv[index].cv == cvar) |
michael@0 | 295 | { |
michael@0 | 296 | if (broadcast) |
michael@0 | 297 | notified->cv[index].times = -1; |
michael@0 | 298 | else if (-1 != notified->cv[index].times) |
michael@0 | 299 | notified->cv[index].times += 1; |
michael@0 | 300 | return; /* we're finished */ |
michael@0 | 301 | } |
michael@0 | 302 | } |
michael@0 | 303 | /* if not full, enter new CV in this array */ |
michael@0 | 304 | if (notified->length < PT_CV_NOTIFIED_LENGTH) break; |
michael@0 | 305 | |
michael@0 | 306 | /* if there's no link, create an empty array and link it */ |
michael@0 | 307 | if (NULL == notified->link) |
michael@0 | 308 | notified->link = PR_NEWZAP(_PT_Notified); |
michael@0 | 309 | notified = notified->link; |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | /* A brand new entry in the array */ |
michael@0 | 313 | (void)PR_ATOMIC_INCREMENT(&cvar->notify_pending); |
michael@0 | 314 | notified->cv[index].times = (broadcast) ? -1 : 1; |
michael@0 | 315 | notified->cv[index].cv = cvar; |
michael@0 | 316 | notified->length += 1; |
michael@0 | 317 | } /* pt_PostNotifyToCvar */ |
michael@0 | 318 | |
michael@0 | 319 | PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock) |
michael@0 | 320 | { |
michael@0 | 321 | PRCondVar *cv = PR_NEW(PRCondVar); |
michael@0 | 322 | PR_ASSERT(lock != NULL); |
michael@0 | 323 | if (cv != NULL) |
michael@0 | 324 | { |
michael@0 | 325 | int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); |
michael@0 | 326 | PR_ASSERT(0 == rv); |
michael@0 | 327 | cv->lock = lock; |
michael@0 | 328 | cv->notify_pending = 0; |
michael@0 | 329 | #if defined(DEBUG) |
michael@0 | 330 | pt_debug.cvars_created += 1; |
michael@0 | 331 | #endif |
michael@0 | 332 | } |
michael@0 | 333 | return cv; |
michael@0 | 334 | } /* PR_NewCondVar */ |
michael@0 | 335 | |
michael@0 | 336 | PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar) |
michael@0 | 337 | { |
michael@0 | 338 | if (0 > PR_ATOMIC_DECREMENT(&cvar->notify_pending)) |
michael@0 | 339 | { |
michael@0 | 340 | PRIntn rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv); |
michael@0 | 341 | #if defined(DEBUG) |
michael@0 | 342 | memset(cvar, 0xaf, sizeof(PRCondVar)); |
michael@0 | 343 | pt_debug.cvars_destroyed += 1; |
michael@0 | 344 | #endif |
michael@0 | 345 | PR_Free(cvar); |
michael@0 | 346 | } |
michael@0 | 347 | } /* PR_DestroyCondVar */ |
michael@0 | 348 | |
michael@0 | 349 | PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout) |
michael@0 | 350 | { |
michael@0 | 351 | PRIntn rv; |
michael@0 | 352 | PRThread *thred = PR_GetCurrentThread(); |
michael@0 | 353 | |
michael@0 | 354 | PR_ASSERT(cvar != NULL); |
michael@0 | 355 | /* We'd better be locked */ |
michael@0 | 356 | PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); |
michael@0 | 357 | PR_ASSERT(PR_TRUE == cvar->lock->locked); |
michael@0 | 358 | /* and it better be by us */ |
michael@0 | 359 | PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); |
michael@0 | 360 | |
michael@0 | 361 | if (_PT_THREAD_INTERRUPTED(thred)) goto aborted; |
michael@0 | 362 | |
michael@0 | 363 | /* |
michael@0 | 364 | * The thread waiting is used for PR_Interrupt |
michael@0 | 365 | */ |
michael@0 | 366 | thred->waiting = cvar; /* this is where we're waiting */ |
michael@0 | 367 | |
michael@0 | 368 | /* |
michael@0 | 369 | * If we have pending notifies, post them now. |
michael@0 | 370 | * |
michael@0 | 371 | * This is not optimal. We're going to post these notifies |
michael@0 | 372 | * while we're holding the lock. That means on MP systems |
michael@0 | 373 | * that they are going to collide for the lock that we will |
michael@0 | 374 | * hold until we actually wait. |
michael@0 | 375 | */ |
michael@0 | 376 | if (0 != cvar->lock->notified.length) |
michael@0 | 377 | pt_PostNotifies(cvar->lock, PR_FALSE); |
michael@0 | 378 | |
michael@0 | 379 | /* |
michael@0 | 380 | * We're surrendering the lock, so clear out the locked field. |
michael@0 | 381 | */ |
michael@0 | 382 | cvar->lock->locked = PR_FALSE; |
michael@0 | 383 | |
michael@0 | 384 | if (timeout == PR_INTERVAL_NO_TIMEOUT) |
michael@0 | 385 | rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex); |
michael@0 | 386 | else |
michael@0 | 387 | rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout); |
michael@0 | 388 | |
michael@0 | 389 | /* We just got the lock back - this better be empty */ |
michael@0 | 390 | PR_ASSERT(PR_FALSE == cvar->lock->locked); |
michael@0 | 391 | cvar->lock->locked = PR_TRUE; |
michael@0 | 392 | cvar->lock->owner = pthread_self(); |
michael@0 | 393 | |
michael@0 | 394 | PR_ASSERT(0 == cvar->lock->notified.length); |
michael@0 | 395 | thred->waiting = NULL; /* and now we're not */ |
michael@0 | 396 | if (_PT_THREAD_INTERRUPTED(thred)) goto aborted; |
michael@0 | 397 | if (rv != 0) |
michael@0 | 398 | { |
michael@0 | 399 | _PR_MD_MAP_DEFAULT_ERROR(rv); |
michael@0 | 400 | return PR_FAILURE; |
michael@0 | 401 | } |
michael@0 | 402 | return PR_SUCCESS; |
michael@0 | 403 | |
michael@0 | 404 | aborted: |
michael@0 | 405 | PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); |
michael@0 | 406 | thred->state &= ~PT_THREAD_ABORTED; |
michael@0 | 407 | return PR_FAILURE; |
michael@0 | 408 | } /* PR_WaitCondVar */ |
michael@0 | 409 | |
michael@0 | 410 | PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar) |
michael@0 | 411 | { |
michael@0 | 412 | PR_ASSERT(cvar != NULL); |
michael@0 | 413 | pt_PostNotifyToCvar(cvar, PR_FALSE); |
michael@0 | 414 | return PR_SUCCESS; |
michael@0 | 415 | } /* PR_NotifyCondVar */ |
michael@0 | 416 | |
michael@0 | 417 | PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar) |
michael@0 | 418 | { |
michael@0 | 419 | PR_ASSERT(cvar != NULL); |
michael@0 | 420 | pt_PostNotifyToCvar(cvar, PR_TRUE); |
michael@0 | 421 | return PR_SUCCESS; |
michael@0 | 422 | } /* PR_NotifyAllCondVar */ |
michael@0 | 423 | |
michael@0 | 424 | /**************************************************************/ |
michael@0 | 425 | /**************************************************************/ |
michael@0 | 426 | /***************************MONITORS***************************/ |
michael@0 | 427 | /**************************************************************/ |
michael@0 | 428 | /**************************************************************/ |
michael@0 | 429 | |
michael@0 | 430 | /* |
michael@0 | 431 | * Notifies just get posted to the monitor. The actual notification is done |
michael@0 | 432 | * when the monitor is fully exited so that MP systems don't contend for a |
michael@0 | 433 | * monitor that they can't enter. |
michael@0 | 434 | */ |
michael@0 | 435 | static void pt_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast) |
michael@0 | 436 | { |
michael@0 | 437 | PR_ASSERT(NULL != mon); |
michael@0 | 438 | PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon); |
michael@0 | 439 | |
michael@0 | 440 | /* mon->notifyTimes is protected by the monitor, so we don't need to |
michael@0 | 441 | * acquire mon->lock. |
michael@0 | 442 | */ |
michael@0 | 443 | if (broadcast) |
michael@0 | 444 | mon->notifyTimes = -1; |
michael@0 | 445 | else if (-1 != mon->notifyTimes) |
michael@0 | 446 | mon->notifyTimes += 1; |
michael@0 | 447 | } /* pt_PostNotifyToMonitor */ |
michael@0 | 448 | |
michael@0 | 449 | static void pt_PostNotifiesFromMonitor(pthread_cond_t *cv, PRIntn times) |
michael@0 | 450 | { |
michael@0 | 451 | PRIntn rv; |
michael@0 | 452 | |
michael@0 | 453 | /* |
michael@0 | 454 | * Time to actually notify any waits that were affected while the monitor |
michael@0 | 455 | * was entered. |
michael@0 | 456 | */ |
michael@0 | 457 | PR_ASSERT(NULL != cv); |
michael@0 | 458 | PR_ASSERT(0 != times); |
michael@0 | 459 | if (-1 == times) |
michael@0 | 460 | { |
michael@0 | 461 | rv = pthread_cond_broadcast(cv); |
michael@0 | 462 | PR_ASSERT(0 == rv); |
michael@0 | 463 | } |
michael@0 | 464 | else |
michael@0 | 465 | { |
michael@0 | 466 | while (times-- > 0) |
michael@0 | 467 | { |
michael@0 | 468 | rv = pthread_cond_signal(cv); |
michael@0 | 469 | PR_ASSERT(0 == rv); |
michael@0 | 470 | } |
michael@0 | 471 | } |
michael@0 | 472 | } /* pt_PostNotifiesFromMonitor */ |
michael@0 | 473 | |
michael@0 | 474 | PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void) |
michael@0 | 475 | { |
michael@0 | 476 | PRMonitor *mon; |
michael@0 | 477 | int rv; |
michael@0 | 478 | |
michael@0 | 479 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 480 | |
michael@0 | 481 | mon = PR_NEWZAP(PRMonitor); |
michael@0 | 482 | if (mon == NULL) |
michael@0 | 483 | { |
michael@0 | 484 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
michael@0 | 485 | return NULL; |
michael@0 | 486 | } |
michael@0 | 487 | |
michael@0 | 488 | rv = _PT_PTHREAD_MUTEX_INIT(mon->lock, _pt_mattr); |
michael@0 | 489 | PR_ASSERT(0 == rv); |
michael@0 | 490 | if (0 != rv) |
michael@0 | 491 | goto error1; |
michael@0 | 492 | |
michael@0 | 493 | _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); |
michael@0 | 494 | |
michael@0 | 495 | rv = _PT_PTHREAD_COND_INIT(mon->entryCV, _pt_cvar_attr); |
michael@0 | 496 | PR_ASSERT(0 == rv); |
michael@0 | 497 | if (0 != rv) |
michael@0 | 498 | goto error2; |
michael@0 | 499 | |
michael@0 | 500 | rv = _PT_PTHREAD_COND_INIT(mon->waitCV, _pt_cvar_attr); |
michael@0 | 501 | PR_ASSERT(0 == rv); |
michael@0 | 502 | if (0 != rv) |
michael@0 | 503 | goto error3; |
michael@0 | 504 | |
michael@0 | 505 | mon->notifyTimes = 0; |
michael@0 | 506 | mon->entryCount = 0; |
michael@0 | 507 | mon->refCount = 1; |
michael@0 | 508 | mon->name = NULL; |
michael@0 | 509 | return mon; |
michael@0 | 510 | |
michael@0 | 511 | error3: |
michael@0 | 512 | pthread_cond_destroy(&mon->entryCV); |
michael@0 | 513 | error2: |
michael@0 | 514 | pthread_mutex_destroy(&mon->lock); |
michael@0 | 515 | error1: |
michael@0 | 516 | PR_Free(mon); |
michael@0 | 517 | _PR_MD_MAP_DEFAULT_ERROR(rv); |
michael@0 | 518 | return NULL; |
michael@0 | 519 | } /* PR_NewMonitor */ |
michael@0 | 520 | |
michael@0 | 521 | PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) |
michael@0 | 522 | { |
michael@0 | 523 | PRMonitor* mon = PR_NewMonitor(); |
michael@0 | 524 | if (mon) |
michael@0 | 525 | mon->name = name; |
michael@0 | 526 | return mon; |
michael@0 | 527 | } |
michael@0 | 528 | |
michael@0 | 529 | PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon) |
michael@0 | 530 | { |
michael@0 | 531 | int rv; |
michael@0 | 532 | |
michael@0 | 533 | PR_ASSERT(mon != NULL); |
michael@0 | 534 | if (PR_ATOMIC_DECREMENT(&mon->refCount) == 0) |
michael@0 | 535 | { |
michael@0 | 536 | rv = pthread_cond_destroy(&mon->waitCV); PR_ASSERT(0 == rv); |
michael@0 | 537 | rv = pthread_cond_destroy(&mon->entryCV); PR_ASSERT(0 == rv); |
michael@0 | 538 | rv = pthread_mutex_destroy(&mon->lock); PR_ASSERT(0 == rv); |
michael@0 | 539 | #if defined(DEBUG) |
michael@0 | 540 | memset(mon, 0xaf, sizeof(PRMonitor)); |
michael@0 | 541 | #endif |
michael@0 | 542 | PR_Free(mon); |
michael@0 | 543 | } |
michael@0 | 544 | } /* PR_DestroyMonitor */ |
michael@0 | 545 | |
michael@0 | 546 | /* The GC uses this; it is quite arguably a bad interface. I'm just |
michael@0 | 547 | * duplicating it for now - XXXMB |
michael@0 | 548 | */ |
michael@0 | 549 | PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon) |
michael@0 | 550 | { |
michael@0 | 551 | pthread_t self = pthread_self(); |
michael@0 | 552 | PRIntn rv; |
michael@0 | 553 | PRIntn count = 0; |
michael@0 | 554 | |
michael@0 | 555 | rv = pthread_mutex_lock(&mon->lock); |
michael@0 | 556 | PR_ASSERT(0 == rv); |
michael@0 | 557 | if (pthread_equal(mon->owner, self)) |
michael@0 | 558 | count = mon->entryCount; |
michael@0 | 559 | rv = pthread_mutex_unlock(&mon->lock); |
michael@0 | 560 | PR_ASSERT(0 == rv); |
michael@0 | 561 | return count; |
michael@0 | 562 | } |
michael@0 | 563 | |
michael@0 | 564 | PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon) |
michael@0 | 565 | { |
michael@0 | 566 | #if defined(DEBUG) || defined(FORCE_PR_ASSERT) |
michael@0 | 567 | PRIntn rv; |
michael@0 | 568 | |
michael@0 | 569 | rv = pthread_mutex_lock(&mon->lock); |
michael@0 | 570 | PR_ASSERT(0 == rv); |
michael@0 | 571 | PR_ASSERT(mon->entryCount != 0 && |
michael@0 | 572 | pthread_equal(mon->owner, pthread_self())); |
michael@0 | 573 | rv = pthread_mutex_unlock(&mon->lock); |
michael@0 | 574 | PR_ASSERT(0 == rv); |
michael@0 | 575 | #endif |
michael@0 | 576 | } |
michael@0 | 577 | |
michael@0 | 578 | PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon) |
michael@0 | 579 | { |
michael@0 | 580 | pthread_t self = pthread_self(); |
michael@0 | 581 | PRIntn rv; |
michael@0 | 582 | |
michael@0 | 583 | PR_ASSERT(mon != NULL); |
michael@0 | 584 | rv = pthread_mutex_lock(&mon->lock); |
michael@0 | 585 | PR_ASSERT(0 == rv); |
michael@0 | 586 | if (mon->entryCount != 0) |
michael@0 | 587 | { |
michael@0 | 588 | if (pthread_equal(mon->owner, self)) |
michael@0 | 589 | goto done; |
michael@0 | 590 | while (mon->entryCount != 0) |
michael@0 | 591 | { |
michael@0 | 592 | rv = pthread_cond_wait(&mon->entryCV, &mon->lock); |
michael@0 | 593 | PR_ASSERT(0 == rv); |
michael@0 | 594 | } |
michael@0 | 595 | } |
michael@0 | 596 | /* and now I have the monitor */ |
michael@0 | 597 | PR_ASSERT(0 == mon->notifyTimes); |
michael@0 | 598 | PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner)); |
michael@0 | 599 | _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner); |
michael@0 | 600 | |
michael@0 | 601 | done: |
michael@0 | 602 | mon->entryCount += 1; |
michael@0 | 603 | rv = pthread_mutex_unlock(&mon->lock); |
michael@0 | 604 | PR_ASSERT(0 == rv); |
michael@0 | 605 | } /* PR_EnterMonitor */ |
michael@0 | 606 | |
michael@0 | 607 | PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) |
michael@0 | 608 | { |
michael@0 | 609 | pthread_t self = pthread_self(); |
michael@0 | 610 | PRIntn rv; |
michael@0 | 611 | PRBool notifyEntryWaiter = PR_FALSE; |
michael@0 | 612 | PRIntn notifyTimes = 0; |
michael@0 | 613 | |
michael@0 | 614 | PR_ASSERT(mon != NULL); |
michael@0 | 615 | rv = pthread_mutex_lock(&mon->lock); |
michael@0 | 616 | PR_ASSERT(0 == rv); |
michael@0 | 617 | /* the entries should be > 0 and we'd better be the owner */ |
michael@0 | 618 | PR_ASSERT(mon->entryCount > 0); |
michael@0 | 619 | PR_ASSERT(pthread_equal(mon->owner, self)); |
michael@0 | 620 | if (mon->entryCount == 0 || !pthread_equal(mon->owner, self)) |
michael@0 | 621 | { |
michael@0 | 622 | rv = pthread_mutex_unlock(&mon->lock); |
michael@0 | 623 | PR_ASSERT(0 == rv); |
michael@0 | 624 | return PR_FAILURE; |
michael@0 | 625 | } |
michael@0 | 626 | |
michael@0 | 627 | mon->entryCount -= 1; /* reduce by one */ |
michael@0 | 628 | if (mon->entryCount == 0) |
michael@0 | 629 | { |
michael@0 | 630 | /* and if it transitioned to zero - notify an entry waiter */ |
michael@0 | 631 | /* make the owner unknown */ |
michael@0 | 632 | _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); |
michael@0 | 633 | notifyEntryWaiter = PR_TRUE; |
michael@0 | 634 | notifyTimes = mon->notifyTimes; |
michael@0 | 635 | mon->notifyTimes = 0; |
michael@0 | 636 | /* We will access the members of 'mon' after unlocking mon->lock. |
michael@0 | 637 | * Add a reference. */ |
michael@0 | 638 | PR_ATOMIC_INCREMENT(&mon->refCount); |
michael@0 | 639 | } |
michael@0 | 640 | rv = pthread_mutex_unlock(&mon->lock); |
michael@0 | 641 | PR_ASSERT(0 == rv); |
michael@0 | 642 | if (notifyEntryWaiter) |
michael@0 | 643 | { |
michael@0 | 644 | if (notifyTimes) |
michael@0 | 645 | pt_PostNotifiesFromMonitor(&mon->waitCV, notifyTimes); |
michael@0 | 646 | rv = pthread_cond_signal(&mon->entryCV); |
michael@0 | 647 | PR_ASSERT(0 == rv); |
michael@0 | 648 | /* We are done accessing the members of 'mon'. Release the |
michael@0 | 649 | * reference. */ |
michael@0 | 650 | PR_DestroyMonitor(mon); |
michael@0 | 651 | } |
michael@0 | 652 | return PR_SUCCESS; |
michael@0 | 653 | } /* PR_ExitMonitor */ |
michael@0 | 654 | |
michael@0 | 655 | PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout) |
michael@0 | 656 | { |
michael@0 | 657 | PRStatus rv; |
michael@0 | 658 | PRUint32 saved_entries; |
michael@0 | 659 | pthread_t saved_owner; |
michael@0 | 660 | |
michael@0 | 661 | PR_ASSERT(mon != NULL); |
michael@0 | 662 | rv = pthread_mutex_lock(&mon->lock); |
michael@0 | 663 | PR_ASSERT(0 == rv); |
michael@0 | 664 | /* the entries better be positive */ |
michael@0 | 665 | PR_ASSERT(mon->entryCount > 0); |
michael@0 | 666 | /* and it better be owned by us */ |
michael@0 | 667 | PR_ASSERT(pthread_equal(mon->owner, pthread_self())); |
michael@0 | 668 | |
michael@0 | 669 | /* tuck these away 'till later */ |
michael@0 | 670 | saved_entries = mon->entryCount; |
michael@0 | 671 | mon->entryCount = 0; |
michael@0 | 672 | _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner); |
michael@0 | 673 | _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); |
michael@0 | 674 | /* |
michael@0 | 675 | * If we have pending notifies, post them now. |
michael@0 | 676 | * |
michael@0 | 677 | * This is not optimal. We're going to post these notifies |
michael@0 | 678 | * while we're holding the lock. That means on MP systems |
michael@0 | 679 | * that they are going to collide for the lock that we will |
michael@0 | 680 | * hold until we actually wait. |
michael@0 | 681 | */ |
michael@0 | 682 | if (0 != mon->notifyTimes) |
michael@0 | 683 | { |
michael@0 | 684 | pt_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); |
michael@0 | 685 | mon->notifyTimes = 0; |
michael@0 | 686 | } |
michael@0 | 687 | rv = pthread_cond_signal(&mon->entryCV); |
michael@0 | 688 | PR_ASSERT(0 == rv); |
michael@0 | 689 | |
michael@0 | 690 | if (timeout == PR_INTERVAL_NO_TIMEOUT) |
michael@0 | 691 | rv = pthread_cond_wait(&mon->waitCV, &mon->lock); |
michael@0 | 692 | else |
michael@0 | 693 | rv = pt_TimedWait(&mon->waitCV, &mon->lock, timeout); |
michael@0 | 694 | PR_ASSERT(0 == rv); |
michael@0 | 695 | |
michael@0 | 696 | while (mon->entryCount != 0) |
michael@0 | 697 | { |
michael@0 | 698 | rv = pthread_cond_wait(&mon->entryCV, &mon->lock); |
michael@0 | 699 | PR_ASSERT(0 == rv); |
michael@0 | 700 | } |
michael@0 | 701 | PR_ASSERT(0 == mon->notifyTimes); |
michael@0 | 702 | /* reinstate the interesting information */ |
michael@0 | 703 | mon->entryCount = saved_entries; |
michael@0 | 704 | _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner); |
michael@0 | 705 | |
michael@0 | 706 | rv = pthread_mutex_unlock(&mon->lock); |
michael@0 | 707 | PR_ASSERT(0 == rv); |
michael@0 | 708 | return rv; |
michael@0 | 709 | } /* PR_Wait */ |
michael@0 | 710 | |
michael@0 | 711 | PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon) |
michael@0 | 712 | { |
michael@0 | 713 | pt_PostNotifyToMonitor(mon, PR_FALSE); |
michael@0 | 714 | return PR_SUCCESS; |
michael@0 | 715 | } /* PR_Notify */ |
michael@0 | 716 | |
michael@0 | 717 | PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon) |
michael@0 | 718 | { |
michael@0 | 719 | pt_PostNotifyToMonitor(mon, PR_TRUE); |
michael@0 | 720 | return PR_SUCCESS; |
michael@0 | 721 | } /* PR_NotifyAll */ |
michael@0 | 722 | |
michael@0 | 723 | /**************************************************************/ |
michael@0 | 724 | /**************************************************************/ |
michael@0 | 725 | /**************************SEMAPHORES**************************/ |
michael@0 | 726 | /**************************************************************/ |
michael@0 | 727 | /**************************************************************/ |
michael@0 | 728 | PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *semaphore) |
michael@0 | 729 | { |
michael@0 | 730 | static PRBool unwarned = PR_TRUE; |
michael@0 | 731 | if (unwarned) unwarned = _PR_Obsolete( |
michael@0 | 732 | "PR_PostSem", "locks & condition variables"); |
michael@0 | 733 | PR_Lock(semaphore->cvar->lock); |
michael@0 | 734 | PR_NotifyCondVar(semaphore->cvar); |
michael@0 | 735 | semaphore->count += 1; |
michael@0 | 736 | PR_Unlock(semaphore->cvar->lock); |
michael@0 | 737 | } /* PR_PostSem */ |
michael@0 | 738 | |
michael@0 | 739 | PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *semaphore) |
michael@0 | 740 | { |
michael@0 | 741 | PRStatus status = PR_SUCCESS; |
michael@0 | 742 | static PRBool unwarned = PR_TRUE; |
michael@0 | 743 | if (unwarned) unwarned = _PR_Obsolete( |
michael@0 | 744 | "PR_WaitSem", "locks & condition variables"); |
michael@0 | 745 | PR_Lock(semaphore->cvar->lock); |
michael@0 | 746 | while ((semaphore->count == 0) && (PR_SUCCESS == status)) |
michael@0 | 747 | status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 748 | if (PR_SUCCESS == status) semaphore->count -= 1; |
michael@0 | 749 | PR_Unlock(semaphore->cvar->lock); |
michael@0 | 750 | return status; |
michael@0 | 751 | } /* PR_WaitSem */ |
michael@0 | 752 | |
michael@0 | 753 | PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *semaphore) |
michael@0 | 754 | { |
michael@0 | 755 | static PRBool unwarned = PR_TRUE; |
michael@0 | 756 | if (unwarned) unwarned = _PR_Obsolete( |
michael@0 | 757 | "PR_DestroySem", "locks & condition variables"); |
michael@0 | 758 | PR_DestroyLock(semaphore->cvar->lock); |
michael@0 | 759 | PR_DestroyCondVar(semaphore->cvar); |
michael@0 | 760 | PR_Free(semaphore); |
michael@0 | 761 | } /* PR_DestroySem */ |
michael@0 | 762 | |
michael@0 | 763 | PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) |
michael@0 | 764 | { |
michael@0 | 765 | PRSemaphore *semaphore; |
michael@0 | 766 | static PRBool unwarned = PR_TRUE; |
michael@0 | 767 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 768 | |
michael@0 | 769 | if (unwarned) unwarned = _PR_Obsolete( |
michael@0 | 770 | "PR_NewSem", "locks & condition variables"); |
michael@0 | 771 | |
michael@0 | 772 | semaphore = PR_NEWZAP(PRSemaphore); |
michael@0 | 773 | if (NULL != semaphore) |
michael@0 | 774 | { |
michael@0 | 775 | PRLock *lock = PR_NewLock(); |
michael@0 | 776 | if (NULL != lock) |
michael@0 | 777 | { |
michael@0 | 778 | semaphore->cvar = PR_NewCondVar(lock); |
michael@0 | 779 | if (NULL != semaphore->cvar) |
michael@0 | 780 | { |
michael@0 | 781 | semaphore->count = value; |
michael@0 | 782 | return semaphore; |
michael@0 | 783 | } |
michael@0 | 784 | PR_DestroyLock(lock); |
michael@0 | 785 | } |
michael@0 | 786 | PR_Free(semaphore); |
michael@0 | 787 | } |
michael@0 | 788 | return NULL; |
michael@0 | 789 | } |
michael@0 | 790 | |
michael@0 | 791 | /* |
michael@0 | 792 | * Define the interprocess named semaphore functions. |
michael@0 | 793 | * There are three implementations: |
michael@0 | 794 | * 1. POSIX semaphore based; |
michael@0 | 795 | * 2. System V semaphore based; |
michael@0 | 796 | * 3. unsupported (fails with PR_NOT_IMPLEMENTED_ERROR). |
michael@0 | 797 | */ |
michael@0 | 798 | |
michael@0 | 799 | #ifdef _PR_HAVE_POSIX_SEMAPHORES |
michael@0 | 800 | #include <fcntl.h> |
michael@0 | 801 | |
michael@0 | 802 | PR_IMPLEMENT(PRSem *) PR_OpenSemaphore( |
michael@0 | 803 | const char *name, |
michael@0 | 804 | PRIntn flags, |
michael@0 | 805 | PRIntn mode, |
michael@0 | 806 | PRUintn value) |
michael@0 | 807 | { |
michael@0 | 808 | PRSem *sem; |
michael@0 | 809 | char osname[PR_IPC_NAME_SIZE]; |
michael@0 | 810 | |
michael@0 | 811 | if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) |
michael@0 | 812 | == PR_FAILURE) |
michael@0 | 813 | { |
michael@0 | 814 | return NULL; |
michael@0 | 815 | } |
michael@0 | 816 | |
michael@0 | 817 | sem = PR_NEW(PRSem); |
michael@0 | 818 | if (NULL == sem) |
michael@0 | 819 | { |
michael@0 | 820 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
michael@0 | 821 | return NULL; |
michael@0 | 822 | } |
michael@0 | 823 | |
michael@0 | 824 | if (flags & PR_SEM_CREATE) |
michael@0 | 825 | { |
michael@0 | 826 | int oflag = O_CREAT; |
michael@0 | 827 | |
michael@0 | 828 | if (flags & PR_SEM_EXCL) oflag |= O_EXCL; |
michael@0 | 829 | sem->sem = sem_open(osname, oflag, mode, value); |
michael@0 | 830 | } |
michael@0 | 831 | else |
michael@0 | 832 | { |
michael@0 | 833 | #ifdef HPUX |
michael@0 | 834 | /* Pass 0 as the mode and value arguments to work around a bug. */ |
michael@0 | 835 | sem->sem = sem_open(osname, 0, 0, 0); |
michael@0 | 836 | #else |
michael@0 | 837 | sem->sem = sem_open(osname, 0); |
michael@0 | 838 | #endif |
michael@0 | 839 | } |
michael@0 | 840 | if ((sem_t *) -1 == sem->sem) |
michael@0 | 841 | { |
michael@0 | 842 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 843 | PR_Free(sem); |
michael@0 | 844 | return NULL; |
michael@0 | 845 | } |
michael@0 | 846 | return sem; |
michael@0 | 847 | } |
michael@0 | 848 | |
michael@0 | 849 | PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem) |
michael@0 | 850 | { |
michael@0 | 851 | int rv; |
michael@0 | 852 | rv = sem_wait(sem->sem); |
michael@0 | 853 | if (0 != rv) |
michael@0 | 854 | { |
michael@0 | 855 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 856 | return PR_FAILURE; |
michael@0 | 857 | } |
michael@0 | 858 | return PR_SUCCESS; |
michael@0 | 859 | } |
michael@0 | 860 | |
michael@0 | 861 | PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem) |
michael@0 | 862 | { |
michael@0 | 863 | int rv; |
michael@0 | 864 | rv = sem_post(sem->sem); |
michael@0 | 865 | if (0 != rv) |
michael@0 | 866 | { |
michael@0 | 867 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 868 | return PR_FAILURE; |
michael@0 | 869 | } |
michael@0 | 870 | return PR_SUCCESS; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem) |
michael@0 | 874 | { |
michael@0 | 875 | int rv; |
michael@0 | 876 | rv = sem_close(sem->sem); |
michael@0 | 877 | if (0 != rv) |
michael@0 | 878 | { |
michael@0 | 879 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 880 | return PR_FAILURE; |
michael@0 | 881 | } |
michael@0 | 882 | PR_Free(sem); |
michael@0 | 883 | return PR_SUCCESS; |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name) |
michael@0 | 887 | { |
michael@0 | 888 | int rv; |
michael@0 | 889 | char osname[PR_IPC_NAME_SIZE]; |
michael@0 | 890 | |
michael@0 | 891 | if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) |
michael@0 | 892 | == PR_FAILURE) |
michael@0 | 893 | { |
michael@0 | 894 | return PR_FAILURE; |
michael@0 | 895 | } |
michael@0 | 896 | rv = sem_unlink(osname); |
michael@0 | 897 | if (0 != rv) |
michael@0 | 898 | { |
michael@0 | 899 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 900 | return PR_FAILURE; |
michael@0 | 901 | } |
michael@0 | 902 | return PR_SUCCESS; |
michael@0 | 903 | } |
michael@0 | 904 | |
michael@0 | 905 | #elif defined(_PR_HAVE_SYSV_SEMAPHORES) |
michael@0 | 906 | |
michael@0 | 907 | #include <fcntl.h> |
michael@0 | 908 | #include <sys/sem.h> |
michael@0 | 909 | |
michael@0 | 910 | /* |
michael@0 | 911 | * From the semctl(2) man page in glibc 2.0 |
michael@0 | 912 | */ |
michael@0 | 913 | #if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) \ |
michael@0 | 914 | || defined(FREEBSD) || defined(OPENBSD) || defined(BSDI) \ |
michael@0 | 915 | || defined(DARWIN) || defined(SYMBIAN) |
michael@0 | 916 | /* union semun is defined by including <sys/sem.h> */ |
michael@0 | 917 | #else |
michael@0 | 918 | /* according to X/OPEN we have to define it ourselves */ |
michael@0 | 919 | union semun { |
michael@0 | 920 | int val; |
michael@0 | 921 | struct semid_ds *buf; |
michael@0 | 922 | unsigned short *array; |
michael@0 | 923 | }; |
michael@0 | 924 | #endif |
michael@0 | 925 | |
michael@0 | 926 | /* |
michael@0 | 927 | * 'a' (97) is the final closing price of NSCP stock. |
michael@0 | 928 | */ |
michael@0 | 929 | #define NSPR_IPC_KEY_ID 'a' /* the id argument for ftok() */ |
michael@0 | 930 | |
michael@0 | 931 | #define NSPR_SEM_MODE 0666 |
michael@0 | 932 | |
michael@0 | 933 | PR_IMPLEMENT(PRSem *) PR_OpenSemaphore( |
michael@0 | 934 | const char *name, |
michael@0 | 935 | PRIntn flags, |
michael@0 | 936 | PRIntn mode, |
michael@0 | 937 | PRUintn value) |
michael@0 | 938 | { |
michael@0 | 939 | PRSem *sem; |
michael@0 | 940 | key_t key; |
michael@0 | 941 | union semun arg; |
michael@0 | 942 | struct sembuf sop; |
michael@0 | 943 | struct semid_ds seminfo; |
michael@0 | 944 | #define MAX_TRIES 60 |
michael@0 | 945 | PRIntn i; |
michael@0 | 946 | char osname[PR_IPC_NAME_SIZE]; |
michael@0 | 947 | |
michael@0 | 948 | if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) |
michael@0 | 949 | == PR_FAILURE) |
michael@0 | 950 | { |
michael@0 | 951 | return NULL; |
michael@0 | 952 | } |
michael@0 | 953 | |
michael@0 | 954 | /* Make sure the file exists before calling ftok. */ |
michael@0 | 955 | if (flags & PR_SEM_CREATE) |
michael@0 | 956 | { |
michael@0 | 957 | int osfd = open(osname, O_RDWR|O_CREAT, mode); |
michael@0 | 958 | if (-1 == osfd) |
michael@0 | 959 | { |
michael@0 | 960 | _PR_MD_MAP_OPEN_ERROR(errno); |
michael@0 | 961 | return NULL; |
michael@0 | 962 | } |
michael@0 | 963 | if (close(osfd) == -1) |
michael@0 | 964 | { |
michael@0 | 965 | _PR_MD_MAP_CLOSE_ERROR(errno); |
michael@0 | 966 | return NULL; |
michael@0 | 967 | } |
michael@0 | 968 | } |
michael@0 | 969 | key = ftok(osname, NSPR_IPC_KEY_ID); |
michael@0 | 970 | if ((key_t)-1 == key) |
michael@0 | 971 | { |
michael@0 | 972 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 973 | return NULL; |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | sem = PR_NEW(PRSem); |
michael@0 | 977 | if (NULL == sem) |
michael@0 | 978 | { |
michael@0 | 979 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
michael@0 | 980 | return NULL; |
michael@0 | 981 | } |
michael@0 | 982 | |
michael@0 | 983 | if (flags & PR_SEM_CREATE) |
michael@0 | 984 | { |
michael@0 | 985 | sem->semid = semget(key, 1, mode|IPC_CREAT|IPC_EXCL); |
michael@0 | 986 | if (sem->semid >= 0) |
michael@0 | 987 | { |
michael@0 | 988 | /* creator of a semaphore is responsible for initializing it */ |
michael@0 | 989 | arg.val = 0; |
michael@0 | 990 | if (semctl(sem->semid, 0, SETVAL, arg) == -1) |
michael@0 | 991 | { |
michael@0 | 992 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 993 | PR_Free(sem); |
michael@0 | 994 | return NULL; |
michael@0 | 995 | } |
michael@0 | 996 | /* call semop to set sem_otime to nonzero */ |
michael@0 | 997 | sop.sem_num = 0; |
michael@0 | 998 | sop.sem_op = value; |
michael@0 | 999 | sop.sem_flg = 0; |
michael@0 | 1000 | if (semop(sem->semid, &sop, 1) == -1) |
michael@0 | 1001 | { |
michael@0 | 1002 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1003 | PR_Free(sem); |
michael@0 | 1004 | return NULL; |
michael@0 | 1005 | } |
michael@0 | 1006 | return sem; |
michael@0 | 1007 | } |
michael@0 | 1008 | |
michael@0 | 1009 | if (errno != EEXIST || flags & PR_SEM_EXCL) |
michael@0 | 1010 | { |
michael@0 | 1011 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1012 | PR_Free(sem); |
michael@0 | 1013 | return NULL; |
michael@0 | 1014 | } |
michael@0 | 1015 | } |
michael@0 | 1016 | |
michael@0 | 1017 | sem->semid = semget(key, 1, NSPR_SEM_MODE); |
michael@0 | 1018 | if (sem->semid == -1) |
michael@0 | 1019 | { |
michael@0 | 1020 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1021 | PR_Free(sem); |
michael@0 | 1022 | return NULL; |
michael@0 | 1023 | } |
michael@0 | 1024 | for (i = 0; i < MAX_TRIES; i++) |
michael@0 | 1025 | { |
michael@0 | 1026 | arg.buf = &seminfo; |
michael@0 | 1027 | semctl(sem->semid, 0, IPC_STAT, arg); |
michael@0 | 1028 | if (seminfo.sem_otime != 0) break; |
michael@0 | 1029 | sleep(1); |
michael@0 | 1030 | } |
michael@0 | 1031 | if (i == MAX_TRIES) |
michael@0 | 1032 | { |
michael@0 | 1033 | PR_SetError(PR_IO_TIMEOUT_ERROR, 0); |
michael@0 | 1034 | PR_Free(sem); |
michael@0 | 1035 | return NULL; |
michael@0 | 1036 | } |
michael@0 | 1037 | return sem; |
michael@0 | 1038 | } |
michael@0 | 1039 | |
michael@0 | 1040 | PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem) |
michael@0 | 1041 | { |
michael@0 | 1042 | struct sembuf sop; |
michael@0 | 1043 | |
michael@0 | 1044 | sop.sem_num = 0; |
michael@0 | 1045 | sop.sem_op = -1; |
michael@0 | 1046 | sop.sem_flg = 0; |
michael@0 | 1047 | if (semop(sem->semid, &sop, 1) == -1) |
michael@0 | 1048 | { |
michael@0 | 1049 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1050 | return PR_FAILURE; |
michael@0 | 1051 | } |
michael@0 | 1052 | return PR_SUCCESS; |
michael@0 | 1053 | } |
michael@0 | 1054 | |
michael@0 | 1055 | PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem) |
michael@0 | 1056 | { |
michael@0 | 1057 | struct sembuf sop; |
michael@0 | 1058 | |
michael@0 | 1059 | sop.sem_num = 0; |
michael@0 | 1060 | sop.sem_op = 1; |
michael@0 | 1061 | sop.sem_flg = 0; |
michael@0 | 1062 | if (semop(sem->semid, &sop, 1) == -1) |
michael@0 | 1063 | { |
michael@0 | 1064 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1065 | return PR_FAILURE; |
michael@0 | 1066 | } |
michael@0 | 1067 | return PR_SUCCESS; |
michael@0 | 1068 | } |
michael@0 | 1069 | |
michael@0 | 1070 | PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem) |
michael@0 | 1071 | { |
michael@0 | 1072 | PR_Free(sem); |
michael@0 | 1073 | return PR_SUCCESS; |
michael@0 | 1074 | } |
michael@0 | 1075 | |
michael@0 | 1076 | PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name) |
michael@0 | 1077 | { |
michael@0 | 1078 | key_t key; |
michael@0 | 1079 | int semid; |
michael@0 | 1080 | /* On some systems (e.g., glibc 2.0) semctl requires a fourth argument */ |
michael@0 | 1081 | union semun unused; |
michael@0 | 1082 | char osname[PR_IPC_NAME_SIZE]; |
michael@0 | 1083 | |
michael@0 | 1084 | if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) |
michael@0 | 1085 | == PR_FAILURE) |
michael@0 | 1086 | { |
michael@0 | 1087 | return PR_FAILURE; |
michael@0 | 1088 | } |
michael@0 | 1089 | key = ftok(osname, NSPR_IPC_KEY_ID); |
michael@0 | 1090 | if ((key_t) -1 == key) |
michael@0 | 1091 | { |
michael@0 | 1092 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1093 | return PR_FAILURE; |
michael@0 | 1094 | } |
michael@0 | 1095 | if (unlink(osname) == -1) |
michael@0 | 1096 | { |
michael@0 | 1097 | _PR_MD_MAP_UNLINK_ERROR(errno); |
michael@0 | 1098 | return PR_FAILURE; |
michael@0 | 1099 | } |
michael@0 | 1100 | semid = semget(key, 1, NSPR_SEM_MODE); |
michael@0 | 1101 | if (-1 == semid) |
michael@0 | 1102 | { |
michael@0 | 1103 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1104 | return PR_FAILURE; |
michael@0 | 1105 | } |
michael@0 | 1106 | unused.val = 0; |
michael@0 | 1107 | if (semctl(semid, 0, IPC_RMID, unused) == -1) |
michael@0 | 1108 | { |
michael@0 | 1109 | _PR_MD_MAP_DEFAULT_ERROR(errno); |
michael@0 | 1110 | return PR_FAILURE; |
michael@0 | 1111 | } |
michael@0 | 1112 | return PR_SUCCESS; |
michael@0 | 1113 | } |
michael@0 | 1114 | |
michael@0 | 1115 | #else /* neither POSIX nor System V semaphores are available */ |
michael@0 | 1116 | |
michael@0 | 1117 | PR_IMPLEMENT(PRSem *) PR_OpenSemaphore( |
michael@0 | 1118 | const char *name, |
michael@0 | 1119 | PRIntn flags, |
michael@0 | 1120 | PRIntn mode, |
michael@0 | 1121 | PRUintn value) |
michael@0 | 1122 | { |
michael@0 | 1123 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
michael@0 | 1124 | return NULL; |
michael@0 | 1125 | } |
michael@0 | 1126 | |
michael@0 | 1127 | PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem) |
michael@0 | 1128 | { |
michael@0 | 1129 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
michael@0 | 1130 | return PR_FAILURE; |
michael@0 | 1131 | } |
michael@0 | 1132 | |
michael@0 | 1133 | PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem) |
michael@0 | 1134 | { |
michael@0 | 1135 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
michael@0 | 1136 | return PR_FAILURE; |
michael@0 | 1137 | } |
michael@0 | 1138 | |
michael@0 | 1139 | PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem) |
michael@0 | 1140 | { |
michael@0 | 1141 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
michael@0 | 1142 | return PR_FAILURE; |
michael@0 | 1143 | } |
michael@0 | 1144 | |
michael@0 | 1145 | PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name) |
michael@0 | 1146 | { |
michael@0 | 1147 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
michael@0 | 1148 | return PR_FAILURE; |
michael@0 | 1149 | } |
michael@0 | 1150 | |
michael@0 | 1151 | #endif /* end of interprocess named semaphore functions */ |
michael@0 | 1152 | |
michael@0 | 1153 | /**************************************************************/ |
michael@0 | 1154 | /**************************************************************/ |
michael@0 | 1155 | /******************ROUTINES FOR DCE EMULATION******************/ |
michael@0 | 1156 | /**************************************************************/ |
michael@0 | 1157 | /**************************************************************/ |
michael@0 | 1158 | |
michael@0 | 1159 | #include "prpdce.h" |
michael@0 | 1160 | |
michael@0 | 1161 | PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) |
michael@0 | 1162 | { |
michael@0 | 1163 | PRIntn rv = pthread_mutex_trylock(&lock->mutex); |
michael@0 | 1164 | if (rv == PT_TRYLOCK_SUCCESS) |
michael@0 | 1165 | { |
michael@0 | 1166 | PR_ASSERT(PR_FALSE == lock->locked); |
michael@0 | 1167 | lock->locked = PR_TRUE; |
michael@0 | 1168 | lock->owner = pthread_self(); |
michael@0 | 1169 | } |
michael@0 | 1170 | /* XXX set error code? */ |
michael@0 | 1171 | return (PT_TRYLOCK_SUCCESS == rv) ? PR_SUCCESS : PR_FAILURE; |
michael@0 | 1172 | } /* PRP_TryLock */ |
michael@0 | 1173 | |
michael@0 | 1174 | PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void) |
michael@0 | 1175 | { |
michael@0 | 1176 | PRCondVar *cv; |
michael@0 | 1177 | |
michael@0 | 1178 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 1179 | |
michael@0 | 1180 | cv = PR_NEW(PRCondVar); |
michael@0 | 1181 | if (cv != NULL) |
michael@0 | 1182 | { |
michael@0 | 1183 | int rv; |
michael@0 | 1184 | rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); |
michael@0 | 1185 | PR_ASSERT(0 == rv); |
michael@0 | 1186 | cv->lock = _PR_NAKED_CV_LOCK; |
michael@0 | 1187 | } |
michael@0 | 1188 | return cv; |
michael@0 | 1189 | } /* PRP_NewNakedCondVar */ |
michael@0 | 1190 | |
michael@0 | 1191 | PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar) |
michael@0 | 1192 | { |
michael@0 | 1193 | int rv; |
michael@0 | 1194 | rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv); |
michael@0 | 1195 | #if defined(DEBUG) |
michael@0 | 1196 | memset(cvar, 0xaf, sizeof(PRCondVar)); |
michael@0 | 1197 | #endif |
michael@0 | 1198 | PR_Free(cvar); |
michael@0 | 1199 | } /* PRP_DestroyNakedCondVar */ |
michael@0 | 1200 | |
michael@0 | 1201 | PR_IMPLEMENT(PRStatus) PRP_NakedWait( |
michael@0 | 1202 | PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout) |
michael@0 | 1203 | { |
michael@0 | 1204 | PRIntn rv; |
michael@0 | 1205 | PR_ASSERT(cvar != NULL); |
michael@0 | 1206 | /* XXX do we really want to assert this in a naked wait? */ |
michael@0 | 1207 | PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex)); |
michael@0 | 1208 | if (timeout == PR_INTERVAL_NO_TIMEOUT) |
michael@0 | 1209 | rv = pthread_cond_wait(&cvar->cv, &ml->mutex); |
michael@0 | 1210 | else |
michael@0 | 1211 | rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout); |
michael@0 | 1212 | if (rv != 0) |
michael@0 | 1213 | { |
michael@0 | 1214 | _PR_MD_MAP_DEFAULT_ERROR(rv); |
michael@0 | 1215 | return PR_FAILURE; |
michael@0 | 1216 | } |
michael@0 | 1217 | return PR_SUCCESS; |
michael@0 | 1218 | } /* PRP_NakedWait */ |
michael@0 | 1219 | |
michael@0 | 1220 | PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar) |
michael@0 | 1221 | { |
michael@0 | 1222 | int rv; |
michael@0 | 1223 | PR_ASSERT(cvar != NULL); |
michael@0 | 1224 | rv = pthread_cond_signal(&cvar->cv); |
michael@0 | 1225 | PR_ASSERT(0 == rv); |
michael@0 | 1226 | return PR_SUCCESS; |
michael@0 | 1227 | } /* PRP_NakedNotify */ |
michael@0 | 1228 | |
michael@0 | 1229 | PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar) |
michael@0 | 1230 | { |
michael@0 | 1231 | int rv; |
michael@0 | 1232 | PR_ASSERT(cvar != NULL); |
michael@0 | 1233 | rv = pthread_cond_broadcast(&cvar->cv); |
michael@0 | 1234 | PR_ASSERT(0 == rv); |
michael@0 | 1235 | return PR_SUCCESS; |
michael@0 | 1236 | } /* PRP_NakedBroadcast */ |
michael@0 | 1237 | |
michael@0 | 1238 | #endif /* defined(_PR_PTHREADS) */ |
michael@0 | 1239 | |
michael@0 | 1240 | /* ptsynch.c */ |