1 /* sem_timedwait -- wait on a semaphore. Generic futex-using version.
2 Copyright (C) 2003, 2007 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Paul Mackerras <paulus@au.ibm.com>, 2003.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
19
20 #include <errno.h>
21 #include <sysdep.h>
22 #include <lowlevellock.h>
23 #include <internaltypes.h>
24 #include <semaphore.h>
25
26 #include <pthreadP.h>
27
28
29 extern void __sem_wait_cleanup (void *arg) attribute_hidden;
30
31
32 int
sem_timedwait(sem_t * sem,const struct timespec * abstime)33 sem_timedwait (sem_t *sem, const struct timespec *abstime)
34 {
35 struct new_sem *isem = (struct new_sem *) sem;
36 int err;
37
38 if (atomic_decrement_if_positive (&isem->value) > 0)
39 return 0;
40
41 if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
42 {
43 __set_errno (EINVAL);
44 return -1;
45 }
46
47 atomic_increment (&isem->nwaiters);
48
49 pthread_cleanup_push (__sem_wait_cleanup, isem);
50
51 while (1)
52 {
53 struct timeval tv;
54 struct timespec rt;
55 int sec, nsec;
56
57 /* Get the current time. */
58 __gettimeofday (&tv, NULL);
59
60 /* Compute relative timeout. */
61 sec = abstime->tv_sec - tv.tv_sec;
62 nsec = abstime->tv_nsec - tv.tv_usec * 1000;
63 if (nsec < 0)
64 {
65 nsec += 1000000000;
66 --sec;
67 }
68
69 /* Already timed out? */
70 err = -ETIMEDOUT;
71 if (sec < 0)
72 {
73 __set_errno (ETIMEDOUT);
74 err = -1;
75 break;
76 }
77
78 /* Do wait. */
79 rt.tv_sec = sec;
80 rt.tv_nsec = nsec;
81
82 /* Enable asynchronous cancellation. Required by the standard. */
83 int oldtype = __pthread_enable_asynccancel ();
84
85 err = lll_futex_timed_wait (&isem->value, 0, &rt,
86 isem->private ^ FUTEX_PRIVATE_FLAG);
87
88 /* Disable asynchronous cancellation. */
89 __pthread_disable_asynccancel (oldtype);
90
91 if (err != 0 && err != -EWOULDBLOCK)
92 {
93 __set_errno (-err);
94 err = -1;
95 break;
96 }
97
98 if (atomic_decrement_if_positive (&isem->value) > 0)
99 {
100 err = 0;
101 break;
102 }
103 }
104
105 pthread_cleanup_pop (0);
106
107 atomic_decrement (&isem->nwaiters);
108
109 return err;
110 }
111