⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ptsynch.c

📁 Netscape NSPR库源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//*  * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ *  * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. *  * The Original Code is the Netscape Portable Runtime (NSPR). *  * The Initial Developer of the Original Code is Netscape * Communications Corporation.  Portions created by Netscape are  * Copyright (C) 1998-2000 Netscape Communications Corporation.  All * Rights Reserved. *  * Contributor(s): *  * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable  * instead of those above.  If you wish to allow use of your  * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL.  If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. *//*** File:            ptsynch.c** Descritpion:        Implemenation for thread synchronization using pthreads** Exports:            prlock.h, prcvar.h, prmon.h, prcmon.h*/#if defined(_PR_PTHREADS)#include "primpl.h"#include "obsolete/prsem.h"#include <string.h>#include <pthread.h>#include <sys/time.h>static pthread_mutexattr_t _pt_mattr;static pthread_condattr_t _pt_cvar_attr;#if defined(DEBUG)extern PTDebug pt_debug;  /* this is shared between several modules */#if defined(_PR_DCETHREADS)static pthread_t pt_zero_tid;  /* a null pthread_t (pthread_t is a struct                                * in DCE threads) to compare with */#endif  /* defined(_PR_DCETHREADS) */#endif  /* defined(DEBUG) *//**************************************************************//**************************************************************//*****************************LOCKS****************************//**************************************************************//**************************************************************/void _PR_InitLocks(void){    int rv;    rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr);     PR_ASSERT(0 == rv);#ifdef LINUX#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)    rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP);    PR_ASSERT(0 == rv);#endif#endif    rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);    PR_ASSERT(0 == rv);}static void pt_PostNotifies(PRLock *lock, PRBool unlock){    PRIntn index, rv;    _PT_Notified post;    _PT_Notified *notified, *prev = NULL;    /*     * Time to actually notify any conditions that were affected     * while the lock was held. Get a copy of the list that's in     * the lock structure and then zero the original. If it's     * linked to other such structures, we own that storage.     */    post = lock->notified;  /* a safe copy; we own the lock */#if defined(DEBUG)    memset(&lock->notified, 0, sizeof(_PT_Notified));  /* reset */#else    lock->notified.length = 0;  /* these are really sufficient */    lock->notified.link = NULL;#endif    /* should (may) we release lock before notifying? */    if (unlock)    {        rv = pthread_mutex_unlock(&lock->mutex);        PR_ASSERT(0 == rv);    }    notified = &post;  /* this is where we start */    do    {        for (index = 0; index < notified->length; ++index)        {            PRCondVar *cv = notified->cv[index].cv;            PR_ASSERT(NULL != cv);            PR_ASSERT(0 != notified->cv[index].times);            if (-1 == notified->cv[index].times)            {                rv = pthread_cond_broadcast(&cv->cv);                PR_ASSERT(0 == rv);            }            else            {                while (notified->cv[index].times-- > 0)                {                    rv = pthread_cond_signal(&cv->cv);                    PR_ASSERT(0 == rv);                }            }#if defined(DEBUG)            pt_debug.cvars_notified += 1;            if (0 > PR_AtomicDecrement(&cv->notify_pending))            {                pt_debug.delayed_cv_deletes += 1;                PR_DestroyCondVar(cv);            }#else  /* defined(DEBUG) */            if (0 > PR_AtomicDecrement(&cv->notify_pending))                PR_DestroyCondVar(cv);#endif  /* defined(DEBUG) */        }        prev = notified;        notified = notified->link;        if (&post != prev) PR_DELETE(prev);    } while (NULL != notified);}  /* pt_PostNotifies */PR_IMPLEMENT(PRLock*) PR_NewLock(void){    PRIntn rv;    PRLock *lock;    if (!_pr_initialized) _PR_ImplicitInitialization();    lock = PR_NEWZAP(PRLock);    if (lock != NULL)    {        rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr);         PR_ASSERT(0 == rv);    }#if defined(DEBUG)    pt_debug.locks_created += 1;#endif    return lock;}  /* PR_NewLock */PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock){    PRIntn rv;    PR_ASSERT(NULL != lock);    PR_ASSERT(PR_FALSE == lock->locked);    PR_ASSERT(0 == lock->notified.length);    PR_ASSERT(NULL == lock->notified.link);    rv = pthread_mutex_destroy(&lock->mutex);    PR_ASSERT(0 == rv);#if defined(DEBUG)    memset(lock, 0xaf, sizeof(PRLock));    pt_debug.locks_destroyed += 1;#endif    PR_DELETE(lock);}  /* PR_DestroyLock */PR_IMPLEMENT(void) PR_Lock(PRLock *lock){    PRIntn rv;    PR_ASSERT(lock != NULL);    rv = pthread_mutex_lock(&lock->mutex);    PR_ASSERT(0 == rv);    PR_ASSERT(0 == lock->notified.length);    PR_ASSERT(NULL == lock->notified.link);    PR_ASSERT(PR_FALSE == lock->locked);    lock->locked = PR_TRUE;    lock->owner = pthread_self();#if defined(DEBUG)    pt_debug.locks_acquired += 1;#endif}  /* PR_Lock */PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock){    PRIntn rv;    PR_ASSERT(lock != NULL);    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex));    PR_ASSERT(PR_TRUE == lock->locked);    PR_ASSERT(pthread_equal(lock->owner, pthread_self()));    if (!lock->locked || !pthread_equal(lock->owner, pthread_self()))        return PR_FAILURE;    lock->locked = PR_FALSE;    if (0 == lock->notified.length)  /* shortcut */    {        rv = pthread_mutex_unlock(&lock->mutex);        PR_ASSERT(0 == rv);    }    else pt_PostNotifies(lock, PR_TRUE);#if defined(DEBUG)    pt_debug.locks_released += 1;#endif    return PR_SUCCESS;}  /* PR_Unlock *//**************************************************************//**************************************************************//***************************CONDITIONS*************************//**************************************************************//**************************************************************//* * This code is used to compute the absolute time for the wakeup. * It's moderately ugly, so it's defined here and called in a * couple of places. */#define PT_NANOPERMICRO 1000UL#define PT_BILLION 1000000000ULstatic PRIntn pt_TimedWait(    pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout){    int rv;    struct timeval now;    struct timespec tmo;    PRUint32 ticks = PR_TicksPerSecond();    tmo.tv_sec = (PRInt32)(timeout / ticks);    tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks));    tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec);    /* pthreads wants this in absolute time, off we go ... */    (void)GETTIMEOFDAY(&now);    /* that one's usecs, this one's nsecs - grrrr! */    tmo.tv_sec += now.tv_sec;    tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);    tmo.tv_sec += tmo.tv_nsec / PT_BILLION;    tmo.tv_nsec %= PT_BILLION;    rv = pthread_cond_timedwait(cv, ml, &tmo);    /* NSPR doesn't report timeouts */#ifdef _PR_DCETHREADS    if (rv == -1) return (errno == EAGAIN) ? 0 : errno;    else return rv;#else    return (rv == ETIMEDOUT) ? 0 : rv;#endif}  /* pt_TimedWait *//* * Notifies just get posted to the protecting mutex. The * actual notification is done when the lock is released so that * MP systems don't contend for a lock that they can't have. */static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast){    PRIntn index = 0;    _PT_Notified *notified = &cvar->lock->notified;    PR_ASSERT(PR_TRUE == cvar->lock->locked);    PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));    while (1)    {        for (index = 0; index < notified->length; ++index)        {            if (notified->cv[index].cv == cvar)            {                if (broadcast)                    notified->cv[index].times = -1;                else if (-1 != notified->cv[index].times)                    notified->cv[index].times += 1;                goto finished;  /* we're finished */            }        }        /* if not full, enter new CV in this array */        if (notified->length < PT_CV_NOTIFIED_LENGTH) break;        /* if there's no link, create an empty array and link it */        if (NULL == notified->link)            notified->link = PR_NEWZAP(_PT_Notified);        notified = notified->link;    }    /* A brand new entry in the array */    (void)PR_AtomicIncrement(&cvar->notify_pending);    notified->cv[index].times = (broadcast) ? -1 : 1;    notified->cv[index].cv = cvar;    notified->length += 1;finished:    PR_ASSERT(PR_TRUE == cvar->lock->locked);    PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));}  /* pt_PostNotifyToCvar */PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock){    PRCondVar *cv = PR_NEW(PRCondVar);    PR_ASSERT(lock != NULL);    if (cv != NULL)    {        int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);         PR_ASSERT(0 == rv);        cv->lock = lock;        cv->notify_pending = 0;#if defined(DEBUG)        pt_debug.cvars_created += 1;#endif    }    return cv;}  /* PR_NewCondVar */PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar){    if (0 > PR_AtomicDecrement(&cvar->notify_pending))    {        PRIntn rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);#if defined(DEBUG)        memset(cvar, 0xaf, sizeof(PRCondVar));        pt_debug.cvars_destroyed += 1;#endif        PR_DELETE(cvar);    }}  /* PR_DestroyCondVar */PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout){    PRIntn rv;    PRThread *thred = PR_CurrentThread();    PR_ASSERT(cvar != NULL);    /* We'd better be locked */    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));    PR_ASSERT(PR_TRUE == cvar->lock->locked);    /* and it better be by us */    PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));    if (_PT_THREAD_INTERRUPTED(thred)) goto aborted;    /*     * The thread waiting is used for PR_Interrupt     */    thred->waiting = cvar;  /* this is where we're waiting */    /*     * If we have pending notifies, post them now.     *     * This is not optimal. We're going to post these notifies     * while we're holding the lock. That means on MP systems     * that they are going to collide for the lock that we will     * hold until we actually wait.     */    if (0 != cvar->lock->notified.length)        pt_PostNotifies(cvar->lock, PR_FALSE);    /*     * We're surrendering the lock, so clear out the locked field.     */    cvar->lock->locked = PR_FALSE;    if (timeout == PR_INTERVAL_NO_TIMEOUT)        rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex);    else        rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout);    /* We just got the lock back - this better be empty */    PR_ASSERT(PR_FALSE == cvar->lock->locked);    cvar->lock->locked = PR_TRUE;    cvar->lock->owner = pthread_self();    PR_ASSERT(0 == cvar->lock->notified.length);    thred->waiting = NULL;  /* and now we're not */    if (_PT_THREAD_INTERRUPTED(thred)) goto aborted;    if (rv != 0)    {        _PR_MD_MAP_DEFAULT_ERROR(rv);        return PR_FAILURE;    }    return PR_SUCCESS;aborted:    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);    thred->state &= ~PT_THREAD_ABORTED;    return PR_FAILURE;}  /* PR_WaitCondVar */PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar){    PR_ASSERT(cvar != NULL);       pt_PostNotifyToCvar(cvar, PR_FALSE);    return PR_SUCCESS;}  /* PR_NotifyCondVar */PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar){    PR_ASSERT(cvar != NULL);    pt_PostNotifyToCvar(cvar, PR_TRUE);    return PR_SUCCESS;}  /* PR_NotifyAllCondVar *//**************************************************************//**************************************************************//***************************MONITORS***************************//**************************************************************//**************************************************************/PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void){    PRMonitor *mon;    PRCondVar *cvar;    if (!_pr_initialized) _PR_ImplicitInitialization();    cvar = PR_NEWZAP(PRCondVar);    if (NULL == cvar)    {        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);        return NULL;    }    mon = PR_NEWZAP(PRMonitor);    if (mon != NULL)    {        int rv;        rv = _PT_PTHREAD_MUTEX_INIT(mon->lock.mutex, _pt_mattr);         PR_ASSERT(0 == rv);        _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);        mon->cvar = cvar;        rv = _PT_PTHREAD_COND_INIT(mon->cvar->cv, _pt_cvar_attr);         PR_ASSERT(0 == rv);        mon->entryCount = 0;        mon->cvar->lock = &mon->lock;        if (0 != rv)        {            PR_DELETE(mon);            PR_DELETE(cvar);            mon = NULL;        }    }    return mon;}  /* PR_NewMonitor */PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name){    PRMonitor* mon = PR_NewMonitor();    mon->name = name;    return mon;}PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon){    int rv;    PR_ASSERT(mon != NULL);    PR_DestroyCondVar(mon->cvar);    rv = pthread_mutex_destroy(&mon->lock.mutex); PR_ASSERT(0 == rv);#if defined(DEBUG)        memset(mon, 0xaf, sizeof(PRMonitor));#endif    PR_DELETE(mon);    }  /* PR_DestroyMonitor *//* The GC uses this; it is quite arguably a bad interface.  I'm just  * duplicating it for now - XXXMB */PR_IMPLEMENT(PRInt32) PR_GetMonitorEntryCount(PRMonitor *mon){    pthread_t self = pthread_self();    if (pthread_equal(mon->owner, self))        return mon->entryCount;    return 0;}PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon){    pthread_t self = pthread_self();    PR_ASSERT(mon != NULL);    /*     * This is safe only if mon->owner (a pthread_t) can be     * read in one instruction.  Perhaps mon->owner should be     * a "PRThread *"?     */    if (!pthread_equal(mon->owner, self))    {        PR_Lock(&mon->lock);        /* and now I have the lock */        PR_ASSERT(0 == mon->entryCount);        PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner));        _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner);    }    mon->entryCount += 1;}  /* PR_EnterMonitor */PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon){    pthread_t self = pthread_self();    PR_ASSERT(mon != NULL);    /* The lock better be that - locked */    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));    /* we'd better be the owner */    PR_ASSERT(pthread_equal(mon->owner, self));    if (!pthread_equal(mon->owner, self))        return PR_FAILURE;    /* if it's locked and we have it, then the entries should be > 0 */    PR_ASSERT(mon->entryCount > 0);    mon->entryCount -= 1;  /* reduce by one */    if (mon->entryCount == 0)    {        /* and if it transitioned to zero - unlock */        _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);  /* make the owner unknown */        PR_Unlock(&mon->lock);    }    return PR_SUCCESS;}  /* PR_ExitMonitor */PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout){    PRStatus rv;    PRInt16 saved_entries;    pthread_t saved_owner;    PR_ASSERT(mon != NULL);    /* we'd better be locked */    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));    /* and the entries better be positive */    PR_ASSERT(mon->entryCount > 0);    /* and it better be by us */    PR_ASSERT(pthread_equal(mon->owner, pthread_self()));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -