Created
March 1, 2012 17:34
-
-
Save aprell/1951574 to your computer and use it in GitHub Desktop.
Switching between coroutines/tasks: setjmp/longjmp (single stack)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <setjmp.h> | |
#include <assert.h> | |
#include <unistd.h> | |
#include <time.h> | |
#include "list.h" | |
struct task { | |
jmp_buf env; | |
struct list_head list; | |
}; | |
static LIST_HEAD(tasklist); | |
static void (*tasks[2])(void *); | |
static int ntasks; | |
static jmp_buf sched; | |
static void task_add(struct list_head *tasklist, jmp_buf env) | |
{ | |
struct task *t = malloc(sizeof(*t)); | |
memcpy(t->env, env, sizeof(jmp_buf)); | |
INIT_LIST_HEAD(&t->list); | |
list_add_tail(&t->list, tasklist); | |
} | |
static void task_switch(struct list_head *tasklist) | |
{ | |
jmp_buf env; | |
if (!list_empty(tasklist)) { | |
struct task *t = list_first_entry(tasklist, struct task, list); | |
list_del(&t->list); | |
memcpy(env, t->env, sizeof(jmp_buf)); | |
free(t); | |
longjmp(env, 1); | |
} | |
} | |
static void task_join(struct list_head *tasklist) | |
{ | |
jmp_buf env; | |
while (!list_empty(tasklist)) { | |
struct task *t = list_first_entry(tasklist, struct task, list); | |
list_del(&t->list); | |
memcpy(env, t->env, sizeof(jmp_buf)); | |
free(t); | |
longjmp(env, 1); | |
} | |
} | |
void schedule(void) | |
{ | |
static int i; | |
srand(time(NULL)); | |
setjmp(sched); | |
while (ntasks-- > 0) { | |
int n = rand() % 5; | |
tasks[i++](&n); | |
printf("Never reached\n"); | |
} | |
task_join(&tasklist); | |
} | |
// A task yields control n times | |
void task0(void *arg) | |
{ | |
jmp_buf env; | |
static int n; | |
static int i; | |
n = *(int *)arg; | |
printf("Task 0: n = %d\n", n); | |
if (setjmp(env) == 0) { | |
task_add(&tasklist, env); | |
// Jump back to scheduler | |
longjmp(sched, 1); | |
} | |
for (i = 0; i < n; i++) { | |
if (setjmp(env) == 0) { | |
task_add(&tasklist, env); | |
task_switch(&tasklist); | |
} | |
printf("Task 0: resume\n"); | |
} | |
printf("Task 0: complete\n"); | |
longjmp(sched, 1); | |
} | |
void task1(void *arg) | |
{ | |
jmp_buf env; | |
static int n; | |
static int i; | |
n = *(int *)arg; | |
printf("Task 1: n = %d\n", n); | |
if (setjmp(env) == 0) { | |
task_add(&tasklist, env); | |
// Jump back to scheduler | |
longjmp(sched, 1); | |
} | |
for (i = 0; i < n; i++) { | |
if (setjmp(env) == 0) { | |
task_add(&tasklist, env); | |
task_switch(&tasklist); | |
} | |
printf("Task 1: resume\n"); | |
} | |
printf("Task 1: complete\n"); | |
longjmp(sched, 1); | |
} | |
int main(void) | |
{ | |
tasks[0] = task0; | |
tasks[1] = task1; | |
ntasks = 2; | |
schedule(); | |
return 0; | |
} |
@jonathan-annett :
I believe the list.h which comes from Linux kernel would be the header.
You may use the modified version for userspace if you're not familiar with that.
Sorry @jonathan-annett, I just discovered your comment... What @Ruinland said: The list.h
header comes from the Linux kernel. The version from the link above should work just fine.
This reuses the same stack space for both task0
and task1
, which is why you need to use static
storage duration for the local variables, right? This means task switching is only possible while in the top level function of a task (e.g. in task0
or task1
, but not in someFunctionCalledByTask0
). For that one needs separate stacks.
@musteresel Exactly.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
care to post list.h?