1

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.

peads
  • 13
  • 1
  • 5
  • `void *` is not compatible with function pointers, so you cannot pass in a function pointer as an argument to a function that takes `void *` as an argument. – Christian Gibbons Jun 07 '22 at 21:36
  • 1
    I don't understand what you are trying to do. `bar` always returns NULL so you are always passing NULL as the start routine to `pthread_create`. – kaylum Jun 07 '22 at 21:37
  • 1
    Seems to me like you are trying to create a sort of "closure" in C. That's not how the language works. Your `bar` function always returns `NULL` regardless of its parameter, so the statement `foo = bar(...)` doesn't make much sense anyway. If you want to select between two functions to be executed as thread just make two wrappers for them and use those: `foo = random > 1 ? wrapper_for_baz : wrapper_for_bay` – Marco Bonelli Jun 07 '22 at 21:46
  • @kaylum I'm not interested in the actual return value of `bar`, atm. I want the functionality to be modified based on some arbitrary condition. – peads Jun 07 '22 at 21:48
  • @MarcoBonelli darn. I was hoping that wasn't the case. Oh well. Thank you anyway! – peads Jun 07 '22 at 21:49
  • @peads How can you not be interested in the return value? That's the very thing that is causing the crash. Because it is the NULL return value that is being passed into `pthread_create`. – kaylum Jun 07 '22 at 21:55
  • @kaylum OP seems to think that `bar(random > 1 ? *baz : *bay)` gives a "modified" version of the function `bar` with an already applied parameter of `*baz` or `*bay`... which is not the case. This would be doable in JS with something like `foo = bar.bind(this, random > 1 ? baz : bay)`, not in C though. – Marco Bonelli Jun 07 '22 at 21:59
  • @MarcoBonelli Correct. It's funny that it does almost "work" in that it does the `printf` of whichever function is selected by the pseudo random value, but then, segfaults. I'm assuming it's because it can't determine where to return at runtime after of execution of `bar` completes like the valgrind output suggests. – peads Jun 07 '22 at 22:08
  • @peads no.. the print works waay before the thread even has a chance to start. It's because you are literally calling `bar` which immediately calls `outputFun`, and `outputFun` is just `baz` or `bay`. Then after calling `outputFunc` it returns `NULL`, and the `NULL` ends up being assigned to `foo`. Your `foo` is then passed to `pthread_create` and when it tries to start the thread it literally calls a `NULL` function pointer which crashes as expected. You got the semantics of the whole thing completely wrong, maybe check out some [good C book](https://stackoverflow.com/q/562303/3889449). – Marco Bonelli Jun 07 '22 at 22:15
  • @MarcoBonelli Fwiw, I was basing it on how I'd expect `FunctionalInterface`s to act in Java 8+, but the js example is much more concise way to do it for a comment. – peads Jun 07 '22 at 22:16
  • @MarcoBonelli Interesting. There's the why that I needed. Thanks. Also, if you make it an answer, I'll mark it as closed. – peads Jun 07 '22 at 22:18
  • 1
    See also: [How do I bind arguments to a C function pointer?](https://stackoverflow.com/questions/19304957/how-do-i-bind-arguments-to-a-c-function-pointer) - I think your question could be closed as a duplicate of this one. – Marco Bonelli Jun 07 '22 at 22:18
  • @MarcoBonelli yeah, I was just hoping there were a less verbose way to do it. :/ – peads Jun 07 '22 at 22:20
  • 1
    @peads unfortunately not, maybe a little less verbose would be this solution: [Passing function pointer to arg of pthread_create function](https://stackoverflow.com/questions/49976918/passing-function-pointer-to-arg-of-pthread-create-function), but it's still quite annoying to do this in C. – Marco Bonelli Jun 07 '22 at 22:22

1 Answers1

0

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;
}
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • Thanks. This is effectively what @MarcoBonelli said in the comments to the OP: https://stackoverflow.com/questions/72537774/segfault-when-passing-pthread-a-function-pointer-that-takes-another-function-poi?noredirect=1#comment128138046_72537774 I just wish it weren't so. lol – peads Jun 07 '22 at 22:29
  • @peads When you get proficient with this, you can always do "amazing" things by adding wrapper/helper macros ;-) Note that "closures" in some languages have to create such a struct to contain/control the closure (under the hood). There's no such thing as a "free lunch" even if the language syntax pretends like there is. – Craig Estey Jun 07 '22 at 22:32