Back to index

lightning-sunbird  0.9+nobinonly
ptsynch.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039 ** File:            ptsynch.c
00040 ** Descritpion:        Implemenation for thread synchronization using pthreads
00041 ** Exports:            prlock.h, prcvar.h, prmon.h, prcmon.h
00042 */
00043 
00044 #if defined(_PR_PTHREADS)
00045 
00046 #include "primpl.h"
00047 #include "obsolete/prsem.h"
00048 
00049 #include <string.h>
00050 #include <pthread.h>
00051 #include <sys/time.h>
00052 
00053 static pthread_mutexattr_t _pt_mattr;
00054 static pthread_condattr_t _pt_cvar_attr;
00055 
00056 #if defined(DEBUG)
00057 extern PTDebug pt_debug;  /* this is shared between several modules */
00058 
00059 #if defined(_PR_DCETHREADS)
00060 static pthread_t pt_zero_tid;  /* a null pthread_t (pthread_t is a struct
00061                                 * in DCE threads) to compare with */
00062 #endif  /* defined(_PR_DCETHREADS) */
00063 #endif  /* defined(DEBUG) */
00064 
00065 #if defined(FREEBSD)
00066 /*
00067  * On older versions of FreeBSD, pthread_mutex_trylock returns EDEADLK.
00068  * Newer versions return EBUSY.  We still need to support both.
00069  */
00070 static int
00071 pt_pthread_mutex_is_locked(pthread_mutex_t *m)
00072 {
00073     int rv = pthread_mutex_trylock(m);
00074     return (EBUSY == rv || EDEADLK == rv);
00075 }
00076 #endif
00077 
00078 /**************************************************************/
00079 /**************************************************************/
00080 /*****************************LOCKS****************************/
00081 /**************************************************************/
00082 /**************************************************************/
00083 
00084 void _PR_InitLocks(void)
00085 {
00086     int rv;
00087     rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr); 
00088     PR_ASSERT(0 == rv);
00089 
00090 #ifdef LINUX
00091 #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)
00092     rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
00093     PR_ASSERT(0 == rv);
00094 #endif
00095 #endif
00096 
00097     rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);
00098     PR_ASSERT(0 == rv);
00099 }
00100 
00101 static void pt_PostNotifies(PRLock *lock, PRBool unlock)
00102 {
00103     PRIntn index, rv;
00104     _PT_Notified post;
00105     _PT_Notified *notified, *prev = NULL;
00106     /*
00107      * Time to actually notify any conditions that were affected
00108      * while the lock was held. Get a copy of the list that's in
00109      * the lock structure and then zero the original. If it's
00110      * linked to other such structures, we own that storage.
00111      */
00112     post = lock->notified;  /* a safe copy; we own the lock */
00113 
00114 #if defined(DEBUG)
00115     memset(&lock->notified, 0, sizeof(_PT_Notified));  /* reset */
00116 #else
00117     lock->notified.length = 0;  /* these are really sufficient */
00118     lock->notified.link = NULL;
00119 #endif
00120 
00121     /* should (may) we release lock before notifying? */
00122     if (unlock)
00123     {
00124         rv = pthread_mutex_unlock(&lock->mutex);
00125         PR_ASSERT(0 == rv);
00126     }
00127 
00128     notified = &post;  /* this is where we start */
00129     do
00130     {
00131         for (index = 0; index < notified->length; ++index)
00132         {
00133             PRCondVar *cv = notified->cv[index].cv;
00134             PR_ASSERT(NULL != cv);
00135             PR_ASSERT(0 != notified->cv[index].times);
00136             if (-1 == notified->cv[index].times)
00137             {
00138                 rv = pthread_cond_broadcast(&cv->cv);
00139                 PR_ASSERT(0 == rv);
00140             }
00141             else
00142             {
00143                 while (notified->cv[index].times-- > 0)
00144                 {
00145                     rv = pthread_cond_signal(&cv->cv);
00146                     PR_ASSERT(0 == rv);
00147                 }
00148             }
00149 #if defined(DEBUG)
00150             pt_debug.cvars_notified += 1;
00151             if (0 > PR_AtomicDecrement(&cv->notify_pending))
00152             {
00153                 pt_debug.delayed_cv_deletes += 1;
00154                 PR_DestroyCondVar(cv);
00155             }
00156 #else  /* defined(DEBUG) */
00157             if (0 > PR_AtomicDecrement(&cv->notify_pending))
00158                 PR_DestroyCondVar(cv);
00159 #endif  /* defined(DEBUG) */
00160         }
00161         prev = notified;
00162         notified = notified->link;
00163         if (&post != prev) PR_DELETE(prev);
00164     } while (NULL != notified);
00165 }  /* pt_PostNotifies */
00166 
00167 PR_IMPLEMENT(PRLock*) PR_NewLock(void)
00168 {
00169     PRIntn rv;
00170     PRLock *lock;
00171 
00172     if (!_pr_initialized) _PR_ImplicitInitialization();
00173 
00174     lock = PR_NEWZAP(PRLock);
00175     if (lock != NULL)
00176     {
00177         rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr); 
00178         PR_ASSERT(0 == rv);
00179     }
00180 #if defined(DEBUG)
00181     pt_debug.locks_created += 1;
00182 #endif
00183     return lock;
00184 }  /* PR_NewLock */
00185 
00186 PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
00187 {
00188     PRIntn rv;
00189     PR_ASSERT(NULL != lock);
00190     PR_ASSERT(PR_FALSE == lock->locked);
00191     PR_ASSERT(0 == lock->notified.length);
00192     PR_ASSERT(NULL == lock->notified.link);
00193     rv = pthread_mutex_destroy(&lock->mutex);
00194     PR_ASSERT(0 == rv);
00195 #if defined(DEBUG)
00196     memset(lock, 0xaf, sizeof(PRLock));
00197     pt_debug.locks_destroyed += 1;
00198 #endif
00199     PR_DELETE(lock);
00200 }  /* PR_DestroyLock */
00201 
00202 PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
00203 {
00204     PRIntn rv;
00205     PR_ASSERT(lock != NULL);
00206     rv = pthread_mutex_lock(&lock->mutex);
00207     PR_ASSERT(0 == rv);
00208     PR_ASSERT(0 == lock->notified.length);
00209     PR_ASSERT(NULL == lock->notified.link);
00210     PR_ASSERT(PR_FALSE == lock->locked);
00211     lock->locked = PR_TRUE;
00212     lock->owner = pthread_self();
00213 #if defined(DEBUG)
00214     pt_debug.locks_acquired += 1;
00215 #endif
00216 }  /* PR_Lock */
00217 
00218 PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
00219 {
00220     PRIntn rv;
00221 
00222     PR_ASSERT(lock != NULL);
00223     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex));
00224     PR_ASSERT(PR_TRUE == lock->locked);
00225     PR_ASSERT(pthread_equal(lock->owner, pthread_self()));
00226 
00227     if (!lock->locked || !pthread_equal(lock->owner, pthread_self()))
00228         return PR_FAILURE;
00229 
00230     lock->locked = PR_FALSE;
00231     if (0 == lock->notified.length)  /* shortcut */
00232     {
00233         rv = pthread_mutex_unlock(&lock->mutex);
00234         PR_ASSERT(0 == rv);
00235     }
00236     else pt_PostNotifies(lock, PR_TRUE);
00237 
00238 #if defined(DEBUG)
00239     pt_debug.locks_released += 1;
00240 #endif
00241     return PR_SUCCESS;
00242 }  /* PR_Unlock */
00243 
00244 
00245 /**************************************************************/
00246 /**************************************************************/
00247 /***************************CONDITIONS*************************/
00248 /**************************************************************/
00249 /**************************************************************/
00250 
00251 
00252 /*
00253  * This code is used to compute the absolute time for the wakeup.
00254  * It's moderately ugly, so it's defined here and called in a
00255  * couple of places.
00256  */
00257 #define PT_NANOPERMICRO 1000UL
00258 #define PT_BILLION 1000000000UL
00259 
00260 static PRIntn pt_TimedWait(
00261     pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout)
00262 {
00263     int rv;
00264     struct timeval now;
00265     struct timespec tmo;
00266     PRUint32 ticks = PR_TicksPerSecond();
00267 
00268     tmo.tv_sec = (PRInt32)(timeout / ticks);
00269     tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks));
00270     tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec);
00271 
00272     /* pthreads wants this in absolute time, off we go ... */
00273     (void)GETTIMEOFDAY(&now);
00274     /* that one's usecs, this one's nsecs - grrrr! */
00275     tmo.tv_sec += now.tv_sec;
00276     tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);
00277     tmo.tv_sec += tmo.tv_nsec / PT_BILLION;
00278     tmo.tv_nsec %= PT_BILLION;
00279 
00280     rv = pthread_cond_timedwait(cv, ml, &tmo);
00281 
00282     /* NSPR doesn't report timeouts */
00283 #ifdef _PR_DCETHREADS
00284     if (rv == -1) return (errno == EAGAIN) ? 0 : errno;
00285     else return rv;
00286 #else
00287     return (rv == ETIMEDOUT) ? 0 : rv;
00288 #endif
00289 }  /* pt_TimedWait */
00290 
00291 
00292 /*
00293  * Notifies just get posted to the protecting mutex. The
00294  * actual notification is done when the lock is released so that
00295  * MP systems don't contend for a lock that they can't have.
00296  */
00297 static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast)
00298 {
00299     PRIntn index = 0;
00300     _PT_Notified *notified = &cvar->lock->notified;
00301 
00302     PR_ASSERT(PR_TRUE == cvar->lock->locked);
00303     PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
00304     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
00305 
00306     while (1)
00307     {
00308         for (index = 0; index < notified->length; ++index)
00309         {
00310             if (notified->cv[index].cv == cvar)
00311             {
00312                 if (broadcast)
00313                     notified->cv[index].times = -1;
00314                 else if (-1 != notified->cv[index].times)
00315                     notified->cv[index].times += 1;
00316                 goto finished;  /* we're finished */
00317             }
00318         }
00319         /* if not full, enter new CV in this array */
00320         if (notified->length < PT_CV_NOTIFIED_LENGTH) break;
00321 
00322         /* if there's no link, create an empty array and link it */
00323         if (NULL == notified->link)
00324             notified->link = PR_NEWZAP(_PT_Notified);
00325         notified = notified->link;
00326     }
00327 
00328     /* A brand new entry in the array */
00329     (void)PR_AtomicIncrement(&cvar->notify_pending);
00330     notified->cv[index].times = (broadcast) ? -1 : 1;
00331     notified->cv[index].cv = cvar;
00332     notified->length += 1;
00333 
00334 finished:
00335     PR_ASSERT(PR_TRUE == cvar->lock->locked);
00336     PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
00337 }  /* pt_PostNotifyToCvar */
00338 
00339 PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
00340 {
00341     PRCondVar *cv = PR_NEW(PRCondVar);
00342     PR_ASSERT(lock != NULL);
00343     if (cv != NULL)
00344     {
00345         int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); 
00346         PR_ASSERT(0 == rv);
00347         cv->lock = lock;
00348         cv->notify_pending = 0;
00349 #if defined(DEBUG)
00350         pt_debug.cvars_created += 1;
00351 #endif
00352     }
00353     return cv;
00354 }  /* PR_NewCondVar */
00355 
00356 PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
00357 {
00358     if (0 > PR_AtomicDecrement(&cvar->notify_pending))
00359     {
00360         PRIntn rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
00361 #if defined(DEBUG)
00362         memset(cvar, 0xaf, sizeof(PRCondVar));
00363         pt_debug.cvars_destroyed += 1;
00364 #endif
00365         PR_DELETE(cvar);
00366     }
00367 }  /* PR_DestroyCondVar */
00368 
00369 PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
00370 {
00371     PRIntn rv;
00372     PRThread *thred = PR_CurrentThread();
00373 
00374     PR_ASSERT(cvar != NULL);
00375     /* We'd better be locked */
00376     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
00377     PR_ASSERT(PR_TRUE == cvar->lock->locked);
00378     /* and it better be by us */
00379     PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
00380 
00381     if (_PT_THREAD_INTERRUPTED(thred)) goto aborted;
00382 
00383     /*
00384      * The thread waiting is used for PR_Interrupt
00385      */
00386     thred->waiting = cvar;  /* this is where we're waiting */
00387 
00388     /*
00389      * If we have pending notifies, post them now.
00390      *
00391      * This is not optimal. We're going to post these notifies
00392      * while we're holding the lock. That means on MP systems
00393      * that they are going to collide for the lock that we will
00394      * hold until we actually wait.
00395      */
00396     if (0 != cvar->lock->notified.length)
00397         pt_PostNotifies(cvar->lock, PR_FALSE);
00398 
00399     /*
00400      * We're surrendering the lock, so clear out the locked field.
00401      */
00402     cvar->lock->locked = PR_FALSE;
00403 
00404     if (timeout == PR_INTERVAL_NO_TIMEOUT)
00405         rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex);
00406     else
00407         rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout);
00408 
00409     /* We just got the lock back - this better be empty */
00410     PR_ASSERT(PR_FALSE == cvar->lock->locked);
00411     cvar->lock->locked = PR_TRUE;
00412     cvar->lock->owner = pthread_self();
00413 
00414     PR_ASSERT(0 == cvar->lock->notified.length);
00415     thred->waiting = NULL;  /* and now we're not */
00416     if (_PT_THREAD_INTERRUPTED(thred)) goto aborted;
00417     if (rv != 0)
00418     {
00419         _PR_MD_MAP_DEFAULT_ERROR(rv);
00420         return PR_FAILURE;
00421     }
00422     return PR_SUCCESS;
00423 
00424 aborted:
00425     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
00426     thred->state &= ~PT_THREAD_ABORTED;
00427     return PR_FAILURE;
00428 }  /* PR_WaitCondVar */
00429 
00430 PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
00431 {
00432     PR_ASSERT(cvar != NULL);   
00433     pt_PostNotifyToCvar(cvar, PR_FALSE);
00434     return PR_SUCCESS;
00435 }  /* PR_NotifyCondVar */
00436 
00437 PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar)
00438 {
00439     PR_ASSERT(cvar != NULL);
00440     pt_PostNotifyToCvar(cvar, PR_TRUE);
00441     return PR_SUCCESS;
00442 }  /* PR_NotifyAllCondVar */
00443 
00444 /**************************************************************/
00445 /**************************************************************/
00446 /***************************MONITORS***************************/
00447 /**************************************************************/
00448 /**************************************************************/
00449 
00450 PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void)
00451 {
00452     PRMonitor *mon;
00453     PRCondVar *cvar;
00454 
00455     if (!_pr_initialized) _PR_ImplicitInitialization();
00456 
00457     cvar = PR_NEWZAP(PRCondVar);
00458     if (NULL == cvar)
00459     {
00460         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00461         return NULL;
00462     }
00463     mon = PR_NEWZAP(PRMonitor);
00464     if (mon != NULL)
00465     {
00466         int rv;
00467         rv = _PT_PTHREAD_MUTEX_INIT(mon->lock.mutex, _pt_mattr); 
00468         PR_ASSERT(0 == rv);
00469 
00470         _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
00471 
00472         mon->cvar = cvar;
00473         rv = _PT_PTHREAD_COND_INIT(mon->cvar->cv, _pt_cvar_attr); 
00474         PR_ASSERT(0 == rv);
00475         mon->entryCount = 0;
00476         mon->cvar->lock = &mon->lock;
00477         if (0 != rv)
00478         {
00479             PR_DELETE(mon);
00480             PR_DELETE(cvar);
00481             mon = NULL;
00482         }
00483     }
00484     return mon;
00485 }  /* PR_NewMonitor */
00486 
00487 PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
00488 {
00489     PRMonitor* mon = PR_NewMonitor();
00490     if (mon)
00491         mon->name = name;
00492     return mon;
00493 }
00494 
00495 PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
00496 {
00497     int rv;
00498     PR_ASSERT(mon != NULL);
00499     PR_DestroyCondVar(mon->cvar);
00500     rv = pthread_mutex_destroy(&mon->lock.mutex); PR_ASSERT(0 == rv);
00501 #if defined(DEBUG)
00502         memset(mon, 0xaf, sizeof(PRMonitor));
00503 #endif
00504     PR_DELETE(mon);    
00505 }  /* PR_DestroyMonitor */
00506 
00507 
00508 /* The GC uses this; it is quite arguably a bad interface.  I'm just 
00509  * duplicating it for now - XXXMB
00510  */
00511 PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon)
00512 {
00513     pthread_t self = pthread_self();
00514     if (pthread_equal(mon->owner, self))
00515         return mon->entryCount;
00516     return 0;
00517 }
00518 
00519 PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
00520 {
00521     pthread_t self = pthread_self();
00522 
00523     PR_ASSERT(mon != NULL);
00524     /*
00525      * This is safe only if mon->owner (a pthread_t) can be
00526      * read in one instruction.  Perhaps mon->owner should be
00527      * a "PRThread *"?
00528      */
00529     if (!pthread_equal(mon->owner, self))
00530     {
00531         PR_Lock(&mon->lock);
00532         /* and now I have the lock */
00533         PR_ASSERT(0 == mon->entryCount);
00534         PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner));
00535         _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner);
00536     }
00537     mon->entryCount += 1;
00538 }  /* PR_EnterMonitor */
00539 
00540 PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
00541 {
00542     pthread_t self = pthread_self();
00543 
00544     PR_ASSERT(mon != NULL);
00545     /* The lock better be that - locked */
00546     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
00547     /* we'd better be the owner */
00548     PR_ASSERT(pthread_equal(mon->owner, self));
00549     if (!pthread_equal(mon->owner, self))
00550         return PR_FAILURE;
00551 
00552     /* if it's locked and we have it, then the entries should be > 0 */
00553     PR_ASSERT(mon->entryCount > 0);
00554     mon->entryCount -= 1;  /* reduce by one */
00555     if (mon->entryCount == 0)
00556     {
00557         /* and if it transitioned to zero - unlock */
00558         _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);  /* make the owner unknown */
00559         PR_Unlock(&mon->lock);
00560     }
00561     return PR_SUCCESS;
00562 }  /* PR_ExitMonitor */
00563 
00564 PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout)
00565 {
00566     PRStatus rv;
00567     PRInt16 saved_entries;
00568     pthread_t saved_owner;
00569 
00570     PR_ASSERT(mon != NULL);
00571     /* we'd better be locked */
00572     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
00573     /* and the entries better be positive */
00574     PR_ASSERT(mon->entryCount > 0);
00575     /* and it better be by us */
00576     PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
00577 
00578     /* tuck these away 'till later */
00579     saved_entries = mon->entryCount; 
00580     mon->entryCount = 0;
00581     _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner);
00582     _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
00583     
00584     rv = PR_WaitCondVar(mon->cvar, timeout);
00585 
00586     /* reinstate the intresting information */
00587     mon->entryCount = saved_entries;
00588     _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner);
00589 
00590     return rv;
00591 }  /* PR_Wait */
00592 
00593 PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
00594 {
00595     PR_ASSERT(NULL != mon);
00596     /* we'd better be locked */
00597     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
00598     /* and the entries better be positive */
00599     PR_ASSERT(mon->entryCount > 0);
00600     /* and it better be by us */
00601     PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
00602 
00603     pt_PostNotifyToCvar(mon->cvar, PR_FALSE);
00604 
00605     return PR_SUCCESS;
00606 }  /* PR_Notify */
00607 
00608 PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
00609 {
00610     PR_ASSERT(mon != NULL);
00611     /* we'd better be locked */
00612     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
00613     /* and the entries better be positive */
00614     PR_ASSERT(mon->entryCount > 0);
00615     /* and it better be by us */
00616     PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
00617 
00618     pt_PostNotifyToCvar(mon->cvar, PR_TRUE);
00619 
00620     return PR_SUCCESS;
00621 }  /* PR_NotifyAll */
00622 
00623 /**************************************************************/
00624 /**************************************************************/
00625 /**************************SEMAPHORES**************************/
00626 /**************************************************************/
00627 /**************************************************************/
00628 PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *semaphore)
00629 {
00630     static PRBool unwarned = PR_TRUE;
00631     if (unwarned) unwarned = _PR_Obsolete(
00632         "PR_PostSem", "locks & condition variables");
00633        PR_Lock(semaphore->cvar->lock);
00634        PR_NotifyCondVar(semaphore->cvar);
00635        semaphore->count += 1;
00636        PR_Unlock(semaphore->cvar->lock);
00637 }  /* PR_PostSem */
00638 
00639 PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *semaphore)
00640 {
00641        PRStatus status = PR_SUCCESS;
00642     static PRBool unwarned = PR_TRUE;
00643     if (unwarned) unwarned = _PR_Obsolete(
00644         "PR_WaitSem", "locks & condition variables");
00645        PR_Lock(semaphore->cvar->lock);
00646        while ((semaphore->count == 0) && (PR_SUCCESS == status))
00647               status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT);
00648        if (PR_SUCCESS == status) semaphore->count -= 1;
00649        PR_Unlock(semaphore->cvar->lock);
00650        return status;
00651 }  /* PR_WaitSem */
00652 
00653 PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *semaphore)
00654 {
00655     static PRBool unwarned = PR_TRUE;
00656     if (unwarned) unwarned = _PR_Obsolete(
00657         "PR_DestroySem", "locks & condition variables");
00658     PR_DestroyLock(semaphore->cvar->lock);
00659     PR_DestroyCondVar(semaphore->cvar);
00660     PR_DELETE(semaphore);
00661 }  /* PR_DestroySem */
00662 
00663 PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value)
00664 {
00665     PRSemaphore *semaphore;
00666     static PRBool unwarned = PR_TRUE;
00667     if (!_pr_initialized) _PR_ImplicitInitialization();
00668 
00669     if (unwarned) unwarned = _PR_Obsolete(
00670         "PR_NewSem", "locks & condition variables");
00671 
00672     semaphore = PR_NEWZAP(PRSemaphore);
00673     if (NULL != semaphore)
00674     {
00675         PRLock *lock = PR_NewLock();
00676         if (NULL != lock)
00677         {
00678             semaphore->cvar = PR_NewCondVar(lock);
00679             if (NULL != semaphore->cvar)
00680             {
00681                 semaphore->count = value;
00682                 return semaphore;
00683             }
00684             PR_DestroyLock(lock);
00685         }
00686         PR_DELETE(semaphore);
00687     }
00688     return NULL;
00689 }
00690 
00691 /*
00692  * Define the interprocess named semaphore functions.
00693  * There are three implementations:
00694  * 1. POSIX semaphore based;
00695  * 2. System V semaphore based;
00696  * 3. unsupported (fails with PR_NOT_IMPLEMENTED_ERROR).
00697  */
00698 
00699 #ifdef _PR_HAVE_POSIX_SEMAPHORES
00700 #include <fcntl.h>
00701 
00702 PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
00703     const char *name,
00704     PRIntn flags,
00705     PRIntn mode,
00706     PRUintn value)
00707 {
00708     PRSem *sem;
00709     char osname[PR_IPC_NAME_SIZE];
00710 
00711     if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
00712         == PR_FAILURE)
00713     {
00714         return NULL;
00715     }
00716 
00717     sem = PR_NEW(PRSem);
00718     if (NULL == sem)
00719     {
00720         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00721         return NULL;
00722     }
00723 
00724     if (flags & PR_SEM_CREATE)
00725     {
00726         int oflag = O_CREAT;
00727 
00728         if (flags & PR_SEM_EXCL) oflag |= O_EXCL;
00729         sem->sem = sem_open(osname, oflag, mode, value);
00730     }
00731     else
00732     {
00733 #ifdef HPUX
00734         /* Pass 0 as the mode and value arguments to work around a bug. */
00735         sem->sem = sem_open(osname, 0, 0, 0);
00736 #else
00737         sem->sem = sem_open(osname, 0);
00738 #endif
00739     }
00740     if ((sem_t *) -1 == sem->sem)
00741     {
00742         _PR_MD_MAP_DEFAULT_ERROR(errno);
00743         PR_DELETE(sem);
00744         return NULL;
00745     }
00746     return sem;
00747 }
00748 
00749 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
00750 {
00751     int rv;
00752     rv = sem_wait(sem->sem);
00753     if (0 != rv)
00754     {
00755         _PR_MD_MAP_DEFAULT_ERROR(errno);
00756         return PR_FAILURE;
00757     }
00758     return PR_SUCCESS;
00759 }
00760 
00761 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
00762 {
00763     int rv;
00764     rv = sem_post(sem->sem);
00765     if (0 != rv)
00766     {
00767         _PR_MD_MAP_DEFAULT_ERROR(errno);
00768         return PR_FAILURE;
00769     }
00770     return PR_SUCCESS;
00771 }
00772 
00773 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
00774 {
00775     int rv;
00776     rv = sem_close(sem->sem);
00777     if (0 != rv)
00778     {
00779         _PR_MD_MAP_DEFAULT_ERROR(errno);
00780         return PR_FAILURE;
00781     }
00782     PR_DELETE(sem);
00783     return PR_SUCCESS;
00784 }
00785 
00786 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
00787 {
00788     int rv;
00789     char osname[PR_IPC_NAME_SIZE];
00790 
00791     if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
00792         == PR_FAILURE)
00793     {
00794         return PR_FAILURE;
00795     }
00796     rv = sem_unlink(osname);
00797     if (0 != rv)
00798     {
00799         _PR_MD_MAP_DEFAULT_ERROR(errno);
00800         return PR_FAILURE;
00801     }
00802     return PR_SUCCESS;
00803 }
00804     
00805 #elif defined(_PR_HAVE_SYSV_SEMAPHORES)
00806 
00807 #include <fcntl.h>
00808 #include <sys/sem.h>
00809 
00810 /*
00811  * From the semctl(2) man page in glibc 2.0
00812  */
00813 #if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) \
00814     || defined(FREEBSD) || defined(OPENBSD) || defined(BSDI)
00815 /* union semun is defined by including <sys/sem.h> */
00816 #else
00817 /* according to X/OPEN we have to define it ourselves */
00818 union semun {
00819     int val;
00820     struct semid_ds *buf;
00821     unsigned short  *array;
00822 };
00823 #endif
00824 
00825 /*
00826  * 'a' (97) is the final closing price of NSCP stock.
00827  */
00828 #define NSPR_IPC_KEY_ID 'a'  /* the id argument for ftok() */
00829 
00830 #define NSPR_SEM_MODE 0666
00831 
00832 PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
00833     const char *name,
00834     PRIntn flags,
00835     PRIntn mode,
00836     PRUintn value)
00837 {
00838     PRSem *sem;
00839     key_t key;
00840     union semun arg;
00841     struct sembuf sop;
00842     struct semid_ds seminfo;
00843 #define MAX_TRIES 60
00844     PRIntn i;
00845     char osname[PR_IPC_NAME_SIZE];
00846 
00847     if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
00848         == PR_FAILURE)
00849     {
00850         return NULL;
00851     }
00852 
00853     /* Make sure the file exists before calling ftok. */
00854     if (flags & PR_SEM_CREATE)
00855     {
00856         int osfd = open(osname, O_RDWR|O_CREAT, mode);
00857         if (-1 == osfd)
00858         {
00859             _PR_MD_MAP_OPEN_ERROR(errno);
00860             return NULL;
00861         }
00862         if (close(osfd) == -1)
00863         {
00864             _PR_MD_MAP_CLOSE_ERROR(errno);
00865             return NULL;
00866         }
00867     }
00868     key = ftok(osname, NSPR_IPC_KEY_ID);
00869     if ((key_t)-1 == key)
00870     {
00871         _PR_MD_MAP_DEFAULT_ERROR(errno);
00872         return NULL;
00873     }
00874 
00875     sem = PR_NEW(PRSem);
00876     if (NULL == sem)
00877     {
00878         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00879         return NULL;
00880     }
00881 
00882     if (flags & PR_SEM_CREATE)
00883     {
00884         sem->semid = semget(key, 1, mode|IPC_CREAT|IPC_EXCL);
00885         if (sem->semid >= 0)
00886         {
00887             /* creator of a semaphore is responsible for initializing it */
00888             arg.val = 0;
00889             if (semctl(sem->semid, 0, SETVAL, arg) == -1)
00890             {
00891                 _PR_MD_MAP_DEFAULT_ERROR(errno);
00892                 PR_DELETE(sem);
00893                 return NULL;
00894             }
00895             /* call semop to set sem_otime to nonzero */
00896             sop.sem_num = 0;
00897             sop.sem_op = value;
00898             sop.sem_flg = 0;
00899             if (semop(sem->semid, &sop, 1) == -1)
00900             {
00901                 _PR_MD_MAP_DEFAULT_ERROR(errno);
00902                 PR_DELETE(sem);
00903                 return NULL;
00904             }
00905             return sem;
00906         }
00907 
00908         if (errno != EEXIST || flags & PR_SEM_EXCL)
00909         {
00910             _PR_MD_MAP_DEFAULT_ERROR(errno);
00911             PR_DELETE(sem);
00912             return NULL;
00913         }
00914     }
00915 
00916     sem->semid = semget(key, 1, NSPR_SEM_MODE);
00917     if (sem->semid == -1)
00918     {
00919         _PR_MD_MAP_DEFAULT_ERROR(errno);
00920         PR_DELETE(sem);
00921         return NULL;
00922     }
00923     for (i = 0; i < MAX_TRIES; i++)
00924     {
00925         arg.buf = &seminfo;
00926         semctl(sem->semid, 0, IPC_STAT, arg);
00927         if (seminfo.sem_otime != 0) break;
00928         sleep(1);
00929     }
00930     if (i == MAX_TRIES)
00931     {
00932         PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
00933         PR_DELETE(sem);
00934         return NULL;
00935     }
00936     return sem;
00937 }
00938 
00939 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
00940 {
00941     struct sembuf sop;
00942 
00943     sop.sem_num = 0;
00944     sop.sem_op = -1;
00945     sop.sem_flg = 0;
00946     if (semop(sem->semid, &sop, 1) == -1)
00947     {
00948         _PR_MD_MAP_DEFAULT_ERROR(errno);
00949         return PR_FAILURE;
00950     }
00951     return PR_SUCCESS;
00952 }
00953 
00954 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
00955 {
00956     struct sembuf sop;
00957 
00958     sop.sem_num = 0;
00959     sop.sem_op = 1;
00960     sop.sem_flg = 0;
00961     if (semop(sem->semid, &sop, 1) == -1)
00962     {
00963         _PR_MD_MAP_DEFAULT_ERROR(errno);
00964         return PR_FAILURE;
00965     }
00966     return PR_SUCCESS;
00967 }
00968 
00969 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
00970 {
00971     PR_DELETE(sem);
00972     return PR_SUCCESS;
00973 }
00974 
00975 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
00976 {
00977     key_t key;
00978     int semid;
00979     /* On some systems (e.g., glibc 2.0) semctl requires a fourth argument */
00980     union semun unused;
00981     char osname[PR_IPC_NAME_SIZE];
00982 
00983     if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
00984         == PR_FAILURE)
00985     {
00986         return PR_FAILURE;
00987     }
00988     key = ftok(osname, NSPR_IPC_KEY_ID);
00989     if ((key_t) -1 == key)
00990     {
00991         _PR_MD_MAP_DEFAULT_ERROR(errno);
00992         return PR_FAILURE;
00993     }
00994     if (unlink(osname) == -1)
00995     {
00996         _PR_MD_MAP_UNLINK_ERROR(errno);
00997         return PR_FAILURE;
00998     }
00999     semid = semget(key, 1, NSPR_SEM_MODE);
01000     if (-1 == semid)
01001     {
01002         _PR_MD_MAP_DEFAULT_ERROR(errno);
01003         return PR_FAILURE;
01004     }
01005     unused.val = 0;
01006     if (semctl(semid, 0, IPC_RMID, unused) == -1)
01007     { 
01008         _PR_MD_MAP_DEFAULT_ERROR(errno);
01009         return PR_FAILURE;
01010     }
01011     return PR_SUCCESS;
01012 }
01013 
01014 #else /* neither POSIX nor System V semaphores are available */
01015 
01016 PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
01017     const char *name,
01018     PRIntn flags,
01019     PRIntn mode,
01020     PRUintn value)
01021 {
01022     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
01023     return NULL;
01024 }
01025 
01026 PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
01027 {
01028     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
01029     return PR_FAILURE;
01030 }
01031 
01032 PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
01033 {
01034     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
01035     return PR_FAILURE;
01036 }
01037 
01038 PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
01039 {
01040     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
01041     return PR_FAILURE;
01042 }
01043 
01044 PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
01045 {
01046     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
01047     return PR_FAILURE;
01048 }
01049 
01050 #endif /* end of interprocess named semaphore functions */
01051 
01052 /**************************************************************/
01053 /**************************************************************/
01054 /******************ROUTINES FOR DCE EMULATION******************/
01055 /**************************************************************/
01056 /**************************************************************/
01057 
01058 #include "prpdce.h"
01059 
01060 PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
01061 {
01062     PRIntn rv = pthread_mutex_trylock(&lock->mutex);
01063     if (rv == PT_TRYLOCK_SUCCESS)
01064     {
01065         PR_ASSERT(PR_FALSE == lock->locked);
01066         lock->locked = PR_TRUE;
01067         lock->owner = pthread_self();
01068     }
01069     /* XXX set error code? */
01070     return (PT_TRYLOCK_SUCCESS == rv) ? PR_SUCCESS : PR_FAILURE;
01071 }  /* PRP_TryLock */
01072 
01073 PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void)
01074 {
01075     PRCondVar *cv;
01076 
01077     if (!_pr_initialized) _PR_ImplicitInitialization();
01078 
01079     cv = PR_NEW(PRCondVar);
01080     if (cv != NULL)
01081     {
01082         int rv;
01083         rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); 
01084         PR_ASSERT(0 == rv);
01085         cv->lock = _PR_NAKED_CV_LOCK;
01086     }
01087     return cv;
01088 }  /* PRP_NewNakedCondVar */
01089 
01090 PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar)
01091 {
01092     int rv;
01093     rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
01094 #if defined(DEBUG)
01095         memset(cvar, 0xaf, sizeof(PRCondVar));
01096 #endif
01097     PR_DELETE(cvar);
01098 }  /* PRP_DestroyNakedCondVar */
01099 
01100 PR_IMPLEMENT(PRStatus) PRP_NakedWait(
01101     PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout)
01102 {
01103     PRIntn rv;
01104     PR_ASSERT(cvar != NULL);
01105     /* XXX do we really want to assert this in a naked wait? */
01106     PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex));
01107     if (timeout == PR_INTERVAL_NO_TIMEOUT)
01108         rv = pthread_cond_wait(&cvar->cv, &ml->mutex);
01109     else
01110         rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout);
01111     if (rv != 0)
01112     {
01113         _PR_MD_MAP_DEFAULT_ERROR(rv);
01114         return PR_FAILURE;
01115     }
01116     return PR_SUCCESS;
01117 }  /* PRP_NakedWait */
01118 
01119 PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
01120 {
01121     int rv;
01122     PR_ASSERT(cvar != NULL);
01123     rv = pthread_cond_signal(&cvar->cv);
01124     PR_ASSERT(0 == rv);
01125     return PR_SUCCESS;
01126 }  /* PRP_NakedNotify */
01127 
01128 PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar)
01129 {
01130     int rv;
01131     PR_ASSERT(cvar != NULL);
01132     rv = pthread_cond_broadcast(&cvar->cv);
01133     PR_ASSERT(0 == rv);
01134     return PR_SUCCESS;
01135 }  /* PRP_NakedBroadcast */
01136 
01137 #endif  /* defined(_PR_PTHREADS) */
01138 
01139 /* ptsynch.c */