|
From: Julian S. <js...@ac...> - 2003-06-14 09:08:25
|
Alas, this is a bit subtle. pthread_once has always been broken and
is still broken. As of rev 1.127 it looks like this:
static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER;
int __pthread_once ( pthread_once_t *once_control,
void (*init_routine) (void) )
{
int res;
ensure_valgrind("pthread_once");
res = __pthread_mutex_lock(&once_masterlock);
/* init routine called us again ? */
if (res != 0)
return 0;
if (*once_control == 0) {
*once_control = 1;
init_routine();
}
__pthread_mutex_unlock(&once_masterlock);
return 0;
}
Problem with this is "if (res != 0) return 0;". If the init_routine()
calls back here with the _same_ once_control, that's ok. But if it
calls with a different one, iow the init_routine is calling some other
init routine via pthread_once, then __pthread_once returns immediately
without running the new init routine.
An alternative formulation which has been suggested is
{
int res;
ensure_valgrind("pthread_once");
res = __pthread_mutex_lock(&once_masterlock);
if (*once_control == 0) {
*once_control = 1;
__pthread_mutex_unlock(&once_masterlock);
init_routine();
} else {
__pthread_mutex_unlock(&once_masterlock);
}
return 0;
}
This initially seems more promising. The global lock now protects just
the control variable. If init_routine calls back recursively with the
same once_control, the right thing happens (nothing). If it calls back
with some other once_control/init_routine pair, that is handled correctly
too. The problem is it fails to ensure the posix semantics that
2nd and subsequent callers with the same once_control are blocked
until the first caller returns.
The Linux manual pages are too vague and fail to mention this last constraint.
Go to http://www.opengroup.org/onlinepubs/007904975/toc.htm and enter
pthread_once in the search box, to get the official POSIX definition.
Ideas? LinuxThreads seems to have a 3-state mechanism, where *once_control
can be 0, 1 or 2. 2 means completed. 0 means not started. 1 means the init
routine is still running, and so other callers have to block. It's all
complicated with stuff to do with handling fork() and cancellation points,
that I don't understand. Also I don't know whether or not LinuxThreads
uses one global lock, or one per thread.
J
On Friday 13 June 2003 13:24, Dirk Mueller wrote:
> Update of /cvsroot/valgrind/valgrind/coregrind
> In directory sc8-pr-cvs1:/tmp/cvs-serv8179
>
> Modified Files:
> vg_libpthread.c
> Log Message:
> don't bail out when pthread_once's init route calls itself recursively
>
>
> Index: vg_libpthread.c
> ===================================================================
> RCS file: /cvsroot/valgrind/valgrind/coregrind/vg_libpthread.c,v
> retrieving revision 1.126
> retrieving revision 1.127
> diff -C2 -r1.126 -r1.127
> *** vg_libpthread.c 9 May 2003 23:40:34 -0000 1.126
> --- vg_libpthread.c 13 Jun 2003 12:24:41 -0000 1.127
> ***************
> *** 1501,1508 ****
> res = __pthread_mutex_lock(&once_masterlock);
>
> ! if (res != 0) {
> ! barf("pthread_once: Looks like your program's "
> ! "init routine calls back to pthread_once() ?!");
> ! }
>
> if (*once_control == 0) {
> --- 1501,1507 ----
> res = __pthread_mutex_lock(&once_masterlock);
>
> ! /* init routine called us again ? */
> ! if (res != 0)
> ! return 0;
>
> if (*once_control == 0) {
|