Subject: lib/30734: pthread_once() is not cancellation-safe.
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <tshiozak@netbsd.org>
List: netbsd-bugs
Date: 07/12/2005 06:11:00
>Number: 30734
>Category: lib
>Synopsis: pthread_once() is not cancellation-safe.
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue Jul 12 06:11:00 +0000 2005
>Originator: Takuya SHIOZAKI
>Release: trunk
>Organization:
foo
>Environment:
NetBSD aoi.prod.astec.co.jp 3.99.3 NetBSD 3.99.3 (AOI) #0: Fri Apr 15 22:38:49 JST 2005 tshiozak@aoi.prod.astec.co.jp:/usr/home/current-build/src/sys/arch/i386/compile/AOI i386
>Description:
POSIX says about cancellation handling on pthread_once():
>The function pthread_once() is not a cancellation point. However, if
>init_routine() is a cancellation point and is cancelled, the effect on
>once_control is as if pthread_once() was never called.
but, our pthread_once() do not release the mutex and it may occur deadlock.
>How-To-Repeat:
A test program is here:
#include <pthread.h>
#include <stdio.h>
void
init(void)
{
printf("init: %p\n", (void *)pthread_self());
while (1) {
printf("tick.\n");
sleep(1);
pthread_testcancel();
}
}
pthread_once_t once = PTHREAD_ONCE_INIT;
void *
foo(void *p)
{
printf("foo: %p\n", (void *)pthread_self());
pthread_once(&once, &init);
printf("foo done: %p\n", (void *)pthread_self());
return NULL;
}
int
main()
{
pthread_t t;
printf("1\n");
pthread_create(&t, NULL, &foo, NULL);
printf("2\n");
sleep(1);
printf("3\n");
pthread_cancel(t);
printf("4\n");
pthread_join(t, NULL);
printf("5\n");
pthread_once(&once, &init); /* deadlock at this point. */
printf("6\n");
return 0;
}
>Fix:
Index: pthread_mutex.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_mutex.c,v
retrieving revision 1.18
diff -u -r1.18 pthread_mutex.c
--- pthread_mutex.c 14 Mar 2004 01:19:42 -0000 1.18
+++ pthread_mutex.c 12 Jul 2005 05:59:55 -0000
@@ -450,17 +450,25 @@
}
+static void
+once_cleanup(void *closure)
+{
+
+ pthread_mutex_unlock((pthread_mutex_t *)closure);
+}
+
int
pthread_once(pthread_once_t *once_control, void (*routine)(void))
{
if (once_control->pto_done == 0) {
pthread_mutex_lock(&once_control->pto_mutex);
+ pthread_cleanup_push(&once_cleanup, &once_control->pto_mutex);
if (once_control->pto_done == 0) {
routine();
once_control->pto_done = 1;
}
- pthread_mutex_unlock(&once_control->pto_mutex);
+ pthread_cleanup_pop(1);
}
return 0;