[Gc] pthreads and libgc

Andy Wingo wingo at pobox.com
Fri Mar 18 12:56:04 PST 2011

Hello list,

I was tracking down a bug in Guile, got to a sticky situation, and could
use some input.  Let me first show you the code.

    #include <unistd.h>
    #include <pthread.h>
    #include <time.h>
    #include <libguile.h>

    static void *thread_with_guile(void *null)
      return NULL;

    static void *the_thread(void *args)
      scm_with_guile (thread_with_guile, NULL);
      return NULL;

    int main(void)
      int n;

      for (n = 0; n < 10000; n++) {
          pthread_t ptid;
          fprintf (stderr, "%d: create", n);
          pthread_create(&ptid, NULL, the_thread, NULL);
          void *ret;
          fprintf (stderr, " join");
          pthread_join(ptid, &ret);
          fprintf (stderr, " finished.\n");

      return 0;

I don't presume that you will compile it, but if you do, you can do so
with "gcc -o test test.c `pkg-config --cflags --libs guile-2.0`".

As you see, it creates a bunch of threads, and does something within
"guile mode" on each of them.  The use case is obviously for some big
threaded application with plugins, and you don't know what thread will
see Guile first.

The first scm_with_guile call will initialize Guile, which does a
GC_INIT, which basically goes like this:

  GC_all_interior_pointers = 0;
  GC_INIT ();

However, libgc appears already to have been initialized when this
happens, automagically, because of pthreads.  If I run the program under
GDB, it happens here:

    Breakpoint 1, GC_init () at misc.c:634
    634	{
    (gdb) thr apply all bt

    Thread 1 (Thread 0x7ffff78df700 (LWP 14166)):
    #0  GC_init () at misc.c:634
    #1  0x0000003fc8e1455e in GC_generic_malloc_inner (lb=56, k=1) at malloc.c:119
    #2  0x0000003fc8e1f4ce in GC_pthread_create (new_thread=0x7fffffffe020, attr=0x0, start_routine=0x4007e3 <the_thread>, 
        arg=0x0) at pthread_support.c:1469
    #3  0x0000000000400853 in main ()

Unhappily though, the initialization is done with
GC_all_interior_pointers == 1.  So later after Guile initializes, it
gets a segfault:

    Program received signal SIGSEGV, Segmentation fault.
    0x0000003fc8e0f6e3 in GC_is_black_listed (h=0x655000, len=<value optimized out>) at blacklst.c:233
    233	      if (get_pht_entry_from_index(GC_old_normal_bl, index)
    (gdb) thr apply all bt

    Thread 2 (Thread 0x7ffff78dd700 (LWP 14169)):
    #0  0x0000003fc8e0f6e3 in GC_is_black_listed (h=0x655000, len=<value optimized out>) at blacklst.c:233
    #1  0x0000003fc8e0d3db in GC_allochblk_nth (sz=16, kind=1, flags=0, n=29, may_split=<value optimized out>) at allchblk.c:694
    #2  0x0000003fc8e0d7dc in GC_allochblk (sz=16, kind=1, flags=0) at allchblk.c:629
    #3  0x0000003fc8e1a2fb in GC_new_hblk (gran=1, kind=1) at new_hblk.c:191
    #4  0x0000003fc8e0f593 in GC_allocobj (gran=1, kind=1) at alloc.c:1312
    #5  0x0000003fc8e1458a in GC_generic_malloc_inner (lb=8, k=1) at malloc.c:126
    #6  0x0000003fc8e12405 in GC_grow_table (table=0x3fc90394c0, log_size_ptr=0x3fc9028b70) at finalize.c:109
    #7  0x0000003fc8e126eb in GC_register_finalizer_inner (obj=0x654ff0, fn=0x7ffff7ca1841 <system_gc_callback>, cd=0x0, 
        ofn=0x0, ocd=0x0, mp=0x3fc8e11400 <GC_null_finalize_mark_proc>) at finalize.c:318
    #8  0x00007ffff7ca183a in scm_c_register_gc_callback (key=0x654ff0, func=0x7ffff7ca1841 <system_gc_callback>, data=0x0)
        at gc.c:554
    #9  0x00007ffff7ca19dc in scm_storage_prehistory () at gc.c:620
    #10 0x00007ffff7cb5e13 in scm_i_init_guile (base=0x7ffff78dcd80) at init.c:397
    #11 0x00007ffff7d27c48 in scm_i_init_thread_for_guile (base=0x7ffff78dcd80, parent=0x0) at threads.c:661
    #12 0x00007ffff7d27da8 in scm_i_with_guile_and_parent (func=0x4007d4 <thread_with_guile>, data=0x0, parent=0x0)
        at threads.c:798
    #13 0x00007ffff7d27d26 in scm_with_guile (func=0x4007d4 <thread_with_guile>, data=0x0) at threads.c:766
    #14 0x00000000004007fe in the_thread ()
    #15 0x0000003fc8e1e435 in GC_inner_start_routine (sb=<value optimized out>, arg=<value optimized out>) at pthread_start.c:61
    #16 0x0000003fc8e18525 in GC_call_with_stack_base (fn=<value optimized out>, arg=<value optimized out>) at misc.c:1491
    #17 0x0000003fc8607761 in start_thread (arg=0x7ffff78dd700) at pthread_create.c:301
    #18 0x0000003fc7ee098d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

    Thread 1 (Thread 0x7ffff78df700 (LWP 14166)):
    #0  0x0000003fc8607fbd in pthread_join (threadid=140737346656000, thread_return=0x7fffffffe018) at pthread_join.c:89
    #1  0x0000003fc8e1edd7 in GC_pthread_join (thread=140737346656000, retval=0x7fffffffe018) at pthread_support.c:1214
    #2  0x000000000040088a in main ()

In this case `GC_old_normal_bl' is NULL, as it is only initialized to a
valid value if GC_all_interior_pointers is zero, which was not the case
when libgc was automatically initialized.

What should we do here?



More information about the Gc mailing list