about summary refs log tree commit diff
path: root/linuxthreads
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads')
-rw-r--r--linuxthreads/ChangeLog16
-rw-r--r--linuxthreads/condvar.c210
-rw-r--r--linuxthreads/internals.h5
-rw-r--r--linuxthreads/pthread.c141
-rw-r--r--linuxthreads/restart.h6
-rw-r--r--linuxthreads/semaphore.c55
6 files changed, 166 insertions, 267 deletions
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index 50fa766fc6..9732297adc 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,5 +1,21 @@
 2000-04-15  Ulrich Drepper  <drepper@redhat.com>
 
+	* condvar.c: Remove all the special code to handle cond_timedwait.
+	Use timedsuspend instead.
+	* internals.h: Declare __pthread_timedsuspend_old,
+	__pthread_timedsuspend_new, and __pthread_timedsuspend.
+	Remove declaration of __pthread_init_condvar.
+	* pthread.c: Define __pthread_timedsuspend variable.
+	(__pthread_timedsuspend_old): New function.  Timed suspension
+	implementation for old Linux kernels.
+	(__pthread_timedsuspend_new): New function.  Timed suspension
+	implementation for new Linux kernels.
+	* restart.h (timedsuspend): New function.  Call appropriate
+	suspension function through __pthread_timedsuspend.
+	* semaphore.c (sem_timedwait): Use timedsuspend, don't duplicate
+	the code.
+	Patch by Kaz Kylheku <kaz@ashi.footprints.net>.
+
 	* internals.h (WRITE_MEMORY_BARRIER): Define as MEMORY_BARRIER if
 	undefined.
 	* spinlock.c: Use WRITE_MEMORY_BARRIER instead of MEMORY_BARRIER
diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c
index e1c8119231..a06517c83f 100644
--- a/linuxthreads/condvar.c
+++ b/linuxthreads/condvar.c
@@ -25,22 +25,6 @@
 #include "queue.h"
 #include "restart.h"
 
-static int pthread_cond_timedwait_relative_old(pthread_cond_t *,
-    pthread_mutex_t *, const struct timespec *);
-
-static int pthread_cond_timedwait_relative_new(pthread_cond_t *,
-    pthread_mutex_t *, const struct timespec *);
-
-static int (*pthread_cond_tw_rel)(pthread_cond_t *, pthread_mutex_t *,
-    const struct timespec *) = pthread_cond_timedwait_relative_old;
-
-/* initialize this module */
-void __pthread_init_condvar(int rt_sig_available)
-{
-  if (rt_sig_available)
-    pthread_cond_tw_rel = pthread_cond_timedwait_relative_new;
-}
-
 int pthread_cond_init(pthread_cond_t *cond,
                       const pthread_condattr_t *cond_attr)
 {
@@ -127,151 +111,13 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
   return 0;
 }
 
-/* The following function is used on kernels that don't have rt signals.
-   SIGUSR1 is used as the restart signal. The different code is needed
-   because that ordinary signal does not queue. */
-
 static int
-pthread_cond_timedwait_relative_old(pthread_cond_t *cond,
+pthread_cond_timedwait_relative(pthread_cond_t *cond,
 				pthread_mutex_t *mutex,
 				const struct timespec * abstime)
 {
   volatile pthread_descr self = thread_self();
-  sigset_t unblock, initial_mask;
   int already_canceled = 0;
-  int was_signalled = 0;
-  sigjmp_buf jmpbuf;
-  pthread_extricate_if extr;
-
-  /* Check whether the mutex is locked and owned by this thread.  */
-  if (mutex->__m_kind != PTHREAD_MUTEX_FAST_NP && mutex->__m_owner != self)
-    return EINVAL;
-
-  /* Set up extrication interface */
-  extr.pu_object = cond;
-  extr.pu_extricate_func = cond_extricate_func;
-
-  /* Register extrication interface */
-  __pthread_set_own_extricate_if(self, &extr);
-
-  /* Enqueue to wait on the condition and check for cancellation. */
-  __pthread_lock(&cond->__c_lock, self);
-  if (!(THREAD_GETMEM(self, p_canceled)
-      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
-    enqueue(&cond->__c_waiting, self);
-  else
-    already_canceled = 1;
-  __pthread_spin_unlock(&cond->__c_lock);
-
-  if (already_canceled) {
-    __pthread_set_own_extricate_if(self, 0);
-    pthread_exit(PTHREAD_CANCELED);
-  }
-
-  pthread_mutex_unlock(mutex);
-
-  if (atomic_decrement(&self->p_resume_count) == 0) {
-    /* Set up a longjmp handler for the restart signal, unblock
-       the signal and sleep. */
-
-    if (sigsetjmp(jmpbuf, 1) == 0) {
-      THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
-      THREAD_SETMEM(self, p_signal, 0);
-      /* Unblock the restart signal */
-      sigemptyset(&unblock);
-      sigaddset(&unblock, __pthread_sig_restart);
-      sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
-
-      while (1) {
-	struct timeval now;
-	struct timespec reltime;
-
-	/* Compute a time offset relative to now.  */
-	__gettimeofday (&now, NULL);
-	reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
-	reltime.tv_sec = abstime->tv_sec - now.tv_sec;
-	if (reltime.tv_nsec < 0) {
-	  reltime.tv_nsec += 1000000000;
-	  reltime.tv_sec -= 1;
-	}
-
-	/* Sleep for the required duration. If woken by a signal,
-	   resume waiting as required by Single Unix Specification.  */
-	if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0)
-	  break;
-      }
-
-      /* Block the restart signal again */
-      sigprocmask(SIG_SETMASK, &initial_mask, NULL);
-      was_signalled = 0;
-    } else {
-      was_signalled = 1;
-    }
-    THREAD_SETMEM(self, p_signal_jmp, NULL);
-  }
-
-  /* Now was_signalled is true if we exited the above code
-     due to the delivery of a restart signal.  In that case,
-     we know we have been dequeued and resumed and that the
-     resume count is balanced.  Otherwise, there are some
-     cases to consider. First, try to bump up the resume count
-     back to zero. If it goes to 1, it means restart() was
-     invoked on this thread. The signal must be consumed
-     and the count bumped down and everything is cool.
-     Otherwise, no restart was delivered yet, so we remove
-     the thread from the queue. If this succeeds, it's a clear
-     case of timeout. If we fail to remove from the queue, then we
-     must wait for a restart. */
-
-  if (!was_signalled) {
-    if (atomic_increment(&self->p_resume_count) != -1) {
-      __pthread_wait_for_restart_signal(self);
-      atomic_decrement(&self->p_resume_count); /* should be zero now! */
-    } else {
-      int was_on_queue;
-      __pthread_lock(&cond->__c_lock, self);
-      was_on_queue = remove_from_queue(&cond->__c_waiting, self);
-      __pthread_spin_unlock(&cond->__c_lock);
-
-      if (was_on_queue) {
-	__pthread_set_own_extricate_if(self, 0);
-	pthread_mutex_lock(mutex);
-	return ETIMEDOUT;
-      }
-
-      suspend(self);
-    }
-  }
-
-  __pthread_set_own_extricate_if(self, 0);
-
-  /* The remaining logic is the same as in other cancellable waits,
-     such as pthread_join sem_wait or pthread_cond wait. */
-
-  if (THREAD_GETMEM(self, p_woken_by_cancel)
-      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
-    THREAD_SETMEM(self, p_woken_by_cancel, 0);
-    pthread_mutex_lock(mutex);
-    pthread_exit(PTHREAD_CANCELED);
-  }
-
-  pthread_mutex_lock(mutex);
-  return 0;
-}
-
-/* The following function is used on new (late 2.1 and 2.2 and higher) kernels
-   that have rt signals which queue. */
-
-static int
-pthread_cond_timedwait_relative_new(pthread_cond_t *cond,
-				pthread_mutex_t *mutex,
-				const struct timespec * abstime)
-{
-  volatile pthread_descr self = thread_self();
-  sigset_t unblock, initial_mask;
-  int already_canceled = 0;
-  int was_signalled = 0;
-  sigjmp_buf jmpbuf;
   pthread_extricate_if extr;
 
   /* Check whether the mutex is locked and owned by this thread.  */
@@ -279,7 +125,6 @@ pthread_cond_timedwait_relative_new(pthread_cond_t *cond,
     return EINVAL;
 
   already_canceled = 0;
-  was_signalled = 0;
 
   /* Set up extrication interface */
   extr.pu_object = cond;
@@ -304,56 +149,7 @@ pthread_cond_timedwait_relative_new(pthread_cond_t *cond,
 
   pthread_mutex_unlock(mutex);
 
-  /* Set up a longjmp handler for the restart signal, unblock
-     the signal and sleep. */
-
-  if (sigsetjmp(jmpbuf, 1) == 0) {
-    THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
-    THREAD_SETMEM(self, p_signal, 0);
-    /* Unblock the restart signal */
-    sigemptyset(&unblock);
-    sigaddset(&unblock, __pthread_sig_restart);
-    sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
-
-      while (1) {
-	struct timeval now;
-	struct timespec reltime;
-
-	/* Compute a time offset relative to now.  */
-	__gettimeofday (&now, NULL);
-	reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
-	reltime.tv_sec = abstime->tv_sec - now.tv_sec;
-	if (reltime.tv_nsec < 0) {
-	  reltime.tv_nsec += 1000000000;
-	  reltime.tv_sec -= 1;
-	}
-
-	/* Sleep for the required duration. If woken by a signal,
-	   resume waiting as required by Single Unix Specification.  */
-	if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0)
-	  break;
-      }
-
-    /* Block the restart signal again */
-    sigprocmask(SIG_SETMASK, &initial_mask, NULL);
-    was_signalled = 0;
-  } else {
-    was_signalled = 1;
-  }
-  THREAD_SETMEM(self, p_signal_jmp, NULL);
-
-  /* Now was_signalled is true if we exited the above code
-     due to the delivery of a restart signal.  In that case,
-     everything is cool. We have been removed from the queue
-     by the other thread, and consumed its signal.
-
-     Otherwise we this thread woke up spontaneously, or due to a signal other
-     than restart. The next thing to do is to try to remove the thread
-     from the queue. This may fail due to a race against another thread
-     trying to do the same. In the failed case, we know we were signalled,
-     and we may also have to consume a restart signal. */
-
-  if (!was_signalled) {
+  if (!timedsuspend(self, abstime) == 0) {
     int was_on_queue;
 
     /* __pthread_lock will queue back any spurious restarts that
@@ -393,7 +189,7 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
                            const struct timespec * abstime)
 {
   /* Indirect call through pointer! */
-  return pthread_cond_tw_rel(cond, mutex, abstime);
+  return pthread_cond_timedwait_relative(cond, mutex, abstime);
 }
 
 int pthread_cond_signal(pthread_cond_t *cond)
diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h
index 17c8e4d4d6..e6b58df3ca 100644
--- a/linuxthreads/internals.h
+++ b/linuxthreads/internals.h
@@ -431,18 +431,19 @@ extern void __pthread_kill_other_threads_np (void);
 
 void __pthread_restart_old(pthread_descr th);
 void __pthread_suspend_old(pthread_descr self);
+int __pthread_timedsuspend_old(pthread_descr self, const struct timespec *abs);
 
 void __pthread_restart_new(pthread_descr th);
 void __pthread_suspend_new(pthread_descr self);
+int __pthread_timedsuspend_new(pthread_descr self, const struct timespec *abs);
 
 void __pthread_wait_for_restart_signal(pthread_descr self);
 
-void __pthread_init_condvar(int rt_sig_available);
-
 /* Global pointers to old or new suspend functions */
 
 extern void (*__pthread_restart)(pthread_descr);
 extern void (*__pthread_suspend)(pthread_descr);
+extern int (*__pthread_timedsuspend)(pthread_descr, const struct timespec *);
 
 /* Prototypes for the function without cancelation support when the
    normal version has it.  */
diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c
index d3b851e5a2..e8ec26d33c 100644
--- a/linuxthreads/pthread.c
+++ b/linuxthreads/pthread.c
@@ -166,6 +166,7 @@ int __pthread_exit_code = 0;
 
 void (*__pthread_restart)(pthread_descr) = __pthread_restart_old;
 void (*__pthread_suspend)(pthread_descr) = __pthread_suspend_old;
+int (*__pthread_timedsuspend)(pthread_descr, const struct timespec *) = __pthread_timedsuspend_old;
 
 /* Communicate relevant LinuxThreads constants to gdb */
 
@@ -238,7 +239,6 @@ init_rtsigs (void)
       __pthread_sig_cancel = SIGUSR2;
       __pthread_sig_debug = 0;
 #endif
-      __pthread_init_condvar(0);
     }
   else
     {
@@ -246,10 +246,9 @@ init_rtsigs (void)
       current_rtmin = __SIGRTMIN + 3;
       __pthread_restart = __pthread_restart_new;
       __pthread_suspend = __pthread_wait_for_restart_signal;
-      __pthread_init_condvar(1);
+      __pthread_timedsuspend = __pthread_timedsuspend_new;
 #else
       current_rtmin = __SIGRTMIN;
-      __pthread_init_condvar(0);
 #endif
 
       current_rtmax = __SIGRTMAX;
@@ -826,7 +825,8 @@ void __pthread_wait_for_restart_signal(pthread_descr self)
   } while (self->p_signal !=__pthread_sig_restart );
 }
 
-/* The _old variants are for 2.0 and early 2.1 kernels which don't have RT signals.
+/* The _old variants are for 2.0 and early 2.1 kernels which don't have RT
+   signals.
    On these kernels, we use SIGUSR1 and SIGUSR2 for restart and cancellation.
    Since the restart signal does not queue, we use an atomic counter to create
    queuing semantics. This is needed to resolve a rare race condition in
@@ -844,6 +844,83 @@ void __pthread_suspend_old(pthread_descr self)
     __pthread_wait_for_restart_signal(self);
 }
 
+int 
+__pthread_timedsuspend_old(pthread_descr self, const struct timespec *abstime)
+{
+  sigset_t unblock, initial_mask;
+  int was_signalled = 0;
+  sigjmp_buf jmpbuf;
+
+  if (atomic_decrement(&self->p_resume_count) == 0) {
+    /* Set up a longjmp handler for the restart signal, unblock
+       the signal and sleep. */
+
+    if (sigsetjmp(jmpbuf, 1) == 0) {
+      THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
+      THREAD_SETMEM(self, p_signal, 0);
+      /* Unblock the restart signal */
+      sigemptyset(&unblock);
+      sigaddset(&unblock, __pthread_sig_restart);
+      sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
+
+      while (1) {
+	struct timeval now;
+	struct timespec reltime;
+
+	/* Compute a time offset relative to now.  */
+	__gettimeofday (&now, NULL);
+	reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
+	reltime.tv_sec = abstime->tv_sec - now.tv_sec;
+	if (reltime.tv_nsec < 0) {
+	  reltime.tv_nsec += 1000000000;
+	  reltime.tv_sec -= 1;
+	}
+
+	/* Sleep for the required duration. If woken by a signal,
+	   resume waiting as required by Single Unix Specification.  */
+	if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0)
+	  break;
+      }
+
+      /* Block the restart signal again */
+      sigprocmask(SIG_SETMASK, &initial_mask, NULL);
+      was_signalled = 0;
+    } else {
+      was_signalled = 1;
+    }
+    THREAD_SETMEM(self, p_signal_jmp, NULL);
+  }
+
+  /* Now was_signalled is true if we exited the above code
+     due to the delivery of a restart signal.  In that case,
+     we know we have been dequeued and resumed and that the
+     resume count is balanced.  Otherwise, there are some
+     cases to consider. First, try to bump up the resume count
+     back to zero. If it goes to 1, it means restart() was
+     invoked on this thread. The signal must be consumed
+     and the count bumped down and everything is cool. We
+     can return a 1 to the caller.
+     Otherwise, no restart was delivered yet, so a potential
+     race exists; we return a 0 to the caller which must deal
+     with this race in an appropriate way; for example by
+     atomically removing the thread from consideration for a 
+     wakeup---if such a thing fails, it means a restart is
+     being delivered. */
+
+  if (!was_signalled) {
+    if (atomic_increment(&self->p_resume_count) != -1) {
+      __pthread_wait_for_restart_signal(self);
+      atomic_decrement(&self->p_resume_count); /* should be zero now! */
+      /* woke spontaneously and consumed restart signal */
+      return 1;
+    }
+    /* woke spontaneously but did not consume restart---caller must resolve */
+    return 0;
+  }
+  /* woken due to restart signal */
+  return 1;
+}
+
 void __pthread_restart_new(pthread_descr th)
 {
     kill(th->p_pid, __pthread_sig_restart);
@@ -852,6 +929,62 @@ void __pthread_restart_new(pthread_descr th)
 /* There is no __pthread_suspend_new because it would just
    be a wasteful wrapper for __pthread_wait_for_restart_signal */
 
+int 
+__pthread_timedsuspend_new(pthread_descr self, const struct timespec *abstime)
+{
+  sigset_t unblock, initial_mask;
+  int was_signalled = 0;
+  sigjmp_buf jmpbuf;
+
+  if (sigsetjmp(jmpbuf, 1) == 0) {
+    THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
+    THREAD_SETMEM(self, p_signal, 0);
+    /* Unblock the restart signal */
+    sigemptyset(&unblock);
+    sigaddset(&unblock, __pthread_sig_restart);
+    sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
+
+    while (1) {
+      struct timeval now;
+      struct timespec reltime;
+
+      /* Compute a time offset relative to now.  */
+      __gettimeofday (&now, NULL);
+      reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
+      reltime.tv_sec = abstime->tv_sec - now.tv_sec;
+      if (reltime.tv_nsec < 0) {
+	reltime.tv_nsec += 1000000000;
+	reltime.tv_sec -= 1;
+      }
+
+      /* Sleep for the required duration. If woken by a signal,
+	 resume waiting as required by Single Unix Specification.  */
+      if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0)
+	break;
+    }
+
+    /* Block the restart signal again */
+    sigprocmask(SIG_SETMASK, &initial_mask, NULL);
+    was_signalled = 0;
+  } else {
+    was_signalled = 1;
+  }
+  THREAD_SETMEM(self, p_signal_jmp, NULL);
+
+  /* Now was_signalled is true if we exited the above code
+     due to the delivery of a restart signal.  In that case,
+     everything is cool. We have been removed from whatever
+     we were waiting on by the other thread, and consumed its signal.
+
+     Otherwise we this thread woke up spontaneously, or due to a signal other
+     than restart. This is an ambiguous case  that must be resolved by
+     the caller; the thread is still eligible for a restart wakeup
+     so there is a race. */
+
+  return was_signalled;
+}
+
+
 /* Debugging aid */
 
 #ifdef DEBUG
diff --git a/linuxthreads/restart.h b/linuxthreads/restart.h
index 702d7d15c6..0a69309c5b 100644
--- a/linuxthreads/restart.h
+++ b/linuxthreads/restart.h
@@ -25,3 +25,9 @@ static inline void suspend(pthread_descr self)
 {
   __pthread_suspend(self); /* see pthread.c */
 }
+
+static inline int timedsuspend(pthread_descr self, 
+		const struct timespec *abstime)
+{
+   return __pthread_timedsuspend(self, abstime); /* see pthread.c */
+}
diff --git a/linuxthreads/semaphore.c b/linuxthreads/semaphore.c
index 7775b5a7d3..35bb79679b 100644
--- a/linuxthreads/semaphore.c
+++ b/linuxthreads/semaphore.c
@@ -195,10 +195,6 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime)
   pthread_descr self = thread_self();
   pthread_extricate_if extr;
   int already_canceled = 0;
-  int was_signalled = 0;
-  sigjmp_buf jmpbuf;
-  sigset_t unblock;
-  sigset_t initial_mask;
 
   __pthread_lock((pthread_spinlock_t *) &sem->__sem_lock, self);
   if (sem->__sem_value > 0) {
@@ -233,56 +229,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime)
     pthread_exit(PTHREAD_CANCELED);
   }
 
-  /* Set up a longjmp handler for the restart signal, unblock
-     the signal and sleep. */
-
-  if (sigsetjmp(jmpbuf, 1) == 0) {
-    THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
-    THREAD_SETMEM(self, p_signal, 0);
-    /* Unblock the restart signal */
-    sigemptyset(&unblock);
-    sigaddset(&unblock, __pthread_sig_restart);
-    sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
-
-    while (1) {
-        struct timeval now;
-        struct timespec reltime;
-
-        /* Compute a time offset relative to now.  */
-        __gettimeofday (&now, NULL);
-        reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
-        reltime.tv_sec = abstime->tv_sec - now.tv_sec;
-        if (reltime.tv_nsec < 0) {
-          reltime.tv_nsec += 1000000000;
-          reltime.tv_sec -= 1;
-        }
-
-        /* Sleep for the required duration. If woken by a signal,
-           resume waiting as required by Single Unix Specification.  */
-        if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0)
-          break;
-      }
-
-    /* Block the restart signal again */
-    sigprocmask(SIG_SETMASK, &initial_mask, NULL);
-    was_signalled = 0;
-  } else {
-    was_signalled = 1;
-  }
-  THREAD_SETMEM(self, p_signal_jmp, NULL);
-
-  /* Now was_signalled is true if we exited the above code
-     due to the delivery of a restart signal.  In that case,
-     everything is cool. We have been removed from the queue
-     by the other thread, and consumed its signal.
-
-     Otherwise we this thread woke up spontaneously, or due to a signal other
-     than restart. The next thing to do is to try to remove the thread
-     from the queue. This may fail due to a race against another thread
-     trying to do the same. In the failed case, we know we were signalled,
-     and we may also have to consume a restart signal. */
-
-  if (!was_signalled) {
+  if (timedsuspend(self, abstime) == 0) {
     int was_on_queue;
 
     /* __pthread_lock will queue back any spurious restarts that