1 /* Copyright (C) 2003, 2004, 2007 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, see
17 <http://www.gnu.org/licenses/>. */
18
19 #include <endian.h>
20 #include <errno.h>
21 #include <sysdep.h>
22 #include <lowlevellock.h>
23 #include <pthread.h>
24 #include <pthreadP.h>
25 #include <bits/kernel-features.h>
26
27
28 /* Cleanup handler, defined in pthread_cond_wait.c. */
29 extern void __condvar_cleanup (void *arg)
30 __attribute__ ((visibility ("hidden")));
31
32 struct _condvar_cleanup_buffer
33 {
34 int oldtype;
35 pthread_cond_t *cond;
36 pthread_mutex_t *mutex;
37 unsigned int bc_seq;
38 };
39
40 int
41 attribute_protected
__pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)42 __pthread_cond_timedwait (
43 pthread_cond_t *cond,
44 pthread_mutex_t *mutex,
45 const struct timespec *abstime)
46 {
47 struct _pthread_cleanup_buffer buffer;
48 struct _condvar_cleanup_buffer cbuffer;
49 int result = 0;
50
51 /* Catch invalid parameters. */
52 if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
53 return EINVAL;
54
55 int pshared = (cond->__data.__mutex == (void *) ~0l)
56 ? LLL_SHARED : LLL_PRIVATE;
57
58 /* Make sure we are along. */
59 lll_lock (cond->__data.__lock, pshared);
60
61 /* Now we can release the mutex. */
62 int err = __pthread_mutex_unlock_usercnt (mutex, 0);
63 if (err)
64 {
65 lll_unlock (cond->__data.__lock, pshared);
66 return err;
67 }
68
69 /* We have one new user of the condvar. */
70 ++cond->__data.__total_seq;
71 ++cond->__data.__futex;
72 cond->__data.__nwaiters += 1 << COND_NWAITERS_SHIFT;
73
74 /* Remember the mutex we are using here. If there is already a
75 different address store this is a bad user bug. Do not store
76 anything for pshared condvars. */
77 if (cond->__data.__mutex != (void *) ~0l)
78 cond->__data.__mutex = mutex;
79
80 /* Prepare structure passed to cancellation handler. */
81 cbuffer.cond = cond;
82 cbuffer.mutex = mutex;
83
84 /* Before we block we enable cancellation. Therefore we have to
85 install a cancellation handler. */
86 __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer);
87
88 /* The current values of the wakeup counter. The "woken" counter
89 must exceed this value. */
90 unsigned long long int val;
91 unsigned long long int seq;
92 val = seq = cond->__data.__wakeup_seq;
93 /* Remember the broadcast counter. */
94 cbuffer.bc_seq = cond->__data.__broadcast_seq;
95
96 while (1)
97 {
98 struct timespec rt = {.tv_sec = 0, .tv_nsec = 0};
99 #if defined(__UCLIBC_USE_TIME64__)
100 struct __ts64_struct __rt64;
101 #endif
102 {
103 #ifdef __NR_clock_gettime
104 INTERNAL_SYSCALL_DECL (err);
105 # if !defined(__ASSUME_POSIX_TIMERS) || defined(__UCLIBC_USE_TIME64__)
106 int ret =
107 # endif
108 #if defined(__UCLIBC_USE_TIME64__) && defined(__NR_clock_gettime64)
109 INTERNAL_SYSCALL (clock_gettime64, err, 2,
110 (cond->__data.__nwaiters
111 & ((1 << COND_NWAITERS_SHIFT) - 1)),
112 &__rt64);
113
114 if (ret == 0) {
115 rt.tv_sec = __rt64.tv_sec;
116 rt.tv_nsec = __rt64.tv_nsec;
117 }
118
119 #else
120 INTERNAL_SYSCALL (clock_gettime, err, 2,
121 (cond->__data.__nwaiters
122 & ((1 << COND_NWAITERS_SHIFT) - 1)),
123 &rt);
124 #endif
125
126 # ifndef __ASSUME_POSIX_TIMERS
127 if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (ret, err), 0))
128 {
129 struct timeval tv;
130 (void) gettimeofday (&tv, NULL);
131
132 /* Convert the absolute timeout value to a relative timeout. */
133 rt.tv_sec = abstime->tv_sec - tv.tv_sec;
134 rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
135 }
136 else
137 # endif
138 {
139 /* Convert the absolute timeout value to a relative timeout. */
140 rt.tv_sec = abstime->tv_sec - rt.tv_sec;
141 rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec;
142 }
143 #else
144 /* Get the current time. So far we support only one clock. */
145 struct timeval tv;
146 (void) gettimeofday (&tv, NULL);
147
148 /* Convert the absolute timeout value to a relative timeout. */
149 rt.tv_sec = abstime->tv_sec - tv.tv_sec;
150 rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
151 #endif
152 }
153 if (rt.tv_nsec < 0)
154 {
155 rt.tv_nsec += 1000000000;
156 --rt.tv_sec;
157 }
158 /* Did we already time out? */
159 if (__builtin_expect (rt.tv_sec < 0, 0))
160 {
161 if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
162 goto bc_out;
163
164 goto timeout;
165 }
166
167 unsigned int futex_val = cond->__data.__futex;
168
169 /* Prepare to wait. Release the condvar futex. */
170 lll_unlock (cond->__data.__lock, pshared);
171
172 /* Enable asynchronous cancellation. Required by the standard. */
173 cbuffer.oldtype = __pthread_enable_asynccancel ();
174
175 /* Wait until woken by signal or broadcast. */
176 err = lll_futex_timed_wait (&cond->__data.__futex,
177 futex_val, &rt, pshared);
178
179 /* Disable asynchronous cancellation. */
180 __pthread_disable_asynccancel (cbuffer.oldtype);
181
182 /* We are going to look at shared data again, so get the lock. */
183 lll_lock (cond->__data.__lock, pshared);
184
185 /* If a broadcast happened, we are done. */
186 if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
187 goto bc_out;
188
189 /* Check whether we are eligible for wakeup. */
190 val = cond->__data.__wakeup_seq;
191 if (val != seq && cond->__data.__woken_seq != val)
192 break;
193
194 /* Not woken yet. Maybe the time expired? */
195 if (__builtin_expect (err == -ETIMEDOUT, 0))
196 {
197 timeout:
198 /* Yep. Adjust the counters. */
199 ++cond->__data.__wakeup_seq;
200 ++cond->__data.__futex;
201
202 /* The error value. */
203 result = ETIMEDOUT;
204 break;
205 }
206 }
207
208 /* Another thread woken up. */
209 ++cond->__data.__woken_seq;
210
211 bc_out:
212
213 cond->__data.__nwaiters -= 1 << COND_NWAITERS_SHIFT;
214
215 /* If pthread_cond_destroy was called on this variable already,
216 notify the pthread_cond_destroy caller all waiters have left
217 and it can be successfully destroyed. */
218 if (cond->__data.__total_seq == -1ULL
219 && cond->__data.__nwaiters < (1 << COND_NWAITERS_SHIFT))
220 lll_futex_wake (&cond->__data.__nwaiters, 1, pshared);
221
222 /* We are done with the condvar. */
223 lll_unlock (cond->__data.__lock, pshared);
224
225 /* The cancellation handling is back to normal, remove the handler. */
226 __pthread_cleanup_pop (&buffer, 0);
227
228 /* Get the mutex before returning. */
229 err = __pthread_mutex_cond_lock (mutex);
230
231 return err ?: result;
232 }
233
234 weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait)
235