Type cast void * (*) (void *) for void (*) (void)

As part of the assignment, I am trying to create a user-level thread library such as pthreads.

To handle context switching between threads, I use the "swapcontext" function. Before using it, I have to create a context using the "makecontext" function. 'makecontext' expects a function pointer with a return type of void and an argument type (void) .

While the thread function should be of type void* thread_func (void*)

Is there a way to do type casts? Or is there another way to switch context at the user level?

+4
source share
3 answers

It is wrong to call a function with an incompatible prototype, discarding the address of the function to another prototype and calling 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 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 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 change the trampoline to save the void * value returned by the callback function on the stack and get its equivalent pthread_join() .

+7
source

Basically, you can always point any type of pointer to any other pointer, but for function pointers I would highly recommend against .

Your thread_func expects an argument on the stack that will not be provided if it is called after your error. Even worse, thread_func will write the return value somewhere where it should not, thus distorting your stack.

The solution would be to wrap the call in its own function of the appropriate type.

+4
source

You can display a pointer to a function as a variable. The syntax is more inconvenient, but it is certainly possible (a good idea is another discussion).

In this case, most likely, this is not what you want to do. On the man page for swapcontext :

Before calling makecontext (), the caller must allocate a new stack for this context and assign your address ucp-> uc_stack,

Your stream function takes an argument. Pass this argument to the new context through the created stack. The function you pass to makecontext() can be a wrapper function that fetches a value from the stack and passes it to the stream function as an argument. The given type would not provide a way to get the data in the argument before the function in a new context.

0
source

All Articles