Back to index

glibc  2.9
sem_timedwait.c
Go to the documentation of this file.
00001 /* sem_timedwait -- wait on a semaphore.  Generic futex-using version.
00002    Copyright (C) 2003, 2007 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Paul Mackerras <paulus@au.ibm.com>, 2003.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library 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 GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <errno.h>
00022 #include <sysdep.h>
00023 #include <lowlevellock.h>
00024 #include <internaltypes.h>
00025 #include <semaphore.h>
00026 
00027 #include <pthreadP.h>
00028 #include <shlib-compat.h>
00029 
00030 
00031 extern void __sem_wait_cleanup (void *arg) attribute_hidden;
00032 
00033 
00034 int
00035 sem_timedwait (sem_t *sem, const struct timespec *abstime)
00036 {
00037   struct new_sem *isem = (struct new_sem *) sem;
00038   int err;
00039 
00040   if (atomic_decrement_if_positive (&isem->value) > 0)
00041     return 0;
00042 
00043   if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
00044     {
00045       __set_errno (EINVAL);
00046       return -1;
00047     }
00048 
00049   atomic_increment (&isem->nwaiters);
00050 
00051   pthread_cleanup_push (__sem_wait_cleanup, isem);
00052 
00053   while (1)
00054     {
00055       struct timeval tv;
00056       struct timespec rt;
00057       int sec, nsec;
00058 
00059       /* Get the current time.  */
00060       __gettimeofday (&tv, NULL);
00061 
00062       /* Compute relative timeout.  */
00063       sec = abstime->tv_sec - tv.tv_sec;
00064       nsec = abstime->tv_nsec - tv.tv_usec * 1000;
00065       if (nsec < 0)
00066        {
00067          nsec += 1000000000;
00068          --sec;
00069        }
00070 
00071       /* Already timed out?  */
00072       err = -ETIMEDOUT;
00073       if (sec < 0)
00074        {
00075          __set_errno (ETIMEDOUT);
00076          err = -1;
00077          break;
00078        }
00079 
00080       /* Do wait.  */
00081       rt.tv_sec = sec;
00082       rt.tv_nsec = nsec;
00083 
00084       /* Enable asynchronous cancellation.  Required by the standard.  */
00085       int oldtype = __pthread_enable_asynccancel ();
00086 
00087       err = lll_futex_timed_wait (&isem->value, 0, &rt,
00088                               isem->private ^ FUTEX_PRIVATE_FLAG);
00089 
00090       /* Disable asynchronous cancellation.  */
00091       __pthread_disable_asynccancel (oldtype);
00092 
00093       if (err != 0 && err != -EWOULDBLOCK)
00094        {
00095          __set_errno (-err);
00096          err = -1;
00097          break;
00098        }
00099 
00100       if (atomic_decrement_if_positive (&isem->value) > 0)
00101        {
00102          err = 0;
00103          break;
00104        }
00105     }
00106 
00107   pthread_cleanup_pop (0);
00108 
00109   atomic_decrement (&isem->nwaiters);
00110 
00111   return err;
00112 }