cpthreadsfunction-pointerspthread-join

Segfault when passing pthread a function pointer that takes another function pointer as a parameter


I'm using bog-standard x86-64 Ubuntu gcc, and obviously there are no compile problems since I can get a segfault.

I'm trying to create a pthread that invokes a function that takes another function based on some previously determined condition. That is, I'm trying to modify the function invoked in the thread by partially applying its input function.

Code

#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <stdint.h>

void * (*foo)(void*);
void * (*bar(void (*outputFun)(uint8_t a, short unsigned int b)))(void *);

void baz(u_int8_t a, short unsigned int b) {
    printf("a - b is %d - %d\n", a, b);
}

void bay(u_int8_t a, short unsigned int b) {
    printf("b - a is %d - %d\n", b, a);
}

int main() {
    srand(time(NULL));
    unsigned int random = 1 + rand() % 2;
    printf("Our random number is: %d\n", random);

    foo = bar(random > 1 ? *baz : *bay);

    pthread_t thread;
    pthread_create(&thread, NULL,  foo  , NULL);
    // DO SOME STUFF
    pthread_join(thread, NULL);
}

void * (*bar(void (*outputFun)(uint8_t a, short unsigned int b)))(void *) {
    // DO OTHER STUFF
    outputFun(1, 2);
    return NULL;
}

valgrind Error Output

Our random number is: 2
a - b is 1 - 2
--15699-- REDIR: 0x4914b10 (libc.so.6:calloc) redirected to 0x483dce0 (calloc)
==15699== Thread 2:
==15699== Jump to the invalid address stated on the next line
==15699==    at 0x0: ???
==15699==    by 0x485E608: start_thread (pthread_create.c:477)
==15699==    by 0x4998132: clone (clone.S:95)
==15699==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==15699==
==15699==
==15699== Process terminating with default action of signal 11 (SIGSEGV)
==15699==  Bad permissions for mapped region at address 0x0
==15699==    at 0x0: ???
==15699==    by 0x485E608: start_thread (pthread_create.c:477)
==15699==    by 0x4998132: clone (clone.S:95)
--15699-- REDIR: 0x49136d0 (libc.so.6:free) redirected to 0x483c9d0 (free)
==15699==
==15699== HEAP SUMMARY:
==15699==     in use at exit: 272 bytes in 1 blocks
==15699==   total heap usage: 2 allocs, 1 frees, 1,296 bytes allocated
==15699==
==15699== Searching for pointers to 1 not-freed blocks
==15699== Checked 8,476,712 bytes
==15699==
==15699== LEAK SUMMARY:
==15699==    definitely lost: 0 bytes in 0 blocks
==15699==    indirectly lost: 0 bytes in 0 blocks
==15699==      possibly lost: 272 bytes in 1 blocks
==15699==    still reachable: 0 bytes in 0 blocks
==15699==         suppressed: 0 bytes in 0 blocks
==15699== Rerun with --leak-check=full to see details of leaked memory
==15699==
==15699== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==15699==
==15699== 1 errors in context 1 of 1:
==15699== Jump to the invalid address stated on the next line
==15699==    at 0x0: ???
==15699==    by 0x485E608: start_thread (pthread_create.c:477)
==15699==    by 0x4998132: clone (clone.S:95)
==15699==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==15699==
==15699== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)

Fwiw, I'm a Java developer. So, I need all the help I can get, and it will be greatly appreciated. Thanks.

EDIT Apparently, I can't make C bend to my will.


Solution

  • You can do what you want, just not exactly the way you want.

    Some issues:

    1. With foo = bar(random > 1 ? *baz : *bay); you're trying to create a "closure" which C doesn't have
    2. You can't call a function with (e.g. *baz). You'd need baz()
    3. But, I think you just want the address of the function which is (e.g.) baz

    For threads, the last argument to pthread_create is a pointer that gets passed to the thread function. You can create a struct that has all the information/arguments you require. You pass the address of that struct and the thread function can access it.


    Here is a refactoring to do the equivalent in a more standard way:

    #include <stdio.h>
    #include <pthread.h>
    #include <time.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    typedef void (*outputFun) (uint8_t a, short unsigned int b);
    
    typedef struct {
        u_int8_t a;
        unsigned int b;
        outputFun foo;
    } args_t;
    
    void
    baz(u_int8_t a, short unsigned int b)
    {
        printf("a - b is %d - %d\n", a, b);
    }
    
    void
    bay(u_int8_t a, short unsigned int b)
    {
        printf("b - a is %d - %d\n", b, a);
    }
    
    void *
    bar(void *vp)
    {
        args_t *arg = vp;
    
        // DO OTHER STUFF
    
        arg->foo(arg->a,arg->b);
    
        return NULL;
    }
    
    int
    main(void)
    {
        srand(time(NULL));
        unsigned int random = 1 + rand() % 2;
    
        printf("Our random number is: %d\n", random);
    
        args_t arg;
        arg.foo = (random > 1) ? baz : bay;
        arg.a = 23;
        arg.b = 37;
    
        pthread_t thread;
    
        pthread_create(&thread, NULL, bar, &arg);
    
        // DO SOME STUFF
    
        pthread_join(thread, NULL);
    
        return 0;
    }