[BZ #163]
[platform/upstream/glibc.git] / nptl / sysdeps / pthread / pthread_cond_timedwait.c
1 /* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Martin Schwidefsky <schwidefsky@de.ibm.com>, 2003.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <endian.h>
21 #include <errno.h>
22 #include <sysdep.h>
23 #include <lowlevellock.h>
24 #include <pthread.h>
25 #include <pthreadP.h>
26
27 #include <shlib-compat.h>
28
29
30 /* Cleanup handler, defined in pthread_cond_wait.c.  */
31 extern void __condvar_cleanup (void *arg)
32      __attribute__ ((visibility ("hidden")));
33
34 struct _condvar_cleanup_buffer
35 {
36   int oldtype;
37   pthread_cond_t *cond;
38   pthread_mutex_t *mutex;
39   unsigned int bc_seq;
40 };
41
42 int
43 __pthread_cond_timedwait (cond, mutex, abstime)
44      pthread_cond_t *cond;
45      pthread_mutex_t *mutex;
46      const struct timespec *abstime;
47 {
48   struct _pthread_cleanup_buffer buffer;
49   struct _condvar_cleanup_buffer cbuffer;
50   int result = 0;
51
52   /* Catch invalid parameters.  */
53   if (abstime->tv_nsec >= 1000000000)
54     return EINVAL;
55
56   /* Make sure we are along.  */
57   lll_mutex_lock (cond->__data.__lock);
58
59   /* Now we can release the mutex.  */
60   int err = __pthread_mutex_unlock_usercnt (mutex, 0);
61   if (err)
62     {
63       lll_mutex_unlock (cond->__data.__lock);
64       return err;
65     }
66
67   /* We have one new user of the condvar.  */
68   ++cond->__data.__total_seq;
69
70   /* Remember the mutex we are using here.  If there is already a
71      different address store this is a bad user bug.  Do not store
72      anything for pshared condvars.  */
73   if (cond->__data.__mutex != (void *) ~0l)
74     cond->__data.__mutex = mutex;
75
76   /* Prepare structure passed to cancellation handler.  */
77   cbuffer.cond = cond;
78   cbuffer.mutex = mutex;
79
80   /* Before we block we enable cancellation.  Therefore we have to
81      install a cancellation handler.  */
82   __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer);
83
84   /* The current values of the wakeup counter.  The "woken" counter
85      must exceed this value.  */
86   unsigned long long int val;
87   unsigned long long int seq;
88   val = seq = cond->__data.__wakeup_seq;
89   /* Remember the broadcast counter.  */
90   cbuffer.bc_seq = cond->__data.__broadcast_seq;
91
92   /* The futex syscall operates on a 32-bit word.  That is fine, we
93      just use the low 32 bits of the sequence counter.  */
94 #if BYTE_ORDER == LITTLE_ENDIAN
95   int *futex = ((int *) (&cond->__data.__wakeup_seq));
96 #elif BYTE_ORDER == BIG_ENDIAN
97   int *futex = ((int *) (&cond->__data.__wakeup_seq)) + 1;
98 #else
99 # error "No valid byte order"
100 #endif
101
102   while (1)
103     {
104       struct timespec rt;
105       {
106 #ifdef __NR_clock_gettime
107         INTERNAL_SYSCALL_DECL (err);
108         int val;
109         val = INTERNAL_SYSCALL (clock_gettime, err, 2,
110                                 cond->__data.__clock, &rt);
111 # ifndef __ASSUME_POSIX_TIMERS
112         if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (val, err), 0))
113           {
114             struct timeval tv;
115             (void) gettimeofday (&tv, NULL);
116
117             /* Convert the absolute timeout value to a relative timeout.  */
118             rt.tv_sec = abstime->tv_sec - tv.tv_sec;
119             rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
120           }
121         else
122 # endif
123           {
124             /* Convert the absolute timeout value to a relative timeout.  */
125             rt.tv_sec = abstime->tv_sec - rt.tv_sec;
126             rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec;
127           }
128 #else
129         /* Get the current time.  So far we support only one clock.  */
130         struct timeval tv;
131         (void) gettimeofday (&tv, NULL);
132
133         /* Convert the absolute timeout value to a relative timeout.  */
134         rt.tv_sec = abstime->tv_sec - tv.tv_sec;
135         rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
136 #endif
137       }
138       if (rt.tv_nsec < 0)
139         {
140           rt.tv_nsec += 1000000000;
141           --rt.tv_sec;
142         }
143       /* Did we already time out?  */
144       if (__builtin_expect (rt.tv_sec < 0, 0))
145         {
146           if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
147             goto bc_out;
148
149           goto timeout;
150         }
151
152       /* Prepare to wait.  Release the condvar futex.  */
153       lll_mutex_unlock (cond->__data.__lock);
154
155       /* Enable asynchronous cancellation.  Required by the standard.  */
156       cbuffer.oldtype = __pthread_enable_asynccancel ();
157
158       /* Wait until woken by signal or broadcast.  Note that we
159          truncate the 'val' value to 32 bits.  */
160       err = lll_futex_timed_wait (futex, (unsigned int) val, &rt);
161
162       /* Disable asynchronous cancellation.  */
163       __pthread_disable_asynccancel (cbuffer.oldtype);
164
165       /* We are going to look at shared data again, so get the lock.  */
166       lll_mutex_lock(cond->__data.__lock);
167
168       /* If a broadcast happened, we are done.  */
169       if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
170         goto bc_out;
171
172       /* Check whether we are eligible for wakeup.  */
173       val = cond->__data.__wakeup_seq;
174       if (val != seq && cond->__data.__woken_seq != val)
175         break;
176
177       /* Not woken yet.  Maybe the time expired?  */
178       if (__builtin_expect (err == -ETIMEDOUT, 0))
179         {
180         timeout:
181           /* Yep.  Adjust the counters.  */
182           ++cond->__data.__wakeup_seq;
183
184           /* The error value.  */
185           result = ETIMEDOUT;
186           break;
187         }
188     }
189
190   /* Another thread woken up.  */
191   ++cond->__data.__woken_seq;
192
193  bc_out:
194   /* We are done with the condvar.  */
195   lll_mutex_unlock (cond->__data.__lock);
196
197   /* The cancellation handling is back to normal, remove the handler.  */
198   __pthread_cleanup_pop (&buffer, 0);
199
200   /* Get the mutex before returning.  */
201   err = __pthread_mutex_cond_lock (mutex);
202
203   return err ?: result;
204 }
205
206 versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
207                   GLIBC_2_3_2);