This is the mail archive of the libc-alpha@sourceware.cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Subtle bug in LinuxThreads pthread_once().


The Single UNIX Spec says that if a thread is cancelled while in the 
callback initialization function invoked from pthread_once, the 
effect on the pthread_once_t controlling variable is as if pthread_once was
never called.

LinuxThreads doesn't do this right; in the rare event that a thread is
cancelled while doing this initialization, the pthread_once_t object is
left in the ``IN_PROGRESS'' state. This state will lead to a deadlock 
the next time pthread_once is called on the object.

Here is a patch. Also, in this patch I took the liberty of moving the
pthread_cond_broadcast in pthread_once outside of the mutex lock
with the help of a flag. Since there is one global mutex for the whole
application, you don't want to be doing more in the critical regions
than absolutely necessary.


--- mutex.c.orig	Tue Mar 14 09:03:15 2000
+++ mutex.c	Tue Mar 14 09:12:51 2000
@@ -173,11 +173,31 @@
 
 enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 };
 
+/* If a thread is canceled while calling the init_routine out of
+   pthread once, this handler will reset the once_control variable
+   to the NEVER state. */
+
+static void pthread_once_cancelhandler(void *arg)
+{
+    pthread_once_t *once_control = arg;
+
+    pthread_mutex_lock(&once_masterlock);
+    *once_control = NEVER;
+    pthread_mutex_unlock(&once_masterlock);
+    pthread_cond_broadcast(&once_finished);
+}
+
 int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void))
 {
+  /* flag for doing the condition broadcast outside of mutex */
+  int state_changed;
+
   /* Test without locking first for speed */
   if (*once_control == DONE) return 0;
   /* Lock and test again */
+
+  state_changed = 0;
+
   pthread_mutex_lock(&once_masterlock);
   /* If init_routine is being called from another routine, wait until
      it completes. */
@@ -188,12 +208,18 @@
   if (*once_control == NEVER) {
     *once_control = IN_PROGRESS;
     pthread_mutex_unlock(&once_masterlock);
+    pthread_cleanup_push(pthread_once_cancelhandler, once_control);
     init_routine();
+    pthread_cleanup_pop(0);
     pthread_mutex_lock(&once_masterlock);
     *once_control = DONE;
-    pthread_cond_broadcast(&once_finished);
+    state_changed = 1;
   }
   pthread_mutex_unlock(&once_masterlock);
+
+  if (state_changed)
+    pthread_cond_broadcast(&once_finished);
+
   return 0;
 }
 strong_alias (__pthread_once, pthread_once)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]