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?
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.
In principle, you can always cast any type of pointer to any other kind of pointer, but for function pointers, I would strongly suggest against .
Your thread_func
will expect an argument on the stack which will not be provided if invoked after your miscast. Even worse, thread_func
will write a return value somewhere where it shouldn't, thus corrupting your stack.
A solution would be to wrap the invocation in its own function of appropriate type.
You can typecast a function pointer just like a variable. The syntax is more awkward, but it's certainly possible (whether it's a good idea is another discussion entirely).
In this case, though, it's probably not what you want to do. From the man page for swapcontext
:
Before invoking makecontext(), the caller must allocate a new stack for this context and assign its address to ucp->uc_stack,
Your thread function takes an argument. Pass that argument to the new context via the stack you created. The function that you pass to makecontext()
can be a wrapper function that retrieves the value from the stack and passes it to the thread function as an argument. A typecast alone wouldn't provide a method for getting the data in the argument down to the function in the new context.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.