]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/mach/hurd/htl/pt-hurd-cond-timedwait.c
c6b48047c7afa53c18a80e58569cc10c87879643
[thirdparty/glibc.git] / sysdeps / mach / hurd / htl / pt-hurd-cond-timedwait.c
1 /* pthread_hurd_cond_timedwait_np. Hurd-specific wait on a condition.
2 Copyright (C) 2012-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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 <pthread.h>
20 #include <assert.h>
21 #include <hurd/signal.h>
22
23 #include <pt-internal.h>
24
25 extern int __pthread_hurd_cond_timedwait_internal (pthread_cond_t *cond,
26 pthread_mutex_t *mutex,
27 const struct timespec
28 *abstime);
29
30 int
31 __pthread_hurd_cond_timedwait_np (pthread_cond_t *cond,
32 pthread_mutex_t *mutex,
33 const struct timespec *abstime)
34 {
35 return __pthread_hurd_cond_timedwait_internal (cond, mutex, abstime);
36 }
37
38 strong_alias (__pthread_hurd_cond_timedwait_np, pthread_hurd_cond_timedwait_np);
39
40 int
41 __pthread_hurd_cond_timedwait_internal (pthread_cond_t *cond,
42 pthread_mutex_t *mutex,
43 const struct timespec *abstime)
44 {
45 struct hurd_sigstate *ss = _hurd_self_sigstate ();
46 struct __pthread *self = _pthread_self ();
47 error_t err = 0;
48 int cancel, drain;
49 clockid_t clock_id = __pthread_default_condattr.__clock;
50
51 /* This function will be called by hurd_thread_cancel while we are blocked
52 We wake up our thread if it's still blocking or about to block, so it will
53 progress and notice the cancellation flag. */
54 void cancel_me (void)
55 {
56 int unblock;
57
58 __pthread_spin_lock (&cond->__lock);
59 /* The thread only needs to be awaken if it's blocking or about to block.
60 If it was already unblocked, it's not queued any more. */
61 unblock = self->prevp != NULL;
62 if (unblock)
63 __pthread_dequeue (self);
64 __pthread_spin_unlock (&cond->__lock);
65
66 if (unblock)
67 __pthread_wakeup (self);
68 }
69
70 assert (ss->intr_port == MACH_PORT_NULL); /* Sanity check for signal bugs. */
71
72 if (abstime != NULL && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000))
73 return EINVAL;
74
75 /* Atomically enqueue our thread on the condition variable's queue of
76 waiters, and mark our sigstate to indicate that `cancel_me' must be
77 called to wake us up. We must hold the sigstate lock while acquiring
78 the condition variable's lock and tweaking it, so that
79 hurd_thread_cancel can never suspend us and then deadlock waiting for
80 the condition variable's lock. */
81
82 __spin_lock (&ss->lock);
83 __pthread_spin_lock (&cond->__lock);
84 cancel = ss->cancel;
85 if (cancel)
86 /* We were cancelled before doing anything. Don't block at all. */
87 ss->cancel = 0;
88 else
89 {
90 /* Put us on the queue so that pthread_cond_broadcast will know to wake
91 us up. */
92 __pthread_enqueue (&cond->__queue, self);
93 if (cond->__attr)
94 clock_id = cond->__attr->__clock;
95 /* Tell hurd_thread_cancel how to unblock us. */
96 ss->cancel_hook = &cancel_me;
97 }
98 __pthread_spin_unlock (&cond->__lock);
99 __spin_unlock (&ss->lock);
100
101 if (cancel)
102 {
103 /* Cancelled on entry. Just leave the mutex locked. */
104 mutex = NULL;
105
106 __spin_lock (&ss->lock);
107 }
108 else
109 {
110 /* Release MUTEX before blocking. */
111 __pthread_mutex_unlock (mutex);
112
113 /* Block the thread. */
114 if (abstime != NULL)
115 err = __pthread_timedblock (self, abstime, clock_id);
116 else
117 {
118 err = 0;
119 __pthread_block (self);
120 }
121
122 /* As it was done when enqueueing, prevent hurd_thread_cancel from
123 suspending us while the condition lock is held. */
124 __spin_lock (&ss->lock);
125 __pthread_spin_lock (&cond->__lock);
126 if (self->prevp == NULL)
127 /* Another thread removed us from the list of waiters, which means
128 a wakeup message has been sent. It was either consumed while
129 we were blocking, or queued after we timed out and before we
130 acquired the condition lock, in which case the message queue
131 must be drained. */
132 drain = err ? 1 : 0;
133 else
134 {
135 /* We're still in the list of waiters. Noone attempted to wake us
136 up, i.e. we timed out. */
137 __pthread_dequeue (self);
138 drain = 0;
139 }
140 __pthread_spin_unlock (&cond->__lock);
141
142 if (drain)
143 __pthread_block (self);
144 }
145
146 /* Clear the hook, now that we are done blocking. */
147 ss->cancel_hook = NULL;
148 /* Check the cancellation flag; we might have unblocked due to
149 cancellation rather than a normal pthread_cond_signal or
150 pthread_cond_broadcast (or we might have just happened to get cancelled
151 right after waking up). */
152 cancel |= ss->cancel;
153 ss->cancel = 0;
154 __spin_unlock (&ss->lock);
155
156 if (mutex != NULL)
157 /* Reacquire the mutex and return. */
158 __pthread_mutex_lock (mutex);
159
160 if (cancel)
161 return EINTR;
162 else if (err)
163 {
164 assert (err == ETIMEDOUT);
165 return err;
166 }
167
168 return 0;
169 }