Back to index

glibc  2.9
pthread_cond_timedwait.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003, 2004, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Martin Schwidefsky <schwidefsky@de.ibm.com>, 2003.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <endian.h>
00021 #include <errno.h>
00022 #include <sysdep.h>
00023 #include <lowlevellock.h>
00024 #include <pthread.h>
00025 #include <pthreadP.h>
00026 #include <kernel-features.h>
00027 
00028 #include <shlib-compat.h>
00029 
00030 
00031 /* Cleanup handler, defined in pthread_cond_wait.c.  */
00032 extern void __condvar_cleanup (void *arg)
00033      __attribute__ ((visibility ("hidden")));
00034 
00035 struct _condvar_cleanup_buffer
00036 {
00037   int oldtype;
00038   pthread_cond_t *cond;
00039   pthread_mutex_t *mutex;
00040   unsigned int bc_seq;
00041 };
00042 
00043 int
00044 __pthread_cond_timedwait (cond, mutex, abstime)
00045      pthread_cond_t *cond;
00046      pthread_mutex_t *mutex;
00047      const struct timespec *abstime;
00048 {
00049   struct _pthread_cleanup_buffer buffer;
00050   struct _condvar_cleanup_buffer cbuffer;
00051   int result = 0;
00052 
00053   /* Catch invalid parameters.  */
00054   if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
00055     return EINVAL;
00056 
00057   int pshared = (cond->__data.__mutex == (void *) ~0l)
00058               ? LLL_SHARED : LLL_PRIVATE;
00059 
00060   /* Make sure we are along.  */
00061   lll_lock (cond->__data.__lock, pshared);
00062 
00063   /* Now we can release the mutex.  */
00064   int err = __pthread_mutex_unlock_usercnt (mutex, 0);
00065   if (err)
00066     {
00067       lll_unlock (cond->__data.__lock, pshared);
00068       return err;
00069     }
00070 
00071   /* We have one new user of the condvar.  */
00072   ++cond->__data.__total_seq;
00073   ++cond->__data.__futex;
00074   cond->__data.__nwaiters += 1 << COND_NWAITERS_SHIFT;
00075 
00076   /* Remember the mutex we are using here.  If there is already a
00077      different address store this is a bad user bug.  Do not store
00078      anything for pshared condvars.  */
00079   if (cond->__data.__mutex != (void *) ~0l)
00080     cond->__data.__mutex = mutex;
00081 
00082   /* Prepare structure passed to cancellation handler.  */
00083   cbuffer.cond = cond;
00084   cbuffer.mutex = mutex;
00085 
00086   /* Before we block we enable cancellation.  Therefore we have to
00087      install a cancellation handler.  */
00088   __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer);
00089 
00090   /* The current values of the wakeup counter.  The "woken" counter
00091      must exceed this value.  */
00092   unsigned long long int val;
00093   unsigned long long int seq;
00094   val = seq = cond->__data.__wakeup_seq;
00095   /* Remember the broadcast counter.  */
00096   cbuffer.bc_seq = cond->__data.__broadcast_seq;
00097 
00098   while (1)
00099     {
00100       struct timespec rt;
00101       {
00102 #ifdef __NR_clock_gettime
00103        INTERNAL_SYSCALL_DECL (err);
00104        int ret;
00105        ret = INTERNAL_SYSCALL (clock_gettime, err, 2,
00106                             (cond->__data.__nwaiters
00107                              & ((1 << COND_NWAITERS_SHIFT) - 1)),
00108                             &rt);
00109 # ifndef __ASSUME_POSIX_TIMERS
00110        if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (ret, err), 0))
00111          {
00112            struct timeval tv;
00113            (void) gettimeofday (&tv, NULL);
00114 
00115            /* Convert the absolute timeout value to a relative timeout.  */
00116            rt.tv_sec = abstime->tv_sec - tv.tv_sec;
00117            rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
00118          }
00119        else
00120 # endif
00121          {
00122            /* Convert the absolute timeout value to a relative timeout.  */
00123            rt.tv_sec = abstime->tv_sec - rt.tv_sec;
00124            rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec;
00125          }
00126 #else
00127        /* Get the current time.  So far we support only one clock.  */
00128        struct timeval tv;
00129        (void) gettimeofday (&tv, NULL);
00130 
00131        /* Convert the absolute timeout value to a relative timeout.  */
00132        rt.tv_sec = abstime->tv_sec - tv.tv_sec;
00133        rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
00134 #endif
00135       }
00136       if (rt.tv_nsec < 0)
00137        {
00138          rt.tv_nsec += 1000000000;
00139          --rt.tv_sec;
00140        }
00141       /* Did we already time out?  */
00142       if (__builtin_expect (rt.tv_sec < 0, 0))
00143        {
00144          if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
00145            goto bc_out;
00146 
00147          goto timeout;
00148        }
00149 
00150       unsigned int futex_val = cond->__data.__futex;
00151 
00152       /* Prepare to wait.  Release the condvar futex.  */
00153       lll_unlock (cond->__data.__lock, pshared);
00154 
00155       /* Enable asynchronous cancellation.  Required by the standard.  */
00156       cbuffer.oldtype = __pthread_enable_asynccancel ();
00157 
00158       /* Wait until woken by signal or broadcast.  */
00159       err = lll_futex_timed_wait (&cond->__data.__futex,
00160                               futex_val, &rt, pshared);
00161 
00162       /* Disable asynchronous cancellation.  */
00163       __pthread_disable_asynccancel (cbuffer.oldtype);
00164 
00165       /* We are going to look at shared data again, so get the lock.  */
00166       lll_lock (cond->__data.__lock, pshared);
00167 
00168       /* If a broadcast happened, we are done.  */
00169       if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
00170        goto bc_out;
00171 
00172       /* Check whether we are eligible for wakeup.  */
00173       val = cond->__data.__wakeup_seq;
00174       if (val != seq && cond->__data.__woken_seq != val)
00175        break;
00176 
00177       /* Not woken yet.  Maybe the time expired?  */
00178       if (__builtin_expect (err == -ETIMEDOUT, 0))
00179        {
00180        timeout:
00181          /* Yep.  Adjust the counters.  */
00182          ++cond->__data.__wakeup_seq;
00183          ++cond->__data.__futex;
00184 
00185          /* The error value.  */
00186          result = ETIMEDOUT;
00187          break;
00188        }
00189     }
00190 
00191   /* Another thread woken up.  */
00192   ++cond->__data.__woken_seq;
00193 
00194  bc_out:
00195 
00196   cond->__data.__nwaiters -= 1 << COND_NWAITERS_SHIFT;
00197 
00198   /* If pthread_cond_destroy was called on this variable already,
00199      notify the pthread_cond_destroy caller all waiters have left
00200      and it can be successfully destroyed.  */
00201   if (cond->__data.__total_seq == -1ULL
00202       && cond->__data.__nwaiters < (1 << COND_NWAITERS_SHIFT))
00203     lll_futex_wake (&cond->__data.__nwaiters, 1, pshared);
00204 
00205   /* We are done with the condvar.  */
00206   lll_unlock (cond->__data.__lock, pshared);
00207 
00208   /* The cancellation handling is back to normal, remove the handler.  */
00209   __pthread_cleanup_pop (&buffer, 0);
00210 
00211   /* Get the mutex before returning.  */
00212   err = __pthread_mutex_cond_lock (mutex);
00213 
00214   return err ?: result;
00215 }
00216 
00217 versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
00218                 GLIBC_2_3_2);