develooper Front page | perl.perl5.porters | Postings from August 2008

How to call perl-callbacks from multithreaded C library?

From:
Josef Wolf
Date:
August 4, 2008 23:40
Subject:
How to call perl-callbacks from multithreaded C library?
Message ID:
20080805063922.GD27925@raven.wolf.lan
Hello,

I have asked over there in perl-ithreads, but it seems that I have
to go to this list.

I want to create an XS interface to a library which by itself creates
multiple threads which are invoking a callback.

Since I don't know how many threads the library creates (maybe even
dynamically), I do not want to allocate/clone multiple interpreters.

From the perlguts manpage I got the impression that I can call from
multiple threads into one interpreter if I make sure that:

1. At any time only one thread is within the interpreter.
   --> so I have to use a mutex, nothing new here.

2. The Thread Local Storage is set up properly before the interpreter
   is called.
   --> so I have to PERL_SET_CONTEXT(my_perl) after acquiring the mutex.

So I wrote a little test program just to check whether my understanding
matches reality.  No surprise: my understanding did _not_ match reality
(I would not post here, then ;-)

Although I made sure that both conditions are satisfied, I keep getting
segfaults at the PUSHMARK(SP) statement when it is called from the
newly created thread.  It works as expected when called from the main
thread (which allocated the perl interpreter).

Any ideas what's missing/going wrong here?

Here's the code:


static SV *callback_ref = (SV*)NULL;

static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

/* This is called by different threads, so it grabs the mutex and
   does PERL_SET_CONTEXT()
   */
static int call_perl (int cnt, ...)
{
    dTHX;
    dSP;
    I32 ax;

    int i;
    I32 numargs;
    va_list args;

    pthread_mutex_lock(&mutex);

    PERL_SET_CONTEXT(my_perl);

    ENTER;
    SAVETMPS;

    /* This PUSHMARK() crashes when called from the newly created thread.
    */
    PUSHMARK(SP);  /* !!!!!! */

    va_start (args, cnt);
    while (cnt-- > 0) {
        char *str = va_arg(args, char*);
        XPUSHs(sv_2mortal (newSVpv (str, strlen (str))));
        fprintf (stderr, "%s\n", str);
    }
    va_end (args);
    PUTBACK;

    numargs = call_sv (callback_ref, G_ARRAY);

    if (numargs != 1)
        croak("Big trouble\n");

    SPAGAIN;

    for (i=0; i<numargs; i++) {
        printf ("ret %i\n", POPi);
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    pthread_mutex_unlock(&mutex);

    return numargs;
}

static pthread_t new_thread;
static void new_main (void *args)
{
    dTHX;

    /* Initially set context (is this needed at all?
     */
    PERL_SET_CONTEXT(my_perl);

    sleep (5);
    call_perl (1, "test");
}

void register_callback (SV *callback)
{
    dTHX;

    if (callback_ref == (SV*)NULL) {
        callback_ref = newSVsv (callback); /* first time, create new SV */
    } else {
        SvSetSV (callback_ref, callback);  /* been here, overwrite */
    }

    pthread_create (&new_thread, NULL, new_main, NULL);

    do {
        sleep (1);
    } while (call_perl (1, "check"));
}



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About