Type cast void*(*)(void*) to void(*)(void)

c, casting, multithreading, pthreads

Solution

It is illegal to invoke a function with an incompatible prototype by casting the address of the function to a different prototype and invoking it through the resulting pointer:

void *my_callback(void *arg) { ... }

void (*broken)(void *) = (void (*)(void *)) my_callback;
broken(some_arg);   // incorrect, my_callback returns a `void *`

What you can do is pass to `makecontext` your own callback which will call `thread_func` and ignore its return value. A small function that only serves to call another function is sometimes called a trampoline.

/* return type is compatible with the prototype of the callback received
   by makecontext; simply calls the real callback */
static void trampoline(int cb, int arg)
{
  void *(*real_cb)(void *) = (void *(*)(void *)) cb;
  void *real_arg = arg;
  real_cb(real_arg);
}

int my_pthread_create(void *(*cb)(void *), void *arg)
{
  ucontext_t *ucp;
  ...
  /* For brevity treating `void *` as the same size as `int` -
     DO NOT USE AS-IS.
     makecontext exposes an annoyingly inconvenient API that only
     accepts int arguments; correct code would deconstruct each
     pointer into two ints (on architectures where pointer is
     larger than int) and reconstruct them in the trampoline. */
  makecontext(ucp, trampoline, 2, (int) cb, (int) arg);
  ...
}

For bonus points, you can modify the trampoline to store the `void *` value returned by the callback function on the stack and have your equivalent of `pthread_join()` retrieve it.

Problem

As a part of an assignment, I am trying to create a user level thread library like pthreads. For handling the context switching between the threads, I am using 'swapcontext' function. Before using it I have to make a context using 'makecontext' function. 'makecontext' expects a function pointer with return type `void` and argument type `(void)`. Whereas, the thread function has to be of the type `void* thread_func (void*)` Is there a way to do a typecasting? Or is there some other way to do context switching at the user level?

Original source