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: