LinuxThreads library.
[kopensolaris-gnu/glibc.git] / linuxthreads / condvar.c
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).                                */
5 /*                                                                      */
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.               */
10 /*                                                                      */
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.                 */
15
16 /* Condition variables */
17
18 #include <errno.h>
19 #include <sched.h>
20 #include <stddef.h>
21 #include <sys/time.h>
22 #include "pthread.h"
23 #include "internals.h"
24 #include "spinlock.h"
25 #include "queue.h"
26 #include "restart.h"
27
28 static void remove_from_queue(pthread_queue * q, pthread_descr th);
29
30 int pthread_cond_init(pthread_cond_t *cond,
31                       const pthread_condattr_t *cond_attr)
32 {
33   cond->c_spinlock = 0;
34   queue_init(&cond->c_waiting);
35   return 0;
36 }
37
38 int pthread_cond_destroy(pthread_cond_t *cond)
39 {
40   pthread_descr head;
41
42   acquire(&cond->c_spinlock);
43   head = cond->c_waiting.head;
44   release(&cond->c_spinlock);
45   if (head != NULL) return EBUSY;
46   return 0;
47 }
48
49 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
50 {
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);
65   }
66   return 0;
67 }
68
69 static inline int
70 pthread_cond_timedwait_relative(pthread_cond_t *cond,
71                                 pthread_mutex_t *mutex,
72                                 const struct timespec * reltime)
73 {
74   volatile pthread_descr self = thread_self();
75   sigset_t unblock, initial_mask;
76   int retsleep;
77   sigjmp_buf jmpbuf;
78
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;
89     self->p_signal = 0;
90     /* Check for cancellation */
91     if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
92       retsleep = -1;
93     } else {
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);
102     }
103   } else {
104     retsleep = -1;
105   }
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);
119   }
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;
126   }
127   /* Otherwise, return normally */
128   release(&cond->c_spinlock);
129   pthread_mutex_lock(mutex);
130   return 0;
131 }
132
133 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
134                            const struct timespec * abstime)
135 {
136   struct timeval now;
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;
144     reltime.tv_sec -= 1;
145   }
146   if (reltime.tv_sec < 0) return ETIMEDOUT;
147   return pthread_cond_timedwait_relative(cond, mutex, &reltime);
148 }
149
150 int pthread_cond_signal(pthread_cond_t *cond)
151 {
152   pthread_descr th;
153
154   acquire(&cond->c_spinlock);
155   th = dequeue(&cond->c_waiting);
156   release(&cond->c_spinlock);
157   if (th != NULL) restart(th);
158   return 0;
159 }
160
161 int pthread_cond_broadcast(pthread_cond_t *cond)
162 {
163   pthread_queue tosignal;
164   pthread_descr th;
165
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);
173   return 0;
174 }
175
176 int pthread_condattr_init(pthread_condattr_t *attr)
177 {
178   return 0;
179 }
180
181 int pthread_condattr_destroy(pthread_condattr_t *attr)
182 {
183   return 0;
184 }
185
186 /* Auxiliary function on queues */
187
188 static void remove_from_queue(pthread_queue * q, pthread_descr th)
189 {
190   pthread_descr t;
191
192   if (q->head == NULL) return;
193   if (q->head == th) {
194     q->head = th->p_nextwaiting;
195     if (q->head == NULL) q->tail = NULL;
196     th->p_nextwaiting = NULL;
197     return;
198   }
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;
204       return;
205     }
206   }
207 }