iconv: Suppress array out of bounds warning.
[platform/upstream/glibc.git] / nptl / pthread_cond_timedwait.c
1 /* Copyright (C) 2003-2015 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 <sys/time.h>
26 #include <kernel-features.h>
27
28 #include <shlib-compat.h>
29
30 #ifndef HAVE_CLOCK_GETTIME_VSYSCALL
31 # undef INTERNAL_VSYSCALL
32 # define INTERNAL_VSYSCALL INTERNAL_SYSCALL
33 # undef INLINE_VSYSCALL
34 # define INLINE_VSYSCALL INLINE_SYSCALL
35 #else
36 # include <bits/libc-vdso.h>
37 #endif
38
39 /* Cleanup handler, defined in pthread_cond_wait.c.  */
40 extern void __condvar_cleanup (void *arg)
41      __attribute__ ((visibility ("hidden")));
42
43 struct _condvar_cleanup_buffer
44 {
45   int oldtype;
46   pthread_cond_t *cond;
47   pthread_mutex_t *mutex;
48   unsigned int bc_seq;
49 };
50
51 int
52 __pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
53                           const struct timespec *abstime)
54 {
55   struct _pthread_cleanup_buffer buffer;
56   struct _condvar_cleanup_buffer cbuffer;
57   int result = 0;
58
59   /* Catch invalid parameters.  */
60   if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
61     return EINVAL;
62
63   int pshared = (cond->__data.__mutex == (void *) ~0l)
64                 ? LLL_SHARED : LLL_PRIVATE;
65
66 #if (defined lll_futex_timed_wait_requeue_pi \
67      && defined __ASSUME_REQUEUE_PI)
68   int pi_flag = 0;
69 #endif
70
71   /* Make sure we are alone.  */
72   lll_lock (cond->__data.__lock, pshared);
73
74   /* Now we can release the mutex.  */
75   int err = __pthread_mutex_unlock_usercnt (mutex, 0);
76   if (err)
77     {
78       lll_unlock (cond->__data.__lock, pshared);
79       return err;
80     }
81
82   /* We have one new user of the condvar.  */
83   ++cond->__data.__total_seq;
84   ++cond->__data.__futex;
85   cond->__data.__nwaiters += 1 << COND_NWAITERS_SHIFT;
86
87   /* Work around the fact that the kernel rejects negative timeout values
88      despite them being valid.  */
89   if (__glibc_unlikely (abstime->tv_sec < 0))
90     goto timeout;
91
92   /* Remember the mutex we are using here.  If there is already a
93      different address store this is a bad user bug.  Do not store
94      anything for pshared condvars.  */
95   if (cond->__data.__mutex != (void *) ~0l)
96     cond->__data.__mutex = mutex;
97
98   /* Prepare structure passed to cancellation handler.  */
99   cbuffer.cond = cond;
100   cbuffer.mutex = mutex;
101
102   /* Before we block we enable cancellation.  Therefore we have to
103      install a cancellation handler.  */
104   __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer);
105
106   /* The current values of the wakeup counter.  The "woken" counter
107      must exceed this value.  */
108   unsigned long long int val;
109   unsigned long long int seq;
110   val = seq = cond->__data.__wakeup_seq;
111   /* Remember the broadcast counter.  */
112   cbuffer.bc_seq = cond->__data.__broadcast_seq;
113
114   while (1)
115     {
116 #if (!defined __ASSUME_FUTEX_CLOCK_REALTIME \
117      || !defined lll_futex_timed_wait_bitset)
118       struct timespec rt;
119       {
120 # ifdef __NR_clock_gettime
121         INTERNAL_SYSCALL_DECL (err);
122         (void) INTERNAL_VSYSCALL (clock_gettime, err, 2,
123                                   (cond->__data.__nwaiters
124                                    & ((1 << COND_NWAITERS_SHIFT) - 1)),
125                                   &rt);
126         /* Convert the absolute timeout value to a relative timeout.  */
127         rt.tv_sec = abstime->tv_sec - rt.tv_sec;
128         rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec;
129 # else
130         /* Get the current time.  So far we support only one clock.  */
131         struct timeval tv;
132         (void) __gettimeofday (&tv, NULL);
133
134         /* Convert the absolute timeout value to a relative timeout.  */
135         rt.tv_sec = abstime->tv_sec - tv.tv_sec;
136         rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
137 # endif
138       }
139       if (rt.tv_nsec < 0)
140         {
141           rt.tv_nsec += 1000000000;
142           --rt.tv_sec;
143         }
144       /* Did we already time out?  */
145       if (__glibc_unlikely (rt.tv_sec < 0))
146         {
147           if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
148             goto bc_out;
149
150           goto timeout;
151         }
152 #endif
153
154       unsigned int futex_val = cond->__data.__futex;
155
156       /* Prepare to wait.  Release the condvar futex.  */
157       lll_unlock (cond->__data.__lock, pshared);
158
159       /* Enable asynchronous cancellation.  Required by the standard.  */
160       cbuffer.oldtype = __pthread_enable_asynccancel ();
161
162 /* REQUEUE_PI was implemented after FUTEX_CLOCK_REALTIME, so it is sufficient
163    to check just the former.  */
164 #if (defined lll_futex_timed_wait_requeue_pi \
165      && defined __ASSUME_REQUEUE_PI)
166       /* If pi_flag remained 1 then it means that we had the lock and the mutex
167          but a spurious waker raced ahead of us.  Give back the mutex before
168          going into wait again.  */
169       if (pi_flag)
170         {
171           __pthread_mutex_cond_lock_adjust (mutex);
172           __pthread_mutex_unlock_usercnt (mutex, 0);
173         }
174       pi_flag = USE_REQUEUE_PI (mutex);
175
176       if (pi_flag)
177         {
178           unsigned int clockbit = (cond->__data.__nwaiters & 1
179                                    ? 0 : FUTEX_CLOCK_REALTIME);
180           err = lll_futex_timed_wait_requeue_pi (&cond->__data.__futex,
181                                                  futex_val, abstime, clockbit,
182                                                  &mutex->__data.__lock,
183                                                  pshared);
184           pi_flag = (err == 0);
185         }
186       else
187 #endif
188
189         {
190 #if (!defined __ASSUME_FUTEX_CLOCK_REALTIME \
191      || !defined lll_futex_timed_wait_bitset)
192           /* Wait until woken by signal or broadcast.  */
193           err = lll_futex_timed_wait (&cond->__data.__futex,
194                                       futex_val, &rt, pshared);
195 #else
196           unsigned int clockbit = (cond->__data.__nwaiters & 1
197                                    ? 0 : FUTEX_CLOCK_REALTIME);
198           err = lll_futex_timed_wait_bitset (&cond->__data.__futex, futex_val,
199                                              abstime, clockbit, pshared);
200 #endif
201         }
202
203       /* Disable asynchronous cancellation.  */
204       __pthread_disable_asynccancel (cbuffer.oldtype);
205
206       /* We are going to look at shared data again, so get the lock.  */
207       lll_lock (cond->__data.__lock, pshared);
208
209       /* If a broadcast happened, we are done.  */
210       if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
211         goto bc_out;
212
213       /* Check whether we are eligible for wakeup.  */
214       val = cond->__data.__wakeup_seq;
215       if (val != seq && cond->__data.__woken_seq != val)
216         break;
217
218       /* Not woken yet.  Maybe the time expired?  */
219       if (__glibc_unlikely (err == -ETIMEDOUT))
220         {
221         timeout:
222           /* Yep.  Adjust the counters.  */
223           ++cond->__data.__wakeup_seq;
224           ++cond->__data.__futex;
225
226           /* The error value.  */
227           result = ETIMEDOUT;
228           break;
229         }
230     }
231
232   /* Another thread woken up.  */
233   ++cond->__data.__woken_seq;
234
235  bc_out:
236
237   cond->__data.__nwaiters -= 1 << COND_NWAITERS_SHIFT;
238
239   /* If pthread_cond_destroy was called on this variable already,
240      notify the pthread_cond_destroy caller all waiters have left
241      and it can be successfully destroyed.  */
242   if (cond->__data.__total_seq == -1ULL
243       && cond->__data.__nwaiters < (1 << COND_NWAITERS_SHIFT))
244     lll_futex_wake (&cond->__data.__nwaiters, 1, pshared);
245
246   /* We are done with the condvar.  */
247   lll_unlock (cond->__data.__lock, pshared);
248
249   /* The cancellation handling is back to normal, remove the handler.  */
250   __pthread_cleanup_pop (&buffer, 0);
251
252   /* Get the mutex before returning.  */
253 #if (defined lll_futex_timed_wait_requeue_pi \
254      && defined __ASSUME_REQUEUE_PI)
255   if (pi_flag)
256     {
257       __pthread_mutex_cond_lock_adjust (mutex);
258       err = 0;
259     }
260   else
261 #endif
262     err = __pthread_mutex_cond_lock (mutex);
263
264   return err ?: result;
265 }
266
267 versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
268                   GLIBC_2_3_2);