Back to index

lightning-sunbird  0.9+nobinonly
btcvar.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 #include <kernel/OS.h>
00039 
00040 #include "primpl.h"
00041 
00042 /*
00043 ** Create a new condition variable.
00044 **
00045 **     "lock" is the lock used to protect the condition variable.
00046 **
00047 ** Condition variables are synchronization objects that threads can use
00048 ** to wait for some condition to occur.
00049 **
00050 ** This may fail if memory is tight or if some operating system resource
00051 ** is low. In such cases, a NULL will be returned.
00052 */
00053 PR_IMPLEMENT(PRCondVar*)
00054     PR_NewCondVar (PRLock *lock)
00055 {
00056     PRCondVar *cv = PR_NEW( PRCondVar );
00057     PR_ASSERT( NULL != lock );
00058     if( NULL != cv )
00059     {
00060        cv->lock = lock;
00061        cv->sem = create_sem(0, "CVSem");
00062        cv->handshakeSem = create_sem(0, "CVHandshake");
00063        cv->signalSem = create_sem( 0, "CVSignal");
00064        cv->signalBenCount = 0;
00065        cv->ns = cv->nw = 0;
00066        PR_ASSERT( cv->sem >= B_NO_ERROR );
00067        PR_ASSERT( cv->handshakeSem >= B_NO_ERROR );
00068        PR_ASSERT( cv->signalSem >= B_NO_ERROR );
00069     }
00070     return cv;
00071 } /* PR_NewCondVar */
00072 
00073 /*
00074 ** Destroy a condition variable. There must be no thread
00075 ** waiting on the condvar. The caller is responsible for guaranteeing
00076 ** that the condvar is no longer in use.
00077 **
00078 */
00079 PR_IMPLEMENT(void)
00080     PR_DestroyCondVar (PRCondVar *cvar)
00081 {
00082     status_t result = delete_sem( cvar->sem );
00083     PR_ASSERT( result == B_NO_ERROR );
00084     
00085     result = delete_sem( cvar->handshakeSem );
00086     PR_ASSERT( result == B_NO_ERROR );
00087 
00088     result = delete_sem( cvar->signalSem );
00089     PR_ASSERT( result == B_NO_ERROR );
00090 
00091     PR_DELETE( cvar );
00092 }
00093 
00094 /*
00095 ** The thread that waits on a condition is blocked in a "waiting on
00096 ** condition" state until another thread notifies the condition or a
00097 ** caller specified amount of time expires. The lock associated with
00098 ** the condition variable will be released, which must have be held
00099 ** prior to the call to wait.
00100 **
00101 ** Logically a notified thread is moved from the "waiting on condition"
00102 ** state and made "ready." When scheduled, it will attempt to reacquire
00103 ** the lock that it held when wait was called.
00104 **
00105 ** The timeout has two well known values, PR_INTERVAL_NO_TIMEOUT and
00106 ** PR_INTERVAL_NO_WAIT. The former value requires that a condition be
00107 ** notified (or the thread interrupted) before it will resume from the
00108 ** wait. If the timeout has a value of PR_INTERVAL_NO_WAIT, the effect
00109 ** is to release the lock, possibly causing a rescheduling within the
00110 ** runtime, then immediately attempting to reacquire the lock and resume.
00111 **
00112 ** Any other value for timeout will cause the thread to be rescheduled
00113 ** either due to explicit notification or an expired interval. The latter
00114 ** must be determined by treating time as one part of the monitored data
00115 ** being protected by the lock and tested explicitly for an expired
00116 ** interval.
00117 **
00118 ** Returns PR_FAILURE if the caller has not locked the lock associated
00119 ** with the condition variable or the thread was interrupted (PR_Interrupt()).
00120 ** The particular reason can be extracted with PR_GetError().
00121 */
00122 PR_IMPLEMENT(PRStatus)
00123     PR_WaitCondVar (PRCondVar *cvar, PRIntervalTime timeout)
00124 {
00125     status_t err;
00126     if( timeout == PR_INTERVAL_NO_WAIT ) 
00127     {
00128         PR_Unlock( cvar->lock );
00129         PR_Lock( cvar->lock );
00130         return PR_SUCCESS;
00131     }
00132 
00133     if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
00134     {
00135         if (acquire_sem(cvar->signalSem) == B_INTERRUPTED) 
00136         {
00137             atomic_add( &cvar->signalBenCount, -1 );
00138             return PR_FAILURE;
00139         }
00140     }
00141     cvar->nw += 1;
00142     if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
00143     {
00144         release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
00145     }
00146 
00147     PR_Unlock( cvar->lock );
00148     if( timeout==PR_INTERVAL_NO_TIMEOUT ) 
00149     {
00150        err = acquire_sem(cvar->sem);
00151     } 
00152     else 
00153     {
00154        err = acquire_sem_etc(cvar->sem, 1, B_RELATIVE_TIMEOUT, PR_IntervalToMicroseconds(timeout) );
00155     }
00156 
00157     if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
00158     {
00159         while (acquire_sem(cvar->signalSem) == B_INTERRUPTED);
00160     }
00161 
00162     if (cvar->ns > 0)
00163     {
00164         release_sem_etc(cvar->handshakeSem, 1, B_DO_NOT_RESCHEDULE);
00165         cvar->ns -= 1;
00166     }
00167     cvar->nw -= 1;
00168     if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
00169     {
00170         release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
00171     }
00172 
00173     PR_Lock( cvar->lock );
00174     if(err!=B_NO_ERROR) 
00175     {
00176         return PR_FAILURE;
00177     }
00178     return PR_SUCCESS;
00179 }
00180 
00181 /*
00182 ** Notify ONE thread that is currently waiting on 'cvar'. Which thread is
00183 ** dependent on the implementation of the runtime. Common sense would dictate
00184 ** that all threads waiting on a single condition have identical semantics,
00185 ** therefore which one gets notified is not significant. 
00186 **
00187 ** The calling thead must hold the lock that protects the condition, as
00188 ** well as the invariants that are tightly bound to the condition, when
00189 ** notify is called.
00190 **
00191 ** Returns PR_FAILURE if the caller has not locked the lock associated
00192 ** with the condition variable.
00193 */
00194 PR_IMPLEMENT(PRStatus)
00195     PR_NotifyCondVar (PRCondVar *cvar)
00196 {
00197     status_t err ;
00198     if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
00199     {
00200         if (acquire_sem(cvar->signalSem) == B_INTERRUPTED) 
00201         {
00202             atomic_add( &cvar->signalBenCount, -1 );
00203             return PR_FAILURE;
00204         }
00205     }
00206     if (cvar->nw > cvar->ns)
00207     {
00208         cvar->ns += 1;
00209         release_sem_etc(cvar->sem, 1, B_DO_NOT_RESCHEDULE);
00210         if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
00211         {
00212             release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
00213         }
00214 
00215         while (acquire_sem(cvar->handshakeSem) == B_INTERRUPTED) 
00216         {
00217             err = B_INTERRUPTED; 
00218         }
00219     }
00220     else
00221     {
00222         if( atomic_add( &cvar->signalBenCount, -1 ) > 1 )
00223         {
00224             release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
00225         }
00226     }
00227     return PR_SUCCESS; 
00228 }
00229 
00230 /*
00231 ** Notify all of the threads waiting on the condition variable. The order
00232 ** that the threads are notified is indeterminant. The lock that protects
00233 ** the condition must be held.
00234 **
00235 ** Returns PR_FAILURE if the caller has not locked the lock associated
00236 ** with the condition variable.
00237 */
00238 PR_IMPLEMENT(PRStatus)
00239     PR_NotifyAllCondVar (PRCondVar *cvar)
00240 {
00241     int32 handshakes;
00242     status_t err = B_OK;
00243 
00244     if( atomic_add( &cvar->signalBenCount, 1 ) > 0 ) 
00245     {
00246         if (acquire_sem(cvar->signalSem) == B_INTERRUPTED) 
00247         {
00248             atomic_add( &cvar->signalBenCount, -1 );
00249             return PR_FAILURE;
00250         }
00251     }
00252 
00253     if (cvar->nw > cvar->ns)
00254     {
00255         handshakes = cvar->nw - cvar->ns;
00256         cvar->ns = cvar->nw;                            
00257         release_sem_etc(cvar->sem, handshakes, B_DO_NOT_RESCHEDULE);  
00258         if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
00259         {
00260             release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
00261         }
00262 
00263         while (acquire_sem_etc(cvar->handshakeSem, handshakes, 0, 0) == B_INTERRUPTED) 
00264         {
00265             err = B_INTERRUPTED; 
00266         }
00267     }
00268     else
00269     {
00270         if( atomic_add( &cvar->signalBenCount, -1 ) > 1 ) 
00271         {
00272             release_sem_etc(cvar->signalSem, 1, B_DO_NOT_RESCHEDULE);
00273         }
00274     }
00275     return PR_SUCCESS;
00276 }