Skip to content

Instantly share code, notes, and snippets.

@aprell
Created March 1, 2012 17:34
Show Gist options
  • Save aprell/1951574 to your computer and use it in GitHub Desktop.
Save aprell/1951574 to your computer and use it in GitHub Desktop.
Switching between coroutines/tasks: setjmp/longjmp (single stack)
#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;
}
@musteresel
Copy link

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.

@aprell
Copy link
Author

aprell commented Sep 13, 2019

@musteresel Exactly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment