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;
}
@jonathan-annett
Copy link

care to post list.h?

@Ruinland
Copy link

Ruinland commented Aug 2, 2017

@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.

@aprell
Copy link
Author

aprell commented Sep 15, 2017

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.

@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