nspr: import 3.0 RC1 cutoff from CVS
[mozilla-nspr.git] / nsprpub / pr / src / pthreads / ptsynch.c
blobbd0688c2c90c8a307868f59c4201433ee2cc5ca2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 ** File: ptsynch.c
40 ** Descritpion: Implemenation for thread synchronization using pthreads
41 ** Exports: prlock.h, prcvar.h, prmon.h, prcmon.h
44 #if defined(_PR_PTHREADS)
46 #include "primpl.h"
47 #include "obsolete/prsem.h"
49 #include <string.h>
50 #include <pthread.h>
51 #include <sys/time.h>
53 static pthread_mutexattr_t _pt_mattr;
54 static pthread_condattr_t _pt_cvar_attr;
56 #if defined(DEBUG)
57 extern PTDebug pt_debug; /* this is shared between several modules */
59 #if defined(_PR_DCETHREADS)
60 static pthread_t pt_zero_tid; /* a null pthread_t (pthread_t is a struct
61 * in DCE threads) to compare with */
62 #endif /* defined(_PR_DCETHREADS) */
63 #endif /* defined(DEBUG) */
65 #if defined(FREEBSD)
67 * On older versions of FreeBSD, pthread_mutex_trylock returns EDEADLK.
68 * Newer versions return EBUSY. We still need to support both.
70 static int
71 pt_pthread_mutex_is_locked(pthread_mutex_t *m)
73 int rv = pthread_mutex_trylock(m);
74 return (EBUSY == rv || EDEADLK == rv);
76 #endif
78 /**************************************************************/
79 /**************************************************************/
80 /*****************************LOCKS****************************/
81 /**************************************************************/
82 /**************************************************************/
84 void _PR_InitLocks(void)
86 int rv;
87 rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr);
88 PR_ASSERT(0 == rv);
90 #ifdef LINUX
91 #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)
92 rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
93 PR_ASSERT(0 == rv);
94 #endif
95 #endif
97 rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);
98 PR_ASSERT(0 == rv);
101 static void pt_PostNotifies(PRLock *lock, PRBool unlock)
103 PRIntn index, rv;
104 _PT_Notified post;
105 _PT_Notified *notified, *prev = NULL;
107 * Time to actually notify any conditions that were affected
108 * while the lock was held. Get a copy of the list that's in
109 * the lock structure and then zero the original. If it's
110 * linked to other such structures, we own that storage.
112 post = lock->notified; /* a safe copy; we own the lock */
114 #if defined(DEBUG)
115 memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */
116 #else
117 lock->notified.length = 0; /* these are really sufficient */
118 lock->notified.link = NULL;
119 #endif
121 /* should (may) we release lock before notifying? */
122 if (unlock)
124 rv = pthread_mutex_unlock(&lock->mutex);
125 PR_ASSERT(0 == rv);
128 notified = &post; /* this is where we start */
131 for (index = 0; index < notified->length; ++index)
133 PRCondVar *cv = notified->cv[index].cv;
134 PR_ASSERT(NULL != cv);
135 PR_ASSERT(0 != notified->cv[index].times);
136 if (-1 == notified->cv[index].times)
138 rv = pthread_cond_broadcast(&cv->cv);
139 PR_ASSERT(0 == rv);
141 else
143 while (notified->cv[index].times-- > 0)
145 rv = pthread_cond_signal(&cv->cv);
146 PR_ASSERT(0 == rv);
149 #if defined(DEBUG)
150 pt_debug.cvars_notified += 1;
151 if (0 > PR_AtomicDecrement(&cv->notify_pending))
153 pt_debug.delayed_cv_deletes += 1;
154 PR_DestroyCondVar(cv);
156 #else /* defined(DEBUG) */
157 if (0 > PR_AtomicDecrement(&cv->notify_pending))
158 PR_DestroyCondVar(cv);
159 #endif /* defined(DEBUG) */
161 prev = notified;
162 notified = notified->link;
163 if (&post != prev) PR_DELETE(prev);
164 } while (NULL != notified);
165 } /* pt_PostNotifies */
167 PR_IMPLEMENT(PRLock*) PR_NewLock(void)
169 PRIntn rv;
170 PRLock *lock;
172 if (!_pr_initialized) _PR_ImplicitInitialization();
174 lock = PR_NEWZAP(PRLock);
175 if (lock != NULL)
177 rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr);
178 PR_ASSERT(0 == rv);
180 #if defined(DEBUG)
181 pt_debug.locks_created += 1;
182 #endif
183 return lock;
184 } /* PR_NewLock */
186 PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
188 PRIntn rv;
189 PR_ASSERT(NULL != lock);
190 PR_ASSERT(PR_FALSE == lock->locked);
191 PR_ASSERT(0 == lock->notified.length);
192 PR_ASSERT(NULL == lock->notified.link);
193 rv = pthread_mutex_destroy(&lock->mutex);
194 PR_ASSERT(0 == rv);
195 #if defined(DEBUG)
196 memset(lock, 0xaf, sizeof(PRLock));
197 pt_debug.locks_destroyed += 1;
198 #endif
199 PR_DELETE(lock);
200 } /* PR_DestroyLock */
202 PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
204 PRIntn rv;
205 PR_ASSERT(lock != NULL);
206 rv = pthread_mutex_lock(&lock->mutex);
207 PR_ASSERT(0 == rv);
208 PR_ASSERT(0 == lock->notified.length);
209 PR_ASSERT(NULL == lock->notified.link);
210 PR_ASSERT(PR_FALSE == lock->locked);
211 lock->locked = PR_TRUE;
212 lock->owner = pthread_self();
213 #if defined(DEBUG)
214 pt_debug.locks_acquired += 1;
215 #endif
216 } /* PR_Lock */
218 PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
220 PRIntn rv;
222 PR_ASSERT(lock != NULL);
223 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex));
224 PR_ASSERT(PR_TRUE == lock->locked);
225 PR_ASSERT(pthread_equal(lock->owner, pthread_self()));
227 if (!lock->locked || !pthread_equal(lock->owner, pthread_self()))
228 return PR_FAILURE;
230 lock->locked = PR_FALSE;
231 if (0 == lock->notified.length) /* shortcut */
233 rv = pthread_mutex_unlock(&lock->mutex);
234 PR_ASSERT(0 == rv);
236 else pt_PostNotifies(lock, PR_TRUE);
238 #if defined(DEBUG)
239 pt_debug.locks_released += 1;
240 #endif
241 return PR_SUCCESS;
242 } /* PR_Unlock */
245 /**************************************************************/
246 /**************************************************************/
247 /***************************CONDITIONS*************************/
248 /**************************************************************/
249 /**************************************************************/
253 * This code is used to compute the absolute time for the wakeup.
254 * It's moderately ugly, so it's defined here and called in a
255 * couple of places.
257 #define PT_NANOPERMICRO 1000UL
258 #define PT_BILLION 1000000000UL
260 static PRIntn pt_TimedWait(
261 pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout)
263 int rv;
264 struct timeval now;
265 struct timespec tmo;
266 PRUint32 ticks = PR_TicksPerSecond();
268 tmo.tv_sec = (PRInt32)(timeout / ticks);
269 tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks));
270 tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec);
272 /* pthreads wants this in absolute time, off we go ... */
273 (void)GETTIMEOFDAY(&now);
274 /* that one's usecs, this one's nsecs - grrrr! */
275 tmo.tv_sec += now.tv_sec;
276 tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);
277 tmo.tv_sec += tmo.tv_nsec / PT_BILLION;
278 tmo.tv_nsec %= PT_BILLION;
280 rv = pthread_cond_timedwait(cv, ml, &tmo);
282 /* NSPR doesn't report timeouts */
283 #ifdef _PR_DCETHREADS
284 if (rv == -1) return (errno == EAGAIN) ? 0 : errno;
285 else return rv;
286 #else
287 return (rv == ETIMEDOUT) ? 0 : rv;
288 #endif
289 } /* pt_TimedWait */
293 * Notifies just get posted to the protecting mutex. The
294 * actual notification is done when the lock is released so that
295 * MP systems don't contend for a lock that they can't have.
297 static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast)
299 PRIntn index = 0;
300 _PT_Notified *notified = &cvar->lock->notified;
302 PR_ASSERT(PR_TRUE == cvar->lock->locked);
303 PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
304 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
306 while (1)
308 for (index = 0; index < notified->length; ++index)
310 if (notified->cv[index].cv == cvar)
312 if (broadcast)
313 notified->cv[index].times = -1;
314 else if (-1 != notified->cv[index].times)
315 notified->cv[index].times += 1;
316 goto finished; /* we're finished */
319 /* if not full, enter new CV in this array */
320 if (notified->length < PT_CV_NOTIFIED_LENGTH) break;
322 /* if there's no link, create an empty array and link it */
323 if (NULL == notified->link)
324 notified->link = PR_NEWZAP(_PT_Notified);
325 notified = notified->link;
328 /* A brand new entry in the array */
329 (void)PR_AtomicIncrement(&cvar->notify_pending);
330 notified->cv[index].times = (broadcast) ? -1 : 1;
331 notified->cv[index].cv = cvar;
332 notified->length += 1;
334 finished:
335 PR_ASSERT(PR_TRUE == cvar->lock->locked);
336 PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
337 } /* pt_PostNotifyToCvar */
339 PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
341 PRCondVar *cv = PR_NEW(PRCondVar);
342 PR_ASSERT(lock != NULL);
343 if (cv != NULL)
345 int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
346 PR_ASSERT(0 == rv);
347 cv->lock = lock;
348 cv->notify_pending = 0;
349 #if defined(DEBUG)
350 pt_debug.cvars_created += 1;
351 #endif
353 return cv;
354 } /* PR_NewCondVar */
356 PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
358 if (0 > PR_AtomicDecrement(&cvar->notify_pending))
360 PRIntn rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
361 #if defined(DEBUG)
362 memset(cvar, 0xaf, sizeof(PRCondVar));
363 pt_debug.cvars_destroyed += 1;
364 #endif
365 PR_DELETE(cvar);
367 } /* PR_DestroyCondVar */
369 PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
371 PRIntn rv;
372 PRThread *thred = PR_GetCurrentThread();
374 PR_ASSERT(cvar != NULL);
375 /* We'd better be locked */
376 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
377 PR_ASSERT(PR_TRUE == cvar->lock->locked);
378 /* and it better be by us */
379 PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
381 if (_PT_THREAD_INTERRUPTED(thred)) goto aborted;
384 * The thread waiting is used for PR_Interrupt
386 thred->waiting = cvar; /* this is where we're waiting */
389 * If we have pending notifies, post them now.
391 * This is not optimal. We're going to post these notifies
392 * while we're holding the lock. That means on MP systems
393 * that they are going to collide for the lock that we will
394 * hold until we actually wait.
396 if (0 != cvar->lock->notified.length)
397 pt_PostNotifies(cvar->lock, PR_FALSE);
400 * We're surrendering the lock, so clear out the locked field.
402 cvar->lock->locked = PR_FALSE;
404 if (timeout == PR_INTERVAL_NO_TIMEOUT)
405 rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex);
406 else
407 rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout);
409 /* We just got the lock back - this better be empty */
410 PR_ASSERT(PR_FALSE == cvar->lock->locked);
411 cvar->lock->locked = PR_TRUE;
412 cvar->lock->owner = pthread_self();
414 PR_ASSERT(0 == cvar->lock->notified.length);
415 thred->waiting = NULL; /* and now we're not */
416 if (_PT_THREAD_INTERRUPTED(thred)) goto aborted;
417 if (rv != 0)
419 _PR_MD_MAP_DEFAULT_ERROR(rv);
420 return PR_FAILURE;
422 return PR_SUCCESS;
424 aborted:
425 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
426 thred->state &= ~PT_THREAD_ABORTED;
427 return PR_FAILURE;
428 } /* PR_WaitCondVar */
430 PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
432 PR_ASSERT(cvar != NULL);
433 pt_PostNotifyToCvar(cvar, PR_FALSE);
434 return PR_SUCCESS;
435 } /* PR_NotifyCondVar */
437 PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar)
439 PR_ASSERT(cvar != NULL);
440 pt_PostNotifyToCvar(cvar, PR_TRUE);
441 return PR_SUCCESS;
442 } /* PR_NotifyAllCondVar */
444 /**************************************************************/
445 /**************************************************************/
446 /***************************MONITORS***************************/
447 /**************************************************************/
448 /**************************************************************/
450 PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void)
452 PRMonitor *mon;
453 PRCondVar *cvar;
455 if (!_pr_initialized) _PR_ImplicitInitialization();
457 cvar = PR_NEWZAP(PRCondVar);
458 if (NULL == cvar)
460 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
461 return NULL;
463 mon = PR_NEWZAP(PRMonitor);
464 if (mon != NULL)
466 int rv;
467 rv = _PT_PTHREAD_MUTEX_INIT(mon->lock.mutex, _pt_mattr);
468 PR_ASSERT(0 == rv);
470 _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
472 mon->cvar = cvar;
473 rv = _PT_PTHREAD_COND_INIT(mon->cvar->cv, _pt_cvar_attr);
474 PR_ASSERT(0 == rv);
475 mon->entryCount = 0;
476 mon->cvar->lock = &mon->lock;
477 if (0 != rv)
479 PR_DELETE(mon);
480 PR_DELETE(cvar);
481 mon = NULL;
484 return mon;
485 } /* PR_NewMonitor */
487 PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
489 PRMonitor* mon = PR_NewMonitor();
490 if (mon)
491 mon->name = name;
492 return mon;
495 PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
497 int rv;
498 PR_ASSERT(mon != NULL);
499 PR_DestroyCondVar(mon->cvar);
500 rv = pthread_mutex_destroy(&mon->lock.mutex); PR_ASSERT(0 == rv);
501 #if defined(DEBUG)
502 memset(mon, 0xaf, sizeof(PRMonitor));
503 #endif
504 PR_DELETE(mon);
505 } /* PR_DestroyMonitor */
508 /* The GC uses this; it is quite arguably a bad interface. I'm just
509 * duplicating it for now - XXXMB
511 PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon)
513 pthread_t self = pthread_self();
514 if (pthread_equal(mon->owner, self))
515 return mon->entryCount;
516 return 0;
519 PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
521 pthread_t self = pthread_self();
523 PR_ASSERT(mon != NULL);
525 * This is safe only if mon->owner (a pthread_t) can be
526 * read in one instruction. Perhaps mon->owner should be
527 * a "PRThread *"?
529 if (!pthread_equal(mon->owner, self))
531 PR_Lock(&mon->lock);
532 /* and now I have the lock */
533 PR_ASSERT(0 == mon->entryCount);
534 PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner));
535 _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner);
537 mon->entryCount += 1;
538 } /* PR_EnterMonitor */
540 PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
542 pthread_t self = pthread_self();
544 PR_ASSERT(mon != NULL);
545 /* The lock better be that - locked */
546 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
547 /* we'd better be the owner */
548 PR_ASSERT(pthread_equal(mon->owner, self));
549 if (!pthread_equal(mon->owner, self))
550 return PR_FAILURE;
552 /* if it's locked and we have it, then the entries should be > 0 */
553 PR_ASSERT(mon->entryCount > 0);
554 mon->entryCount -= 1; /* reduce by one */
555 if (mon->entryCount == 0)
557 /* and if it transitioned to zero - unlock */
558 _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); /* make the owner unknown */
559 PR_Unlock(&mon->lock);
561 return PR_SUCCESS;
562 } /* PR_ExitMonitor */
564 PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout)
566 PRStatus rv;
567 PRInt16 saved_entries;
568 pthread_t saved_owner;
570 PR_ASSERT(mon != NULL);
571 /* we'd better be locked */
572 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
573 /* and the entries better be positive */
574 PR_ASSERT(mon->entryCount > 0);
575 /* and it better be by us */
576 PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
578 /* tuck these away 'till later */
579 saved_entries = mon->entryCount;
580 mon->entryCount = 0;
581 _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner);
582 _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
584 rv = PR_WaitCondVar(mon->cvar, timeout);
586 /* reinstate the intresting information */
587 mon->entryCount = saved_entries;
588 _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner);
590 return rv;
591 } /* PR_Wait */
593 PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
595 PR_ASSERT(NULL != mon);
596 /* we'd better be locked */
597 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
598 /* and the entries better be positive */
599 PR_ASSERT(mon->entryCount > 0);
600 /* and it better be by us */
601 PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
603 pt_PostNotifyToCvar(mon->cvar, PR_FALSE);
605 return PR_SUCCESS;
606 } /* PR_Notify */
608 PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
610 PR_ASSERT(mon != NULL);
611 /* we'd better be locked */
612 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
613 /* and the entries better be positive */
614 PR_ASSERT(mon->entryCount > 0);
615 /* and it better be by us */
616 PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
618 pt_PostNotifyToCvar(mon->cvar, PR_TRUE);
620 return PR_SUCCESS;
621 } /* PR_NotifyAll */
623 /**************************************************************/
624 /**************************************************************/
625 /**************************SEMAPHORES**************************/
626 /**************************************************************/
627 /**************************************************************/
628 PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *semaphore)
630 static PRBool unwarned = PR_TRUE;
631 if (unwarned) unwarned = _PR_Obsolete(
632 "PR_PostSem", "locks & condition variables");
633 PR_Lock(semaphore->cvar->lock);
634 PR_NotifyCondVar(semaphore->cvar);
635 semaphore->count += 1;
636 PR_Unlock(semaphore->cvar->lock);
637 } /* PR_PostSem */
639 PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *semaphore)
641 PRStatus status = PR_SUCCESS;
642 static PRBool unwarned = PR_TRUE;
643 if (unwarned) unwarned = _PR_Obsolete(
644 "PR_WaitSem", "locks & condition variables");
645 PR_Lock(semaphore->cvar->lock);
646 while ((semaphore->count == 0) && (PR_SUCCESS == status))
647 status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT);
648 if (PR_SUCCESS == status) semaphore->count -= 1;
649 PR_Unlock(semaphore->cvar->lock);
650 return status;
651 } /* PR_WaitSem */
653 PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *semaphore)
655 static PRBool unwarned = PR_TRUE;
656 if (unwarned) unwarned = _PR_Obsolete(
657 "PR_DestroySem", "locks & condition variables");
658 PR_DestroyLock(semaphore->cvar->lock);
659 PR_DestroyCondVar(semaphore->cvar);
660 PR_DELETE(semaphore);
661 } /* PR_DestroySem */
663 PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value)
665 PRSemaphore *semaphore;
666 static PRBool unwarned = PR_TRUE;
667 if (!_pr_initialized) _PR_ImplicitInitialization();
669 if (unwarned) unwarned = _PR_Obsolete(
670 "PR_NewSem", "locks & condition variables");
672 semaphore = PR_NEWZAP(PRSemaphore);
673 if (NULL != semaphore)
675 PRLock *lock = PR_NewLock();
676 if (NULL != lock)
678 semaphore->cvar = PR_NewCondVar(lock);
679 if (NULL != semaphore->cvar)
681 semaphore->count = value;
682 return semaphore;
684 PR_DestroyLock(lock);
686 PR_DELETE(semaphore);
688 return NULL;
692 * Define the interprocess named semaphore functions.
693 * There are three implementations:
694 * 1. POSIX semaphore based;
695 * 2. System V semaphore based;
696 * 3. unsupported (fails with PR_NOT_IMPLEMENTED_ERROR).
699 #ifdef _PR_HAVE_POSIX_SEMAPHORES
700 #include <fcntl.h>
702 PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
703 const char *name,
704 PRIntn flags,
705 PRIntn mode,
706 PRUintn value)
708 PRSem *sem;
709 char osname[PR_IPC_NAME_SIZE];
711 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
712 == PR_FAILURE)
714 return NULL;
717 sem = PR_NEW(PRSem);
718 if (NULL == sem)
720 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
721 return NULL;
724 if (flags & PR_SEM_CREATE)
726 int oflag = O_CREAT;
728 if (flags & PR_SEM_EXCL) oflag |= O_EXCL;
729 sem->sem = sem_open(osname, oflag, mode, value);
731 else
733 #ifdef HPUX
734 /* Pass 0 as the mode and value arguments to work around a bug. */
735 sem->sem = sem_open(osname, 0, 0, 0);
736 #else
737 sem->sem = sem_open(osname, 0);
738 #endif
740 if ((sem_t *) -1 == sem->sem)
742 _PR_MD_MAP_DEFAULT_ERROR(errno);
743 PR_DELETE(sem);
744 return NULL;
746 return sem;
749 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
751 int rv;
752 rv = sem_wait(sem->sem);
753 if (0 != rv)
755 _PR_MD_MAP_DEFAULT_ERROR(errno);
756 return PR_FAILURE;
758 return PR_SUCCESS;
761 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
763 int rv;
764 rv = sem_post(sem->sem);
765 if (0 != rv)
767 _PR_MD_MAP_DEFAULT_ERROR(errno);
768 return PR_FAILURE;
770 return PR_SUCCESS;
773 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
775 int rv;
776 rv = sem_close(sem->sem);
777 if (0 != rv)
779 _PR_MD_MAP_DEFAULT_ERROR(errno);
780 return PR_FAILURE;
782 PR_DELETE(sem);
783 return PR_SUCCESS;
786 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
788 int rv;
789 char osname[PR_IPC_NAME_SIZE];
791 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
792 == PR_FAILURE)
794 return PR_FAILURE;
796 rv = sem_unlink(osname);
797 if (0 != rv)
799 _PR_MD_MAP_DEFAULT_ERROR(errno);
800 return PR_FAILURE;
802 return PR_SUCCESS;
805 #elif defined(_PR_HAVE_SYSV_SEMAPHORES)
807 #include <fcntl.h>
808 #include <sys/sem.h>
811 * From the semctl(2) man page in glibc 2.0
813 #if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) \
814 || defined(FREEBSD) || defined(OPENBSD) || defined(BSDI)
815 /* union semun is defined by including <sys/sem.h> */
816 #else
817 /* according to X/OPEN we have to define it ourselves */
818 union semun {
819 int val;
820 struct semid_ds *buf;
821 unsigned short *array;
823 #endif
826 * 'a' (97) is the final closing price of NSCP stock.
828 #define NSPR_IPC_KEY_ID 'a' /* the id argument for ftok() */
830 #define NSPR_SEM_MODE 0666
832 PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
833 const char *name,
834 PRIntn flags,
835 PRIntn mode,
836 PRUintn value)
838 PRSem *sem;
839 key_t key;
840 union semun arg;
841 struct sembuf sop;
842 struct semid_ds seminfo;
843 #define MAX_TRIES 60
844 PRIntn i;
845 char osname[PR_IPC_NAME_SIZE];
847 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
848 == PR_FAILURE)
850 return NULL;
853 /* Make sure the file exists before calling ftok. */
854 if (flags & PR_SEM_CREATE)
856 int osfd = open(osname, O_RDWR|O_CREAT, mode);
857 if (-1 == osfd)
859 _PR_MD_MAP_OPEN_ERROR(errno);
860 return NULL;
862 if (close(osfd) == -1)
864 _PR_MD_MAP_CLOSE_ERROR(errno);
865 return NULL;
868 key = ftok(osname, NSPR_IPC_KEY_ID);
869 if ((key_t)-1 == key)
871 _PR_MD_MAP_DEFAULT_ERROR(errno);
872 return NULL;
875 sem = PR_NEW(PRSem);
876 if (NULL == sem)
878 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
879 return NULL;
882 if (flags & PR_SEM_CREATE)
884 sem->semid = semget(key, 1, mode|IPC_CREAT|IPC_EXCL);
885 if (sem->semid >= 0)
887 /* creator of a semaphore is responsible for initializing it */
888 arg.val = 0;
889 if (semctl(sem->semid, 0, SETVAL, arg) == -1)
891 _PR_MD_MAP_DEFAULT_ERROR(errno);
892 PR_DELETE(sem);
893 return NULL;
895 /* call semop to set sem_otime to nonzero */
896 sop.sem_num = 0;
897 sop.sem_op = value;
898 sop.sem_flg = 0;
899 if (semop(sem->semid, &sop, 1) == -1)
901 _PR_MD_MAP_DEFAULT_ERROR(errno);
902 PR_DELETE(sem);
903 return NULL;
905 return sem;
908 if (errno != EEXIST || flags & PR_SEM_EXCL)
910 _PR_MD_MAP_DEFAULT_ERROR(errno);
911 PR_DELETE(sem);
912 return NULL;
916 sem->semid = semget(key, 1, NSPR_SEM_MODE);
917 if (sem->semid == -1)
919 _PR_MD_MAP_DEFAULT_ERROR(errno);
920 PR_DELETE(sem);
921 return NULL;
923 for (i = 0; i < MAX_TRIES; i++)
925 arg.buf = &seminfo;
926 semctl(sem->semid, 0, IPC_STAT, arg);
927 if (seminfo.sem_otime != 0) break;
928 sleep(1);
930 if (i == MAX_TRIES)
932 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
933 PR_DELETE(sem);
934 return NULL;
936 return sem;
939 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
941 struct sembuf sop;
943 sop.sem_num = 0;
944 sop.sem_op = -1;
945 sop.sem_flg = 0;
946 if (semop(sem->semid, &sop, 1) == -1)
948 _PR_MD_MAP_DEFAULT_ERROR(errno);
949 return PR_FAILURE;
951 return PR_SUCCESS;
954 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
956 struct sembuf sop;
958 sop.sem_num = 0;
959 sop.sem_op = 1;
960 sop.sem_flg = 0;
961 if (semop(sem->semid, &sop, 1) == -1)
963 _PR_MD_MAP_DEFAULT_ERROR(errno);
964 return PR_FAILURE;
966 return PR_SUCCESS;
969 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
971 PR_DELETE(sem);
972 return PR_SUCCESS;
975 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
977 key_t key;
978 int semid;
979 /* On some systems (e.g., glibc 2.0) semctl requires a fourth argument */
980 union semun unused;
981 char osname[PR_IPC_NAME_SIZE];
983 if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
984 == PR_FAILURE)
986 return PR_FAILURE;
988 key = ftok(osname, NSPR_IPC_KEY_ID);
989 if ((key_t) -1 == key)
991 _PR_MD_MAP_DEFAULT_ERROR(errno);
992 return PR_FAILURE;
994 if (unlink(osname) == -1)
996 _PR_MD_MAP_UNLINK_ERROR(errno);
997 return PR_FAILURE;
999 semid = semget(key, 1, NSPR_SEM_MODE);
1000 if (-1 == semid)
1002 _PR_MD_MAP_DEFAULT_ERROR(errno);
1003 return PR_FAILURE;
1005 unused.val = 0;
1006 if (semctl(semid, 0, IPC_RMID, unused) == -1)
1008 _PR_MD_MAP_DEFAULT_ERROR(errno);
1009 return PR_FAILURE;
1011 return PR_SUCCESS;
1014 #else /* neither POSIX nor System V semaphores are available */
1016 PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
1017 const char *name,
1018 PRIntn flags,
1019 PRIntn mode,
1020 PRUintn value)
1022 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1023 return NULL;
1026 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
1028 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1029 return PR_FAILURE;
1032 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
1034 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1035 return PR_FAILURE;
1038 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
1040 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1041 return PR_FAILURE;
1044 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
1046 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1047 return PR_FAILURE;
1050 #endif /* end of interprocess named semaphore functions */
1052 /**************************************************************/
1053 /**************************************************************/
1054 /******************ROUTINES FOR DCE EMULATION******************/
1055 /**************************************************************/
1056 /**************************************************************/
1058 #include "prpdce.h"
1060 PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
1062 PRIntn rv = pthread_mutex_trylock(&lock->mutex);
1063 if (rv == PT_TRYLOCK_SUCCESS)
1065 PR_ASSERT(PR_FALSE == lock->locked);
1066 lock->locked = PR_TRUE;
1067 lock->owner = pthread_self();
1069 /* XXX set error code? */
1070 return (PT_TRYLOCK_SUCCESS == rv) ? PR_SUCCESS : PR_FAILURE;
1071 } /* PRP_TryLock */
1073 PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void)
1075 PRCondVar *cv;
1077 if (!_pr_initialized) _PR_ImplicitInitialization();
1079 cv = PR_NEW(PRCondVar);
1080 if (cv != NULL)
1082 int rv;
1083 rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
1084 PR_ASSERT(0 == rv);
1085 cv->lock = _PR_NAKED_CV_LOCK;
1087 return cv;
1088 } /* PRP_NewNakedCondVar */
1090 PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar)
1092 int rv;
1093 rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
1094 #if defined(DEBUG)
1095 memset(cvar, 0xaf, sizeof(PRCondVar));
1096 #endif
1097 PR_DELETE(cvar);
1098 } /* PRP_DestroyNakedCondVar */
1100 PR_IMPLEMENT(PRStatus) PRP_NakedWait(
1101 PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout)
1103 PRIntn rv;
1104 PR_ASSERT(cvar != NULL);
1105 /* XXX do we really want to assert this in a naked wait? */
1106 PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex));
1107 if (timeout == PR_INTERVAL_NO_TIMEOUT)
1108 rv = pthread_cond_wait(&cvar->cv, &ml->mutex);
1109 else
1110 rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout);
1111 if (rv != 0)
1113 _PR_MD_MAP_DEFAULT_ERROR(rv);
1114 return PR_FAILURE;
1116 return PR_SUCCESS;
1117 } /* PRP_NakedWait */
1119 PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
1121 int rv;
1122 PR_ASSERT(cvar != NULL);
1123 rv = pthread_cond_signal(&cvar->cv);
1124 PR_ASSERT(0 == rv);
1125 return PR_SUCCESS;
1126 } /* PRP_NakedNotify */
1128 PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar)
1130 int rv;
1131 PR_ASSERT(cvar != NULL);
1132 rv = pthread_cond_broadcast(&cvar->cv);
1133 PR_ASSERT(0 == rv);
1134 return PR_SUCCESS;
1135 } /* PRP_NakedBroadcast */
1137 #endif /* defined(_PR_PTHREADS) */
1139 /* ptsynch.c */