[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#342574: pthread_cond_timedwait changes canceltype to ASYNCHRONOUS



Package: libc6
Version: 2.3.2.ds1-22

The linuxthread's pthread_cond_timedwait force changes canceltype to
ASYNCHRONOUS if timeout occurred.  NPTL's one seems OK.

This is a test code.

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>

pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

void *func(void *arg)
{
	sleep(1);
	pthread_mutex_lock(&m);
	pthread_cond_broadcast(&cv);
	pthread_mutex_unlock(&m);
	return NULL;
}

int main(int argc, char **argv)
{
	pthread_t tid;
	struct timespec ts;
	struct timeval tv;
	int before, after;

	pthread_create(&tid, NULL, func, NULL);
	gettimeofday(&tv, NULL);
	ts.tv_sec = tv.tv_sec + 10;
	ts.tv_nsec = 0;
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &before);
	pthread_mutex_lock(&m);
	pthread_cond_timedwait(&cv, &m, &ts);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &after);
	pthread_mutex_unlock(&m);
	pthread_join(tid, NULL);
	printf("before = %d, after = %d\n", before, after);
	return 0;
}

And results:

$ ./foo
before = 0, after = 0
$ LD_ASSUME_KERNEL=2.4.1 ./foo
before = 0, after = 1

The canceltype was 0 (DEFERRED) before pthread_cond_timedwait, and
becomes 1 (ASYNCHRONOUS) after timeout.

This is because pthread_cond_timedwait internally calls nanosleep
which is cancellation point.  The nanosleep temporarily changes
canceltype to ASYNCHRONOUS, and it was not reverted if timeout
occurred.

In glibc 2.3.5, it seems read_not_cancel, etc. are used to avoid such
an unexpected side-effects.  This was introduced by:

http://sources.redhat.com/ml/libc-hacker/2004-05/msg00003.html

But nanosleep was not replaced with *_not_cancel version.

Here is a proposal patch for 2.3.5-8.1

diff -ur glibc-2.3.5-8.1/sysdeps/unix/sysv/linux/not-cancel.h glibc-2.3.5/sysdeps/unix/sysv/linux/not-cancel.h
--- glibc-2.3.5-8.1/sysdeps/unix/sysv/linux/not-cancel.h	2003-09-04 23:05:12.000000000 +0900
+++ glibc-2.3.5/sysdeps/unix/sysv/linux/not-cancel.h	2005-12-09 01:42:02.593631864 +0900
@@ -58,3 +58,7 @@
 # define waitpid_not_cancel(pid, stat_loc, options) \
   INLINE_SYSCALL (wait4, 4, pid, stat_loc, options, NULL)
 #endif
+
+/* Uncancelable nanosleep.  */
+#define nanosleep_not_cancel(req, rem) \
+  INLINE_SYSCALL (nanosleep, 2, (req), (rem))
diff -ur glibc-2.3.5-8.1/linuxthreads/pthread.c glibc-2.3.5/linuxthreads/pthread.c
--- glibc-2.3.5-8.1/linuxthreads/pthread.c	2005-12-09 01:40:08.942909000 +0900
+++ glibc-2.3.5/linuxthreads/pthread.c	2005-12-09 01:42:02.594631712 +0900
@@ -1274,7 +1274,7 @@
 
 	/* 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)
+	if (reltime.tv_sec < 0 || nanosleep_not_cancel(&reltime, NULL) == 0)
 	  break;
       }
 
@@ -1361,7 +1361,7 @@
 
       /* 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)
+      if (reltime.tv_sec < 0 || nanosleep_not_cancel(&reltime, NULL) == 0)
 	break;
     }
 
diff -ur glibc-2.3.5-8.1/linuxthreads/spinlock.c glibc-2.3.5/linuxthreads/spinlock.c
--- glibc-2.3.5-8.1/linuxthreads/spinlock.c	2005-12-09 01:40:08.943909000 +0900
+++ glibc-2.3.5/linuxthreads/spinlock.c	2005-12-09 01:42:02.594631712 +0900
@@ -23,6 +23,7 @@
 #include "internals.h"
 #include "spinlock.h"
 #include "restart.h"
+#include <not-cancel.h>
 
 static void __pthread_acquire(__atomic_lock_t * spinlock);
 
@@ -713,7 +714,7 @@
     } else {
       tm.tv_sec = 0;
       tm.tv_nsec = SPIN_SLEEP_DURATION;
-      nanosleep(&tm, NULL);
+      nanosleep_not_cancel(&tm, NULL);
       cnt = 0;
     }
   }



Reply to: