I'm trying to simulate threads swapcontext() and whatnot but I have some issues:
here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <stdarg.h>
#define STACK_SIZE 1024
typedef struct thread {
ucontext_t context;
void* stack;
int done;
} thread_t;
thread_t thread1, thread2;
void thread1_function(int argc, ...) {
va_list args;
va_start(args, argc);
for (int i = 0; i < 10; i++) {
printf("Thread 1 count: %d\n", i);
swapcontext(&thread1.context, &thread2.context);
}
thread1.done = 1;
printf("Thread 1 done\n");
va_end(args);
}
void thread2_function() {
for (int i = 0; i < 10; i++) {
printf("Thread 2 count: %d\n", i);
swapcontext(&thread2.context, &thread1.context);
}
printf("Thread 2 done\n");
thread2.done = 1;
}
int main() {
// Allocate stacks for each thread
thread1.stack = malloc(STACK_SIZE);
thread2.stack = malloc(STACK_SIZE);
// Initialize the execution context for each thread
getcontext(&thread1.context);
thread1.context.uc_stack.ss_sp = thread1.stack;
thread1.context.uc_stack.ss_size = STACK_SIZE;
makecontext(&thread1.context, (void (*)())thread1_function, 1, 2, 0);
getcontext(&thread2.context);
thread2.context.uc_stack.ss_sp = thread2.stack;
thread2.context.uc_stack.ss_size = STACK_SIZE;
makecontext(&thread2.context, (void (*)())thread2_function, 0);
// Switch to thread1
swapcontext(&thread1.context, &thread2.context);
// Free stacks
free(thread1.stack);
free(thread2.stack);
return 0;
}
When I run the code is supposed to run both my callback function instead of I get below error and without the function at all (as far i know):
Thread 2 count: 0 free(): invalid size Aborted
In my tests, STACK_SIZE
must be set to at least 6 KB, otherwise I get the error message free(): invalid size
or a segmentation fault. Therefore, I recommend setting STACK_SIZE
to at least 32 KB in order to be reasonably sure that the stack size is sufficient.
In your posted code, you set up
thread1.context
to run the function thread1_function
andthread2.context
to run the function thread2_function
.However, in the line
swapcontext(&thread1.context, &thread2.context);
of the function main
, you overwrite thread1.context
with the current context and execute thread2.context
. This means that thread1.context
is no longer set up to execute the function thread1_function
, but is instead set up to execute the remainder of main
.
Because you are now executing thread2.context
, the function thread2_function
will be executed. This function will first print
Thread 2 count: 0
and then it will execute this line:
swapcontext(&thread2.context, &thread1.context);
As previously stated, thread1.context
is no longer set up to execute the function thread1_function
, but is instead set up to execute the remainder of main
. Therefore, after calling swapcontext
, the program will execute these lines of main
:
// Free stacks
free(thread1.stack);
free(thread2.stack);
return 0;
These lines will terminate the program, causing nothing else to be printed.
In order to fix this, you should not store the context of main
in thread1.context
. It should be stored in a separate object.
Also, if you don't set the value of thread1.context.uclink
and thread2.context.uclink
to a non-zero value, then the program will terminate as soon as thread1_function
or thread2_function
returns.
One possible solution is to set the member uclink
to the context that is executing the function main
. The function main
can then be programmed to terminate if both threads are done, otherwise it will set the context to the thread that is not yet done.
Another issue is that the line
swapcontext(&thread1.context, &thread2.context);
in the function thread1_function
is not meaningful if thread 2 is already done, because in that case, setting the context to thread 2 will cause thread 2 to repeat its final time slice (with the stack that has already been used by the final time slice of thread 2, which may cause the program to crash).
For this reason, that line should be changed to
if ( !thread2.done ) {
swapcontext(&thread1.context, &thread2.context);
}
The function thread2_function
has the same problem.
Here is a solution which fixes all errors mentioned above:
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <stdarg.h>
#define STACK_SIZE 32768
typedef struct thread {
ucontext_t context;
void* stack;
int done;
} thread_t;
thread_t thread1, thread2;
ucontext_t main_context;
void thread1_function(int argc, ...) {
va_list args;
va_start(args, argc);
for (int i = 0; i < 10; i++) {
printf("Thread 1 count: %d\n", i);
if ( !thread2.done ) {
swapcontext(&thread1.context, &thread2.context);
}
}
thread1.done = 1;
printf("Thread 1 done\n");
va_end(args);
}
void thread2_function() {
for (int i = 0; i < 10; i++) {
printf("Thread 2 count: %d\n", i);
if ( !thread1.done ) {
swapcontext(&thread2.context, &thread1.context);
}
}
printf("Thread 2 done\n");
thread2.done = 1;
}
int main() {
// Allocate stacks for each thread
thread1.stack = malloc(STACK_SIZE);
thread2.stack = malloc(STACK_SIZE);
// Initialize the execution context for each thread
getcontext(&thread1.context);
thread1.context.uc_stack.ss_sp = thread1.stack;
thread1.context.uc_stack.ss_size = STACK_SIZE;
thread1.context.uc_link = &main_context;
makecontext(&thread1.context, (void (*)())thread1_function, 1, 2, 0);
getcontext(&thread2.context);
thread2.context.uc_stack.ss_sp = thread2.stack;
thread2.context.uc_stack.ss_size = STACK_SIZE;
thread2.context.uc_link = &main_context;
makecontext(&thread2.context, (void (*)())thread2_function, 0);
// Switch to thread1
swapcontext( &main_context, &thread1.context );
// If a thread is still running, set context to that thread
if ( !thread1.done ) {
setcontext( &thread1.context );
}
if ( !thread2.done ) {
setcontext( &thread2.context );
}
// Free stacks
free(thread1.stack);
free(thread2.stack);
return 0;
}
This program has the following output:
Thread 1 count: 0
Thread 2 count: 0
Thread 1 count: 1
Thread 2 count: 1
Thread 1 count: 2
Thread 2 count: 2
Thread 1 count: 3
Thread 2 count: 3
Thread 1 count: 4
Thread 2 count: 4
Thread 1 count: 5
Thread 2 count: 5
Thread 1 count: 6
Thread 2 count: 6
Thread 1 count: 7
Thread 2 count: 7
Thread 1 count: 8
Thread 2 count: 8
Thread 1 count: 9
Thread 2 count: 9
Thread 1 done
Thread 2 done