1 /* Linuxthreads - a simple clone()-based implementation of Posix */
2 /* threads for Linux. */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
4 /* and Pavel Krauz (krauz@fsid.cvut.cz). */
6 /* This program is free software; you can redistribute it and/or */
7 /* modify it under the terms of the GNU Library General Public License */
8 /* as published by the Free Software Foundation; either version 2 */
9 /* of the License, or (at your option) any later version. */
11 /* This program 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 */
14 /* GNU Library General Public License for more details. */
16 /* Condition variables */
23 #include "internals.h"
28 static void remove_from_queue(pthread_queue * q, pthread_descr th);
30 int pthread_cond_init(pthread_cond_t *cond,
31 const pthread_condattr_t *cond_attr)
34 queue_init(&cond->c_waiting);
38 int pthread_cond_destroy(pthread_cond_t *cond)
42 acquire(&cond->c_spinlock);
43 head = cond->c_waiting.head;
44 release(&cond->c_spinlock);
45 if (head != NULL) return EBUSY;
49 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
51 volatile pthread_descr self = thread_self();
52 acquire(&cond->c_spinlock);
53 enqueue(&cond->c_waiting, self);
54 release(&cond->c_spinlock);
55 pthread_mutex_unlock(mutex);
56 suspend_with_cancellation(self);
57 pthread_mutex_lock(mutex);
58 /* This is a cancellation point */
59 if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
60 /* Remove ourselves from the waiting queue if we're still on it */
61 acquire(&cond->c_spinlock);
62 remove_from_queue(&cond->c_waiting, self);
63 release(&cond->c_spinlock);
64 pthread_exit(PTHREAD_CANCELED);
70 pthread_cond_timedwait_relative(pthread_cond_t *cond,
71 pthread_mutex_t *mutex,
72 const struct timespec * reltime)
74 volatile pthread_descr self = thread_self();
75 sigset_t unblock, initial_mask;
79 /* Wait on the condition */
80 acquire(&cond->c_spinlock);
81 enqueue(&cond->c_waiting, self);
82 release(&cond->c_spinlock);
83 pthread_mutex_unlock(mutex);
84 /* Set up a longjmp handler for the restart signal */
85 /* No need to save the signal mask, since PTHREAD_SIG_RESTART will be
86 blocked when doing the siglongjmp, and we'll just leave it blocked. */
87 if (sigsetjmp(jmpbuf, 0) == 0) {
88 self->p_signal_jmp = &jmpbuf;
90 /* Check for cancellation */
91 if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
94 /* Unblock the restart signal */
95 sigemptyset(&unblock);
96 sigaddset(&unblock, PTHREAD_SIG_RESTART);
97 sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
98 /* Sleep for the required duration */
99 retsleep = __libc_nanosleep(reltime, NULL);
100 /* Block the restart signal again */
101 sigprocmask(SIG_SETMASK, &initial_mask, NULL);
106 self->p_signal_jmp = NULL;
107 /* Here, either the condition was signaled (self->p_signal != 0)
108 or we got canceled (self->p_canceled != 0)
109 or the timeout occurred (retsleep == 0)
110 or another interrupt occurred (retsleep == -1) */
111 /* Re-acquire the spinlock */
112 acquire(&cond->c_spinlock);
113 /* This is a cancellation point */
114 if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
115 remove_from_queue(&cond->c_waiting, self);
116 release(&cond->c_spinlock);
117 pthread_mutex_lock(mutex);
118 pthread_exit(PTHREAD_CANCELED);
120 /* If not signaled: also remove ourselves and return an error code */
121 if (self->p_signal == 0) {
122 remove_from_queue(&cond->c_waiting, self);
123 release(&cond->c_spinlock);
124 pthread_mutex_lock(mutex);
125 return retsleep == 0 ? ETIMEDOUT : EINTR;
127 /* Otherwise, return normally */
128 release(&cond->c_spinlock);
129 pthread_mutex_lock(mutex);
133 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
134 const struct timespec * abstime)
137 struct timespec reltime;
138 /* Compute a time offset relative to now */
139 __gettimeofday(&now, NULL);
140 reltime.tv_sec = abstime->tv_sec - now.tv_sec;
141 reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
142 if (reltime.tv_nsec < 0) {
143 reltime.tv_nsec += 1000000000;
146 if (reltime.tv_sec < 0) return ETIMEDOUT;
147 return pthread_cond_timedwait_relative(cond, mutex, &reltime);
150 int pthread_cond_signal(pthread_cond_t *cond)
154 acquire(&cond->c_spinlock);
155 th = dequeue(&cond->c_waiting);
156 release(&cond->c_spinlock);
157 if (th != NULL) restart(th);
161 int pthread_cond_broadcast(pthread_cond_t *cond)
163 pthread_queue tosignal;
166 acquire(&cond->c_spinlock);
167 /* Copy the current state of the waiting queue and empty it */
168 tosignal = cond->c_waiting;
169 queue_init(&cond->c_waiting);
170 release(&cond->c_spinlock);
171 /* Now signal each process in the queue */
172 while ((th = dequeue(&tosignal)) != NULL) restart(th);
176 int pthread_condattr_init(pthread_condattr_t *attr)
181 int pthread_condattr_destroy(pthread_condattr_t *attr)
186 /* Auxiliary function on queues */
188 static void remove_from_queue(pthread_queue * q, pthread_descr th)
192 if (q->head == NULL) return;
194 q->head = th->p_nextwaiting;
195 if (q->head == NULL) q->tail = NULL;
196 th->p_nextwaiting = NULL;
199 for (t = q->head; t->p_nextwaiting != NULL; t = t->p_nextwaiting) {
200 if (t->p_nextwaiting == th) {
201 t->p_nextwaiting = th->p_nextwaiting;
202 if (th->p_nextwaiting == NULL) q->tail = t;
203 th->p_nextwaiting = NULL;