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 +}