1 /* Wait on a semaphore with a timeout. Generic version.
2 Copyright (C) 2005-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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 <https://www.gnu.org/licenses/>. */
19 #include <semaphore.h>
24 #include <hurd/hurd.h>
25 #include <sysdep-cancel.h>
27 #include <pt-internal.h>
29 #if !__HAVE_64B_ATOMICS
31 __sem_wait_32_finish (struct new_sem
*isem
);
35 __sem_wait_cleanup (void *arg
)
37 struct new_sem
*isem
= arg
;
39 #if __HAVE_64B_ATOMICS
40 atomic_fetch_add_relaxed (&isem
->data
, -((uint64_t) 1 << SEM_NWAITERS_SHIFT
));
42 __sem_wait_32_finish (isem
);
47 __sem_timedwait_internal (sem_t
*restrict sem
,
49 const struct timespec
*restrict timeout
)
51 struct new_sem
*isem
= (struct new_sem
*) sem
;
53 int flags
= isem
->pshared
? GSYNC_SHARED
: 0;
55 __pthread_testcancel ();
57 if (__sem_waitfast (isem
, 0) == 0)
60 int cancel_oldtype
= LIBC_CANCEL_ASYNC();
62 #if __HAVE_64B_ATOMICS
63 uint64_t d
= atomic_fetch_add_relaxed (&isem
->data
,
64 (uint64_t) 1 << SEM_NWAITERS_SHIFT
);
66 pthread_cleanup_push (__sem_wait_cleanup
, isem
);
70 if ((d
& SEM_VALUE_MASK
) == 0)
72 /* No token, sleep. */
74 err
= __lll_abstimed_wait_intr (
75 ((unsigned int *) &isem
->data
) + SEM_VALUE_OFFSET
,
76 0, timeout
, flags
, clock_id
);
78 err
= __lll_wait_intr (
79 ((unsigned int *) &isem
->data
) + SEM_VALUE_OFFSET
,
82 if (err
!= 0 && err
!= KERN_INVALID_ARGUMENT
)
84 /* Error, interruption or timeout, abort. */
85 if (err
== KERN_TIMEDOUT
)
87 if (err
== KERN_INTERRUPTED
)
89 ret
= __hurd_fail (err
);
90 __sem_wait_cleanup (isem
);
95 d
= atomic_load_relaxed (&isem
->data
);
99 /* Try to acquire and dequeue. */
100 if (atomic_compare_exchange_weak_acquire (&isem
->data
,
101 &d
, d
- 1 - ((uint64_t) 1 << SEM_NWAITERS_SHIFT
)))
110 pthread_cleanup_pop (0);
114 atomic_fetch_add_acquire (&isem
->nwaiters
, 1);
116 pthread_cleanup_push (__sem_wait_cleanup
, isem
);
118 v
= atomic_load_relaxed (&isem
->value
);
125 if ((v
& SEM_NWAITERS_MASK
) != 0)
128 while (!atomic_compare_exchange_weak_release (&isem
->value
,
129 &v
, v
| SEM_NWAITERS_MASK
));
131 if ((v
>> SEM_VALUE_SHIFT
) == 0)
133 /* No token, sleep. */
135 err
= __lll_abstimed_wait_intr (&isem
->value
,
136 SEM_NWAITERS_MASK
, timeout
, flags
, clock_id
);
138 err
= __lll_wait_intr (&isem
->value
,
139 SEM_NWAITERS_MASK
, flags
);
141 if (err
!= 0 && err
!= KERN_INVALID_ARGUMENT
)
143 /* Error, interruption or timeout, abort. */
144 if (err
== KERN_TIMEDOUT
)
146 if (err
== KERN_INTERRUPTED
)
148 ret
= __hurd_fail (err
);
153 v
= atomic_load_relaxed (&isem
->value
);
156 while ((v
>> SEM_VALUE_SHIFT
) == 0);
158 while (!atomic_compare_exchange_weak_acquire (&isem
->value
,
159 &v
, v
- (1 << SEM_VALUE_SHIFT
)));
162 pthread_cleanup_pop (0);
164 __sem_wait_32_finish (isem
);
167 LIBC_CANCEL_RESET (cancel_oldtype
);
172 #if !__HAVE_64B_ATOMICS
173 /* Stop being a registered waiter (non-64b-atomics code only). */
175 __sem_wait_32_finish (struct new_sem
*isem
)
177 unsigned int wguess
= atomic_load_relaxed (&isem
->nwaiters
);
179 atomic_fetch_and_acquire (&isem
->value
, ~SEM_NWAITERS_MASK
);
181 unsigned int wfinal
= atomic_fetch_add_release (&isem
->nwaiters
, -1);
182 if (wfinal
> 1 && wguess
== 1)
184 unsigned int v
= atomic_fetch_or_relaxed (&isem
->value
,
186 v
>>= SEM_VALUE_SHIFT
;
188 __lll_wake (&isem
->value
, isem
->pshared
? GSYNC_SHARED
: 0);
194 __sem_clockwait (sem_t
*sem
, clockid_t clockid
,
195 const struct timespec
*restrict timeout
)
197 return __sem_timedwait_internal (sem
, clockid
, timeout
);
199 weak_alias (__sem_clockwait
, sem_clockwait
);
202 __sem_timedwait (sem_t
*restrict sem
, const struct timespec
*restrict timeout
)
204 return __sem_timedwait_internal (sem
, CLOCK_REALTIME
, timeout
);
207 weak_alias (__sem_timedwait
, sem_timedwait
);