aboutsummaryrefslogtreecommitdiff
path: root/linuxthreads/condvar.c
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads/condvar.c')
-rw-r--r--linuxthreads/condvar.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c
new file mode 100644
index 0000000000..6807522edc
--- /dev/null
+++ b/linuxthreads/condvar.c
@@ -0,0 +1,207 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix */
+/* threads for Linux. */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
+/* and Pavel Krauz (krauz@fsid.cvut.cz). */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU Library General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU Library General Public License for more details. */
+
+/* Condition variables */
+
+#include <errno.h>
+#include <sched.h>
+#include <stddef.h>
+#include <sys/time.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "queue.h"
+#include "restart.h"
+
+static void remove_from_queue(pthread_queue * q, pthread_descr th);
+
+int pthread_cond_init(pthread_cond_t *cond,
+ const pthread_condattr_t *cond_attr)
+{
+ cond->c_spinlock = 0;
+ queue_init(&cond->c_waiting);
+ return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+ pthread_descr head;
+
+ acquire(&cond->c_spinlock);
+ head = cond->c_waiting.head;
+ release(&cond->c_spinlock);
+ if (head != NULL) return EBUSY;
+ return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+ volatile pthread_descr self = thread_self();
+ acquire(&cond->c_spinlock);
+ enqueue(&cond->c_waiting, self);
+ release(&cond->c_spinlock);
+ pthread_mutex_unlock(mutex);
+ suspend_with_cancellation(self);
+ pthread_mutex_lock(mutex);
+ /* This is a cancellation point */
+ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+ /* Remove ourselves from the waiting queue if we're still on it */
+ acquire(&cond->c_spinlock);
+ remove_from_queue(&cond->c_waiting, self);
+ release(&cond->c_spinlock);
+ pthread_exit(PTHREAD_CANCELED);
+ }
+ return 0;
+}
+
+static inline int
+pthread_cond_timedwait_relative(pthread_cond_t *cond,
+ pthread_mutex_t *mutex,
+ const struct timespec * reltime)
+{
+ volatile pthread_descr self = thread_self();
+ sigset_t unblock, initial_mask;
+ int retsleep;
+ sigjmp_buf jmpbuf;
+
+ /* Wait on the condition */
+ acquire(&cond->c_spinlock);
+ enqueue(&cond->c_waiting, self);
+ release(&cond->c_spinlock);
+ pthread_mutex_unlock(mutex);
+ /* Set up a longjmp handler for the restart signal */
+ /* No need to save the signal mask, since PTHREAD_SIG_RESTART will be
+ blocked when doing the siglongjmp, and we'll just leave it blocked. */
+ if (sigsetjmp(jmpbuf, 0) == 0) {
+ self->p_signal_jmp = &jmpbuf;
+ self->p_signal = 0;
+ /* Check for cancellation */
+ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+ retsleep = -1;
+ } else {
+ /* Unblock the restart signal */
+ sigemptyset(&unblock);
+ sigaddset(&unblock, PTHREAD_SIG_RESTART);
+ sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
+ /* Sleep for the required duration */
+ retsleep = __libc_nanosleep(reltime, NULL);
+ /* Block the restart signal again */
+ sigprocmask(SIG_SETMASK, &initial_mask, NULL);
+ }
+ } else {
+ retsleep = -1;
+ }
+ self->p_signal_jmp = NULL;
+ /* Here, either the condition was signaled (self->p_signal != 0)
+ or we got canceled (self->p_canceled != 0)
+ or the timeout occurred (retsleep == 0)
+ or another interrupt occurred (retsleep == -1) */
+ /* Re-acquire the spinlock */
+ acquire(&cond->c_spinlock);
+ /* This is a cancellation point */
+ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+ remove_from_queue(&cond->c_waiting, self);
+ release(&cond->c_spinlock);
+ pthread_mutex_lock(mutex);
+ pthread_exit(PTHREAD_CANCELED);
+ }
+ /* If not signaled: also remove ourselves and return an error code */
+ if (self->p_signal == 0) {
+ remove_from_queue(&cond->c_waiting, self);
+ release(&cond->c_spinlock);
+ pthread_mutex_lock(mutex);
+ return retsleep == 0 ? ETIMEDOUT : EINTR;
+ }
+ /* Otherwise, return normally */
+ release(&cond->c_spinlock);
+ pthread_mutex_lock(mutex);
+ return 0;
+}
+
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+ const struct timespec * abstime)
+{
+ struct timeval now;
+ struct timespec reltime;
+ /* Compute a time offset relative to now */
+ __gettimeofday(&now, NULL);
+ reltime.tv_sec = abstime->tv_sec - now.tv_sec;
+ reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
+ if (reltime.tv_nsec < 0) {
+ reltime.tv_nsec += 1000000000;
+ reltime.tv_sec -= 1;
+ }
+ if (reltime.tv_sec < 0) return ETIMEDOUT;
+ return pthread_cond_timedwait_relative(cond, mutex, &reltime);
+}
+
+int pthread_cond_signal(pthread_cond_t *cond)
+{
+ pthread_descr th;
+
+ acquire(&cond->c_spinlock);
+ th = dequeue(&cond->c_waiting);
+ release(&cond->c_spinlock);
+ if (th != NULL) restart(th);
+ return 0;
+}
+
+int pthread_cond_broadcast(pthread_cond_t *cond)
+{
+ pthread_queue tosignal;
+ pthread_descr th;
+
+ acquire(&cond->c_spinlock);
+ /* Copy the current state of the waiting queue and empty it */
+ tosignal = cond->c_waiting;
+ queue_init(&cond->c_waiting);
+ release(&cond->c_spinlock);
+ /* Now signal each process in the queue */
+ while ((th = dequeue(&tosignal)) != NULL) restart(th);
+ return 0;
+}
+
+int pthread_condattr_init(pthread_condattr_t *attr)
+{
+ return 0;
+}
+
+int pthread_condattr_destroy(pthread_condattr_t *attr)
+{
+ return 0;
+}
+
+/* Auxiliary function on queues */
+
+static void remove_from_queue(pthread_queue * q, pthread_descr th)
+{
+ pthread_descr t;
+
+ if (q->head == NULL) return;
+ if (q->head == th) {
+ q->head = th->p_nextwaiting;
+ if (q->head == NULL) q->tail = NULL;
+ th->p_nextwaiting = NULL;
+ return;
+ }
+ for (t = q->head; t->p_nextwaiting != NULL; t = t->p_nextwaiting) {
+ if (t->p_nextwaiting == th) {
+ t->p_nextwaiting = th->p_nextwaiting;
+ if (th->p_nextwaiting == NULL) q->tail = t;
+ th->p_nextwaiting = NULL;
+ return;
+ }
+ }
+}