nsprpub/pr/src/bthreads/btcvar.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/nsprpub/pr/src/bthreads/btcvar.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,244 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include <kernel/OS.h>
    1.10 +
    1.11 +#include "primpl.h"
    1.12 +
    1.13 +/*
    1.14 +** Create a new condition variable.
    1.15 +**
    1.16 +** 	"lock" is the lock used to protect the condition variable.
    1.17 +**
    1.18 +** Condition variables are synchronization objects that threads can use
    1.19 +** to wait for some condition to occur.
    1.20 +**
    1.21 +** This may fail if memory is tight or if some operating system resource
    1.22 +** is low. In such cases, a NULL will be returned.
    1.23 +*/
    1.24 +PR_IMPLEMENT(PRCondVar*)
    1.25 +    PR_NewCondVar (PRLock *lock)
    1.26 +{
    1.27 +    PRCondVar *cv = PR_NEW( PRCondVar );
    1.28 +    PR_ASSERT( NULL != lock );
    1.29 +    if( NULL != cv )
    1.30 +    {
    1.31 +	cv->lock = lock;
    1.32 +	cv->sem = create_sem(0, "CVSem");
    1.33 +	cv->handshakeSem = create_sem(0, "CVHandshake");
    1.34 +	cv->signalSem = create_sem( 0, "CVSignal");
    1.35 +	cv->signalBenCount = 0;
    1.36 +	cv->ns = cv->nw = 0;
    1.37 +	PR_ASSERT( cv->sem >= B_NO_ERROR );
    1.38 +	PR_ASSERT( cv->handshakeSem >= B_NO_ERROR );
    1.39 +	PR_ASSERT( cv->signalSem >= B_NO_ERROR );
    1.40 +    }
    1.41 +    return cv;
    1.42 +} /* PR_NewCondVar */
    1.43 +
    1.44 +/*
    1.45 +** Destroy a condition variable. There must be no thread
    1.46 +** waiting on the condvar. The caller is responsible for guaranteeing
    1.47 +** that the condvar is no longer in use.
    1.48 +**
    1.49 +*/
    1.50 +PR_IMPLEMENT(void)
    1.51 +    PR_DestroyCondVar (PRCondVar *cvar)
    1.52 +{
    1.53 +    status_t result = delete_sem( cvar->sem );
    1.54 +    PR_ASSERT( result == B_NO_ERROR );
    1.55 +    
    1.56 +    result = delete_sem( cvar->handshakeSem );
    1.57 +    PR_ASSERT( result == B_NO_ERROR );
    1.58 +
    1.59 +    result = delete_sem( cvar->signalSem );
    1.60 +    PR_ASSERT( result == B_NO_ERROR );
    1.61 +
    1.62 +    PR_DELETE( cvar );
    1.63 +}
    1.64 +
    1.65 +/*
    1.66 +** The thread that waits on a condition is blocked in a "waiting on
    1.67 +** condition" state until another thread notifies the condition or a
    1.68 +** caller specified amount of time expires. The lock associated with
    1.69 +** the condition variable will be released, which must have be held
    1.70 +** prior to the call to wait.
    1.71 +**
    1.72 +** Logically a notified thread is moved from the "waiting on condition"
    1.73 +** state and made "ready." When scheduled, it will attempt to reacquire
    1.74 +** the lock that it held when wait was called.
    1.75 +**
    1.76 +** The timeout has two well known values, PR_INTERVAL_NO_TIMEOUT and
    1.77 +** PR_INTERVAL_NO_WAIT. The former value requires that a condition be
    1.78 +** notified (or the thread interrupted) before it will resume from the
    1.79 +** wait. If the timeout has a value of PR_INTERVAL_NO_WAIT, the effect
    1.80 +** is to release the lock, possibly causing a rescheduling within the
    1.81 +** runtime, then immediately attempting to reacquire the lock and resume.
    1.82 +**
    1.83 +** Any other value for timeout will cause the thread to be rescheduled
    1.84 +** either due to explicit notification or an expired interval. The latter
    1.85 +** must be determined by treating time as one part of the monitored data
    1.86 +** being protected by the lock and tested explicitly for an expired
    1.87 +** interval.
    1.88 +**
    1.89 +** Returns PR_FAILURE if the caller has not locked the lock associated
    1.90 +** with the condition variable or the thread was interrupted (PR_Interrupt()).
    1.91 +** The particular reason can be extracted with PR_GetError().
    1.92 +*/
    1.93 +PR_IMPLEMENT(PRStatus)
    1.94 +    PR_WaitCondVar (PRCondVar *cvar, PRIntervalTime timeout)
    1.95 +{
    1.96 +    status_t err;
    1.97 +    if( timeout == PR_INTERVAL_NO_WAIT ) 
    1.98 +    {
    1.99 +        PR_Unlock( cvar->lock );
   1.100 +        PR_Lock( cvar->lock );
   1.101 +        return PR_SUCCESS;
   1.102 +    }
   1.103 +
   1.104 +    if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
   1.105 +    {
   1.106 +        if (acquire_sem(cvar->signalSem) == B_INTERRUPTED) 
   1.107 +        {
   1.108 +            atomic_add( &cvar->signalBenCount, -1 );
   1.109 +            return PR_FAILURE;
   1.110 +        }
   1.111 +    }
   1.112 +    cvar->nw += 1;
   1.113 +    if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
   1.114 +    {
   1.115 +        release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
   1.116 +    }
   1.117 +
   1.118 +    PR_Unlock( cvar->lock );
   1.119 +    if( timeout==PR_INTERVAL_NO_TIMEOUT ) 
   1.120 +    {
   1.121 +    	err = acquire_sem(cvar->sem);
   1.122 +    } 
   1.123 +    else 
   1.124 +    {
   1.125 +    	err = acquire_sem_etc(cvar->sem, 1, B_RELATIVE_TIMEOUT, PR_IntervalToMicroseconds(timeout) );
   1.126 +    }
   1.127 +
   1.128 +    if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
   1.129 +    {
   1.130 +        while (acquire_sem(cvar->signalSem) == B_INTERRUPTED);
   1.131 +    }
   1.132 +
   1.133 +    if (cvar->ns > 0)
   1.134 +    {
   1.135 +        release_sem_etc(cvar->handshakeSem, 1, B_DO_NOT_RESCHEDULE);
   1.136 +        cvar->ns -= 1;
   1.137 +    }
   1.138 +    cvar->nw -= 1;
   1.139 +    if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
   1.140 +    {
   1.141 +        release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
   1.142 +    }
   1.143 +
   1.144 +    PR_Lock( cvar->lock );
   1.145 +    if(err!=B_NO_ERROR) 
   1.146 +    {
   1.147 +        return PR_FAILURE;
   1.148 +    }
   1.149 +    return PR_SUCCESS;
   1.150 +}
   1.151 +
   1.152 +/*
   1.153 +** Notify ONE thread that is currently waiting on 'cvar'. Which thread is
   1.154 +** dependent on the implementation of the runtime. Common sense would dictate
   1.155 +** that all threads waiting on a single condition have identical semantics,
   1.156 +** therefore which one gets notified is not significant. 
   1.157 +**
   1.158 +** The calling thead must hold the lock that protects the condition, as
   1.159 +** well as the invariants that are tightly bound to the condition, when
   1.160 +** notify is called.
   1.161 +**
   1.162 +** Returns PR_FAILURE if the caller has not locked the lock associated
   1.163 +** with the condition variable.
   1.164 +*/
   1.165 +PR_IMPLEMENT(PRStatus)
   1.166 +    PR_NotifyCondVar (PRCondVar *cvar)
   1.167 +{
   1.168 +    status_t err ;
   1.169 +    if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
   1.170 +    {
   1.171 +        if (acquire_sem(cvar->signalSem) == B_INTERRUPTED) 
   1.172 +        {
   1.173 +            atomic_add( &cvar->signalBenCount, -1 );
   1.174 +            return PR_FAILURE;
   1.175 +        }
   1.176 +    }
   1.177 +    if (cvar->nw > cvar->ns)
   1.178 +    {
   1.179 +        cvar->ns += 1;
   1.180 +        release_sem_etc(cvar->sem, 1, B_DO_NOT_RESCHEDULE);
   1.181 +        if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
   1.182 +        {
   1.183 +            release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
   1.184 +        }
   1.185 +
   1.186 +        while (acquire_sem(cvar->handshakeSem) == B_INTERRUPTED) 
   1.187 +        {
   1.188 +            err = B_INTERRUPTED; 
   1.189 +        }
   1.190 +    }
   1.191 +    else
   1.192 +    {
   1.193 +        if( atomic_add( &cvar->signalBenCount, -1 ) > 1 )
   1.194 +        {
   1.195 +            release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
   1.196 +        }
   1.197 +    }
   1.198 +    return PR_SUCCESS; 
   1.199 +}
   1.200 +
   1.201 +/*
   1.202 +** Notify all of the threads waiting on the condition variable. The order
   1.203 +** that the threads are notified is indeterminant. The lock that protects
   1.204 +** the condition must be held.
   1.205 +**
   1.206 +** Returns PR_FAILURE if the caller has not locked the lock associated
   1.207 +** with the condition variable.
   1.208 +*/
   1.209 +PR_IMPLEMENT(PRStatus)
   1.210 +    PR_NotifyAllCondVar (PRCondVar *cvar)
   1.211 +{
   1.212 +    int32 handshakes;
   1.213 +    status_t err = B_OK;
   1.214 +
   1.215 +    if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
   1.216 +    {
   1.217 +        if (acquire_sem(cvar->signalSem) == B_INTERRUPTED) 
   1.218 +        {
   1.219 +            atomic_add( &cvar->signalBenCount, -1 );
   1.220 +            return PR_FAILURE;
   1.221 +        }
   1.222 +    }
   1.223 +
   1.224 +    if (cvar->nw > cvar->ns)
   1.225 +    {
   1.226 +        handshakes = cvar->nw - cvar->ns;
   1.227 +        cvar->ns = cvar->nw;				
   1.228 +        release_sem_etc(cvar->sem, handshakes, B_DO_NOT_RESCHEDULE);	
   1.229 +        if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
   1.230 +        {
   1.231 +            release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
   1.232 +        }
   1.233 +
   1.234 +        while (acquire_sem_etc(cvar->handshakeSem, handshakes, 0, 0) == B_INTERRUPTED) 
   1.235 +        {
   1.236 +            err = B_INTERRUPTED; 
   1.237 +        }
   1.238 +    }
   1.239 +    else
   1.240 +    {
   1.241 +        if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
   1.242 +        {
   1.243 +            release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
   1.244 +        }
   1.245 +    }
   1.246 +    return PR_SUCCESS;
   1.247 +}

mercurial