Search code examples
phpcmultithreadingphp-internals

Cannot invoke another function within a PHP_FUNCTION() in C


I need to call a function within a PHP_FUNCTION() function in C to extend PHP, this is a multithread script and The function itself works perfectly using int main(). Here is what I try to achieve.

#define NUM_THREADS 3
char *messages[NUM_THREADS];

void *PrintHello(void *threadid)
{
    zend_printf("gP");
    int *id_ptr, taskid;
    sleep(4);
    id_ptr = (int *) threadid;
    taskid = *id_ptr;
    zend_printf("Thread %d: %s\n", taskid, messages[taskid]);
    pthread_exit(NULL);
}


PHP_FUNCTION(hello_world)
{
    pthread_t threads[NUM_THREADS];
    int *taskids[NUM_THREADS];
    int rc, t;
    messages[0] = "English: Hello World!";
    messages[1] = "French: Bonjour, le monde!";
    messages[2] = "Spanish: Hola al mundo";

    for(t=0; t < NUM_THREADS; t++)
    {
        taskids[t] = (int *) malloc(sizeof(int));
        *taskids[t] = t;

        zend_printf("Creating thread %d\n <br>", t);
        rc = pthread_create(&threads[t], NULL, (void* (*) (void*)) pthreads_routine, (void *) taskids[t]);


        if (rc) {
            zend_printf("ERR; pthread_create() ret = %d\n", rc);
        }
    }
}

I need to call PrintHello() function from
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);

Do I also need to register void *PrintHello(void *threadid) in

const zend_function_entry my_functions[] = {
    PHP_FE(hello_world, NULL)
    PHP_FE_END
};

the var_dump() out put is

Creating thread 0 
Creating thread 1 
Creating thread 2 
NULL

at the top of void *PrintHello(void *threadid) function I have included zend_printf("gP"); line to make sure that the function is invoked and from the looks of out put it is obvious that the function is not invoked.

My environment is Mac OSX , xCode 7.2, PHP 7.0.1
What am I doing wrong?


Solution

  • It appears you have two problems in your code, both of which explain why you're not getting any output.

    1) At least in my tests, it appears that zend_printf -> php_printf -> vspprintf is not thread safe. Your code always crashes for me once one of the threads attempts to call zend_printf(). But, even if that weren't the case, there's also:

    2) Assuming your php code looks like this:

    <?php
    hello_world();
    

    What's happening is that when you call pthread_create(), it returns immediately having created the thread, though the thread has not necessarily started to run. Then, hello_world returns once all the threads have been created. Then, your main thread ends because there's nothing else to do.

    Once the main thread ends, your spawned threads are immediately terminated. If you're seeing nothing at all, that's because the main thread ends before any of the pthreads are actually scheduled, before they even execute your zend_printf("gP"); line.

    If you change your php code to:

    <?php
    hello_world();
    sleep(10);
    

    Then you give the child threads enough time to be scheduled and given CPU time (at which point they will probably crash calling the first zend_printf), and if not, give them enough time to make it past the sleep(4) to get to the zend_printf(Thread id).

    If you replace your PrintHello with this:

    void *PrintHello(void *threadid)
    {
        int *id_ptr, taskid;
        id_ptr = (int *) threadid;
        taskid = *id_ptr;
    
        printf("Thread %d: start\n", taskid, messages[taskid]);
    
        sleep(4);
        printf("Thread %d: %s\n", taskid, messages[taskid]);
        pthread_exit(NULL);
    }
    

    (replace the zend_printf with regular printf), then you'll get your desired output, at least on the cli.