Use THREAD_GETMEM and THREAD_SETMEM to access the elements of the
[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 int pthread_cond_init(pthread_cond_t *cond,
29                       const pthread_condattr_t *cond_attr)
30 {
31   __pthread_init_lock(&cond->c_lock);
32   cond->c_waiting = NULL;
33   return 0;
34 }
35
36 int pthread_cond_destroy(pthread_cond_t *cond)
37 {
38   if (cond->c_waiting != NULL) return EBUSY;
39   return 0;
40 }
41
42 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
43 {
44   volatile pthread_descr self = thread_self();
45
46   __pthread_lock(&cond->c_lock);
47   enqueue(&cond->c_waiting, self);
48   __pthread_unlock(&cond->c_lock);
49   pthread_mutex_unlock(mutex);
50   suspend_with_cancellation(self);
51   pthread_mutex_lock(mutex);
52   /* This is a cancellation point */
53   if (THREAD_GETMEM(self, p_canceled)
54       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
55     /* Remove ourselves from the waiting queue if we're still on it */
56     __pthread_lock(&cond->c_lock);
57     remove_from_queue(&cond->c_waiting, self);
58     __pthread_unlock(&cond->c_lock);
59     pthread_exit(PTHREAD_CANCELED);
60   }
61   return 0;
62 }
63
64 static inline int
65 pthread_cond_timedwait_relative(pthread_cond_t *cond,
66                                 pthread_mutex_t *mutex,
67                                 const struct timespec * reltime)
68 {
69   volatile pthread_descr self = thread_self();
70   sigset_t unblock, initial_mask;
71   int retsleep;
72   sigjmp_buf jmpbuf;
73
74   /* Wait on the condition */
75   __pthread_lock(&cond->c_lock);
76   enqueue(&cond->c_waiting, self);
77   __pthread_unlock(&cond->c_lock);
78   pthread_mutex_unlock(mutex);
79   /* Set up a longjmp handler for the restart and cancel signals */
80   if (sigsetjmp(jmpbuf, 1) == 0) {
81     THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
82     THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf);
83     THREAD_SETMEM(self, p_signal, 0);
84     /* Check for cancellation */
85     if (THREAD_GETMEM(self, p_canceled)
86         && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
87       retsleep = -1;
88     } else {
89       /* Unblock the restart signal */
90       sigemptyset(&unblock);
91       sigaddset(&unblock, __pthread_sig_restart);
92       sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
93       /* Sleep for the required duration */
94       retsleep = __libc_nanosleep(reltime, NULL);
95       /* Block the restart signal again */
96       sigprocmask(SIG_SETMASK, &initial_mask, NULL);
97     }
98   } else {
99     retsleep = -1;
100   }
101   THREAD_SETMEM(self, p_signal_jmp, NULL);
102   THREAD_SETMEM(self, p_cancel_jmp, NULL);
103   /* Here, either the condition was signaled (self->p_signal != 0)
104                    or we got canceled (self->p_canceled != 0)
105                    or the timeout occurred (retsleep == 0)
106                    or another interrupt occurred (retsleep == -1) */
107   /* This is a cancellation point */
108   if (THREAD_GETMEM(self, p_canceled)
109       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
110     __pthread_lock(&cond->c_lock);
111     remove_from_queue(&cond->c_waiting, self);
112     __pthread_unlock(&cond->c_lock);
113     pthread_mutex_lock(mutex);
114     pthread_exit(PTHREAD_CANCELED);
115   }
116   /* If not signaled: also remove ourselves and return an error code */
117   if (THREAD_GETMEM(self, p_signal) == 0) {
118     __pthread_lock(&cond->c_lock);
119     remove_from_queue(&cond->c_waiting, self);
120     __pthread_unlock(&cond->c_lock);
121     pthread_mutex_lock(mutex);
122     return retsleep == 0 ? ETIMEDOUT : EINTR;
123   }
124   /* Otherwise, return normally */
125   pthread_mutex_lock(mutex);
126   return 0;
127 }
128
129 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
130                            const struct timespec * abstime)
131 {
132   struct timeval now;
133   struct timespec reltime;
134   /* Compute a time offset relative to now */
135   __gettimeofday(&now, NULL);
136   reltime.tv_sec = abstime->tv_sec - now.tv_sec;
137   reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
138   if (reltime.tv_nsec < 0) {
139     reltime.tv_nsec += 1000000000;
140     reltime.tv_sec -= 1;
141   }
142   if (reltime.tv_sec < 0) return ETIMEDOUT;
143   return pthread_cond_timedwait_relative(cond, mutex, &reltime);
144 }
145
146 int pthread_cond_signal(pthread_cond_t *cond)
147 {
148   pthread_descr th;
149
150   __pthread_lock(&cond->c_lock);
151   th = dequeue(&cond->c_waiting);
152   __pthread_unlock(&cond->c_lock);
153   if (th != NULL) restart(th);
154   return 0;
155 }
156
157 int pthread_cond_broadcast(pthread_cond_t *cond)
158 {
159   pthread_descr tosignal, th;
160
161   __pthread_lock(&cond->c_lock);
162   /* Copy the current state of the waiting queue and empty it */
163   tosignal = cond->c_waiting;
164   cond->c_waiting = NULL;
165   __pthread_unlock(&cond->c_lock);
166   /* Now signal each process in the queue */
167   while ((th = dequeue(&tosignal)) != NULL) restart(th);
168   return 0;
169 }
170
171 int pthread_condattr_init(pthread_condattr_t *attr)
172 {
173   return 0;
174 }
175
176 int pthread_condattr_destroy(pthread_condattr_t *attr)
177 {
178   return 0;
179 }