[Gc] GC_thread_exit_proc isn't called with pthread_exit() on Ubuntu/i386

Shiro Kawai shiro at lava.net
Tue Nov 3 13:01:22 PST 2009


This could be a glibc problem but I couldn't reproduce it without
libgc, so I post here to see if anybody else has similar experience.

On i386 machine with glibc-2.7 that comes with Ubuntu 8.10,
GC_thread_exit_proc is never called when the thread is terminated
by pthread_exit() or cancelled by pthread_cancel().

Curiously, other cleanup procedures I set with pthread_cleanup_push
within the thread function are called.

The situation can be exhibited with the attached two files, with
GC CVS HEAD.  I'll explain the way in details below.

This causes libgc deadlock.  If GC_thread_exit_proc isn't
called the thread isn't unregistered from GC.  Subsequent
pthread_create allocates a new thread with the same thread id,
which is registered to GC.  When GC tries to stop the world,
it thinks both threads are alive and sends suspend signal to
these two threads, even if they have the same thread id.  The
first signal suspend the live thread that waits for restart
signal.  The GC triggering thread waits for the ack of the
second suspend signal that never comes.

I searched glibc archives to see if this problem is acknowledged
and/or fixed, but couldn't find relevant info.  I couldn't
make a simpler program without libgc to reproduce this either.

Even if this is a glibc problem, libgc can workaround this
by removing duplicate entry when it registers a new thread.
But I like to check the cause before writing such workaround.



How to exhibit the problem:

* Place cleanup_test.c and cleanup_test.mk on the root of GC source tree.
* Insert printf stub (e.g. priting "GC_thread_exit_proc\n") in
   GC_thread_exit_proc in pthread_support.c and rebuild libgc.
* Run the test program with 'make -f cleanup_test.mk'.

It produces the following output on Ubuntu 8.10/i386.  You see
GC_thread_exit_proc isn't called when pthread_exit() is used,
while innner cleanup routine is called.

=== do_exit start
thread_body_exit
inner cleanup called
=== do_exit end
=== do_normal start
thread_body_normal
inner cleanup called
GC_thread_exit_proc
=== do_normal end

The symptom does not appear on Ubuntu 8.04.3/x86_64.

=== do_exit start
thread_body_exit
inner cleanup called
GC_thread_exit_proc
=== do_exit end
=== do_normal start
thread_body_normal
inner cleanup called
GC_thread_exit_proc
=== do_normal end


--shiro
-------------- next part --------------
#define GC_THREADS
#include "gc.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void do_exit(void)
{
    fprintf(stderr, "thread_body_exit\n");
    pthread_exit(NULL);
}

void do_normal(void)
{
    fprintf(stderr, "thread_body_normal\n");
}

void inner_cleanup(void *arg)
{
    fprintf(stderr, "inner cleanup called\n");
}

void *thread_body(void *arg)
{
    void (*fn)(void) = (void (*)(void))arg;
    pthread_cleanup_push(inner_cleanup, NULL);
    fn();
    pthread_cleanup_pop(1);
    return NULL;
}

int main(int argc, char **argv)
{
    pthread_t thr;
    void *r;
    
    GC_INIT();

    fprintf(stderr, "=== do_exit start\n");
    if (pthread_create(&thr, NULL, thread_body, do_exit) != 0) {
        perror("pthread_create"); exit(1);
    }
    if (pthread_join(thr, &r) != 0) {
        perror("pthread_join"); exit(1);
    }
    fprintf(stderr, "=== do_exit end\n");

    fprintf(stderr, "=== do_normal start\n");
    if (pthread_create(&thr, NULL, thread_body, do_normal) != 0) {
        perror("pthread_create"); exit(1);
    }
    if (pthread_join(thr, &r) != 0) {
        perror("pthread_join"); exit(1);
    }
    fprintf(stderr, "=== do_normal end\n");

    return 0;
}

-------------- next part --------------

run: cleanup_test
	./libtool --mode=execute ./cleanup_test

cleanup_test : cleanup_test.c libgc.la
	./libtool --tag=CC --mode=link cc -I./include -L. -o cleanup_test cleanup_test.c -lgc -lpthread


More information about the Gc mailing list