1 /* sem_timedwait -- wait on a semaphore. SPARC version.
2 Copyright (C) 2003, 2006, 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 sparc_new_sem *isem = (struct sparc_new_sem *) sem;
36 int err;
37 int val;
38
39 if (__atomic_is_v9)
40 val = atomic_decrement_if_positive (&isem->value);
41 else
42 {
43 __sparc32_atomic_do_lock24 (&isem->lock);
44 val = isem->value;
45 if (val > 0)
46 isem->value = val - 1;
47 __sparc32_atomic_do_unlock24 (&isem->lock);
48 }
49
50 if (val > 0)
51 return 0;
52
53 if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
54 {
55 __set_errno (EINVAL);
56 return -1;
57 }
58
59 if (__atomic_is_v9)
60 atomic_increment (&isem->nwaiters);
61 else
62 {
63 __sparc32_atomic_do_lock24 (&isem->lock);
64 isem->nwaiters++;
65 __sparc32_atomic_do_unlock24 (&isem->lock);
66 }
67
68 pthread_cleanup_push (__sem_wait_cleanup, isem);
69
70 while (1)
71 {
72 struct timeval tv;
73 struct timespec rt;
74 int sec, nsec;
75
76 /* Get the current time. */
77 __gettimeofday (&tv, NULL);
78
79 /* Compute relative timeout. */
80 sec = abstime->tv_sec - tv.tv_sec;
81 nsec = abstime->tv_nsec - tv.tv_usec * 1000;
82 if (nsec < 0)
83 {
84 nsec += 1000000000;
85 --sec;
86 }
87
88 /* Already timed out? */
89 err = -ETIMEDOUT;
90 if (sec < 0)
91 {
92 __set_errno (ETIMEDOUT);
93 err = -1;
94 break;
95 }
96
97 /* Do wait. */
98 rt.tv_sec = sec;
99 rt.tv_nsec = nsec;
100
101 /* Enable asynchronous cancellation. Required by the standard. */
102 int oldtype = __pthread_enable_asynccancel ();
103
104 err = lll_futex_timed_wait (&isem->value, 0, &rt,
105 isem->private ^ FUTEX_PRIVATE_FLAG);
106
107 /* Disable asynchronous cancellation. */
108 __pthread_disable_asynccancel (oldtype);
109
110 if (err != 0 && err != -EWOULDBLOCK)
111 {
112 __set_errno (-err);
113 err = -1;
114 break;
115 }
116
117 if (__atomic_is_v9)
118 val = atomic_decrement_if_positive (&isem->value);
119 else
120 {
121 __sparc32_atomic_do_lock24 (&isem->lock);
122 val = isem->value;
123 if (val > 0)
124 isem->value = val - 1;
125 __sparc32_atomic_do_unlock24 (&isem->lock);
126 }
127
128 if (val > 0)
129 {
130 err = 0;
131 break;
132 }
133 }
134
135 pthread_cleanup_pop (0);
136
137 if (__atomic_is_v9)
138 atomic_decrement (&isem->nwaiters);
139 else
140 {
141 __sparc32_atomic_do_lock24 (&isem->lock);
142 isem->nwaiters--;
143 __sparc32_atomic_do_unlock24 (&isem->lock);
144 }
145
146 return err;
147 }
148