Back to index

glibc  2.9
condvar.c
Go to the documentation of this file.
00001 /* Linuxthreads - a simple clone()-based implementation of Posix        */
00002 /* threads for Linux.                                                   */
00003 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
00004 /* and Pavel Krauz (krauz@fsid.cvut.cz).                                */
00005 /*                                                                      */
00006 /* This program is free software; you can redistribute it and/or        */
00007 /* modify it under the terms of the GNU Library General Public License  */
00008 /* as published by the Free Software Foundation; either version 2       */
00009 /* of the License, or (at your option) any later version.               */
00010 /*                                                                      */
00011 /* This program is distributed in the hope that it will be useful,      */
00012 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
00013 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
00014 /* GNU Library General Public License for more details.                 */
00015 
00016 /* Condition variables */
00017 
00018 #include <errno.h>
00019 #include <sched.h>
00020 #include <stddef.h>
00021 #include <sys/time.h>
00022 #include "pthread.h"
00023 #include "internals.h"
00024 #include "spinlock.h"
00025 #include "queue.h"
00026 #include "restart.h"
00027 #include <shlib-compat.h>
00028 
00029 int __pthread_cond_init(pthread_cond_t *cond,
00030                         const pthread_condattr_t *cond_attr)
00031 {
00032   __pthread_init_lock(&cond->__c_lock);
00033   cond->__c_waiting = NULL;
00034   return 0;
00035 }
00036 versioned_symbol (libpthread, __pthread_cond_init, pthread_cond_init,
00037                 GLIBC_2_3_2);
00038 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
00039 strong_alias (__pthread_cond_init, __old_pthread_cond_init)
00040 compat_symbol (libpthread, __old_pthread_cond_init, pthread_cond_init,
00041               GLIBC_2_0);
00042 #endif
00043 
00044 int __pthread_cond_destroy(pthread_cond_t *cond)
00045 {
00046   if (cond->__c_waiting != NULL) return EBUSY;
00047   return 0;
00048 }
00049 versioned_symbol (libpthread, __pthread_cond_destroy, pthread_cond_destroy,
00050                 GLIBC_2_3_2);
00051 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
00052 strong_alias (__pthread_cond_destroy, __old_pthread_cond_destroy)
00053 compat_symbol (libpthread, __old_pthread_cond_destroy, pthread_cond_destroy,
00054               GLIBC_2_0);
00055 #endif
00056 
00057 /* Function called by pthread_cancel to remove the thread from
00058    waiting on a condition variable queue. */
00059 
00060 static int cond_extricate_func(void *obj, pthread_descr th)
00061 {
00062   volatile pthread_descr self = thread_self();
00063   pthread_cond_t *cond = obj;
00064   int did_remove = 0;
00065 
00066   __pthread_lock(&cond->__c_lock, self);
00067   did_remove = remove_from_queue(&cond->__c_waiting, th);
00068   __pthread_unlock(&cond->__c_lock);
00069 
00070   return did_remove;
00071 }
00072 
00073 int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
00074 {
00075   volatile pthread_descr self = thread_self();
00076   pthread_extricate_if extr;
00077   int already_canceled = 0;
00078   int spurious_wakeup_count;
00079 
00080   /* Check whether the mutex is locked and owned by this thread.  */
00081   if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
00082       && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP
00083       && mutex->__m_owner != self)
00084     return EINVAL;
00085 
00086   /* Set up extrication interface */
00087   extr.pu_object = cond;
00088   extr.pu_extricate_func = cond_extricate_func;
00089 
00090   /* Register extrication interface */
00091   THREAD_SETMEM(self, p_condvar_avail, 0);
00092   __pthread_set_own_extricate_if(self, &extr);
00093 
00094   /* Atomically enqueue thread for waiting, but only if it is not
00095      canceled. If the thread is canceled, then it will fall through the
00096      suspend call below, and then call pthread_exit without
00097      having to worry about whether it is still on the condition variable queue.
00098      This depends on pthread_cancel setting p_canceled before calling the
00099      extricate function. */
00100 
00101   __pthread_lock(&cond->__c_lock, self);
00102   if (!(THREAD_GETMEM(self, p_canceled)
00103       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
00104     enqueue(&cond->__c_waiting, self);
00105   else
00106     already_canceled = 1;
00107   __pthread_unlock(&cond->__c_lock);
00108 
00109   if (already_canceled) {
00110     __pthread_set_own_extricate_if(self, 0);
00111     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00112   }
00113 
00114   pthread_mutex_unlock(mutex);
00115 
00116   spurious_wakeup_count = 0;
00117   while (1)
00118     {
00119       suspend(self);
00120       if (THREAD_GETMEM(self, p_condvar_avail) == 0
00121          && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
00122              || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
00123        {
00124          /* Count resumes that don't belong to us. */
00125          spurious_wakeup_count++;
00126          continue;
00127        }
00128       break;
00129     }
00130 
00131   __pthread_set_own_extricate_if(self, 0);
00132 
00133   /* Check for cancellation again, to provide correct cancellation
00134      point behavior */
00135 
00136   if (THREAD_GETMEM(self, p_woken_by_cancel)
00137       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
00138     THREAD_SETMEM(self, p_woken_by_cancel, 0);
00139     pthread_mutex_lock(mutex);
00140     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00141   }
00142 
00143   /* Put back any resumes we caught that don't belong to us. */
00144   while (spurious_wakeup_count--)
00145     restart(self);
00146 
00147   pthread_mutex_lock(mutex);
00148   return 0;
00149 }
00150 versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
00151                 GLIBC_2_3_2);
00152 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
00153 strong_alias (__pthread_cond_wait, __old_pthread_cond_wait)
00154 compat_symbol (libpthread, __old_pthread_cond_wait, pthread_cond_wait,
00155               GLIBC_2_0);
00156 #endif
00157 
00158 static int
00159 pthread_cond_timedwait_relative(pthread_cond_t *cond,
00160                             pthread_mutex_t *mutex,
00161                             const struct timespec * abstime)
00162 {
00163   volatile pthread_descr self = thread_self();
00164   int already_canceled = 0;
00165   pthread_extricate_if extr;
00166   int spurious_wakeup_count;
00167 
00168   /* Check whether the mutex is locked and owned by this thread.  */
00169   if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
00170       && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP
00171       && mutex->__m_owner != self)
00172     return EINVAL;
00173 
00174   /* Set up extrication interface */
00175   extr.pu_object = cond;
00176   extr.pu_extricate_func = cond_extricate_func;
00177 
00178   /* Register extrication interface */
00179   THREAD_SETMEM(self, p_condvar_avail, 0);
00180   __pthread_set_own_extricate_if(self, &extr);
00181 
00182   /* Enqueue to wait on the condition and check for cancellation. */
00183   __pthread_lock(&cond->__c_lock, self);
00184   if (!(THREAD_GETMEM(self, p_canceled)
00185       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
00186     enqueue(&cond->__c_waiting, self);
00187   else
00188     already_canceled = 1;
00189   __pthread_unlock(&cond->__c_lock);
00190 
00191   if (already_canceled) {
00192     __pthread_set_own_extricate_if(self, 0);
00193     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00194   }
00195 
00196   pthread_mutex_unlock(mutex);
00197 
00198   spurious_wakeup_count = 0;
00199   while (1)
00200     {
00201       if (!timedsuspend(self, abstime)) {
00202        int was_on_queue;
00203 
00204        /* __pthread_lock will queue back any spurious restarts that
00205           may happen to it. */
00206 
00207        __pthread_lock(&cond->__c_lock, self);
00208        was_on_queue = remove_from_queue(&cond->__c_waiting, self);
00209        __pthread_unlock(&cond->__c_lock);
00210 
00211        if (was_on_queue) {
00212          __pthread_set_own_extricate_if(self, 0);
00213          pthread_mutex_lock(mutex);
00214          return ETIMEDOUT;
00215        }
00216 
00217        /* Eat the outstanding restart() from the signaller */
00218        suspend(self);
00219       }
00220 
00221       if (THREAD_GETMEM(self, p_condvar_avail) == 0
00222          && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
00223              || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
00224        {
00225          /* Count resumes that don't belong to us. */
00226          spurious_wakeup_count++;
00227          continue;
00228        }
00229       break;
00230     }
00231 
00232   __pthread_set_own_extricate_if(self, 0);
00233 
00234   /* The remaining logic is the same as in other cancellable waits,
00235      such as pthread_join sem_wait or pthread_cond wait. */
00236 
00237   if (THREAD_GETMEM(self, p_woken_by_cancel)
00238       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
00239     THREAD_SETMEM(self, p_woken_by_cancel, 0);
00240     pthread_mutex_lock(mutex);
00241     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00242   }
00243 
00244   /* Put back any resumes we caught that don't belong to us. */
00245   while (spurious_wakeup_count--)
00246     restart(self);
00247 
00248   pthread_mutex_lock(mutex);
00249   return 0;
00250 }
00251 
00252 int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
00253                           const struct timespec * abstime)
00254 {
00255   /* Indirect call through pointer! */
00256   return pthread_cond_timedwait_relative(cond, mutex, abstime);
00257 }
00258 versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
00259                 GLIBC_2_3_2);
00260 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
00261 strong_alias (__pthread_cond_timedwait, __old_pthread_cond_timedwait)
00262 compat_symbol (libpthread, __old_pthread_cond_timedwait,
00263               pthread_cond_timedwait, GLIBC_2_0);
00264 #endif
00265 
00266 int __pthread_cond_signal(pthread_cond_t *cond)
00267 {
00268   pthread_descr th;
00269 
00270   __pthread_lock(&cond->__c_lock, NULL);
00271   th = dequeue(&cond->__c_waiting);
00272   __pthread_unlock(&cond->__c_lock);
00273   if (th != NULL) {
00274     th->p_condvar_avail = 1;
00275     WRITE_MEMORY_BARRIER();
00276     restart(th);
00277   }
00278   return 0;
00279 }
00280 versioned_symbol (libpthread, __pthread_cond_signal, pthread_cond_signal,
00281                 GLIBC_2_3_2);
00282 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
00283 strong_alias (__pthread_cond_signal, __old_pthread_cond_signal)
00284 compat_symbol (libpthread, __old_pthread_cond_signal, pthread_cond_signal,
00285               GLIBC_2_0);
00286 #endif
00287 
00288 int __pthread_cond_broadcast(pthread_cond_t *cond)
00289 {
00290   pthread_descr tosignal, th;
00291 
00292   __pthread_lock(&cond->__c_lock, NULL);
00293   /* Copy the current state of the waiting queue and empty it */
00294   tosignal = cond->__c_waiting;
00295   cond->__c_waiting = NULL;
00296   __pthread_unlock(&cond->__c_lock);
00297   /* Now signal each process in the queue */
00298   while ((th = dequeue(&tosignal)) != NULL) {
00299     th->p_condvar_avail = 1;
00300     WRITE_MEMORY_BARRIER();
00301     restart(th);
00302   }
00303   return 0;
00304 }
00305 versioned_symbol (libpthread, __pthread_cond_broadcast, pthread_cond_broadcast,
00306                 GLIBC_2_3_2);
00307 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
00308 strong_alias (__pthread_cond_broadcast, __old_pthread_cond_broadcast)
00309 compat_symbol (libpthread, __old_pthread_cond_broadcast,
00310               pthread_cond_broadcast, GLIBC_2_0);
00311 #endif
00312 
00313 int __pthread_condattr_init(pthread_condattr_t *attr)
00314 {
00315   return 0;
00316 }
00317 strong_alias (__pthread_condattr_init, pthread_condattr_init)
00318 
00319 int __pthread_condattr_destroy(pthread_condattr_t *attr)
00320 {
00321   return 0;
00322 }
00323 strong_alias (__pthread_condattr_destroy, pthread_condattr_destroy)
00324 
00325 int pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared)
00326 {
00327   *pshared = PTHREAD_PROCESS_PRIVATE;
00328   return 0;
00329 }
00330 
00331 int pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared)
00332 {
00333   if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
00334     return EINVAL;
00335 
00336   /* For now it is not possible to shared a conditional variable.  */
00337   if (pshared != PTHREAD_PROCESS_PRIVATE)
00338     return ENOSYS;
00339 
00340   return 0;
00341 }