Skip to content

Commit 1d26c11

Browse files
committed
Add coroutine implementation with setjmp/longjmp
1 parent 7823a4a commit 1d26c11

File tree

4 files changed

+577
-0
lines changed

4 files changed

+577
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ purpose of these programs is to be illustrative and educational.
66

77
## Project Listing
88
* [Coroutine](https://en.wikipedia.org/wiki/Coroutine)
9+
- [coro](coro/): A coroutine implementation with setjmp/longjmp.
910
- [tinync](tinync/): A tiny `nc` implementation using coroutine.
1011
- [fiber](fiber/): A user-level thread (fiber) using `clone` system call.
1112
- [preempt\_sched](preempt_sched/): A preemptive userspace multitasking based on a SIGALRM signal.

coro/Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CFLAGS = -O2 -Wall
2+
all:
3+
gcc $(CFLAGS) -o coro coro.c
4+
5+
clean:
6+
rm -f coro

coro/coro.c

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/* Implementing coroutines with setjmp/longjmp */
2+
3+
#include <setjmp.h>
4+
#include <stdint.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
#include "list.h"
9+
10+
struct task {
11+
jmp_buf env;
12+
struct list_head list;
13+
};
14+
15+
static LIST_HEAD(tasklist);
16+
static void (**tasks)(void *);
17+
static int ntasks;
18+
static jmp_buf sched;
19+
20+
static void task_add(struct list_head *tasklist, jmp_buf env)
21+
{
22+
struct task *t = malloc(sizeof(*t));
23+
memcpy(t->env, env, sizeof(jmp_buf));
24+
INIT_LIST_HEAD(&t->list);
25+
list_add_tail(&t->list, tasklist);
26+
}
27+
28+
static void task_switch(struct list_head *tasklist)
29+
{
30+
jmp_buf env;
31+
32+
if (!list_empty(tasklist)) {
33+
struct task *t = list_first_entry(tasklist, struct task, list);
34+
list_del(&t->list);
35+
memcpy(env, t->env, sizeof(jmp_buf));
36+
free(t);
37+
longjmp(env, 1);
38+
}
39+
}
40+
41+
static void task_join(struct list_head *tasklist)
42+
{
43+
jmp_buf env;
44+
45+
while (!list_empty(tasklist)) {
46+
struct task *t = list_first_entry(tasklist, struct task, list);
47+
list_del(&t->list);
48+
memcpy(env, t->env, sizeof(jmp_buf));
49+
free(t);
50+
longjmp(env, 1);
51+
}
52+
}
53+
54+
void schedule(void)
55+
{
56+
static int i;
57+
58+
srand(0xCAFEBABE ^ (uintptr_t) &schedule); /* Thanks to ASLR */
59+
60+
setjmp(sched);
61+
62+
while (ntasks-- > 0) {
63+
int n = rand() % 5;
64+
tasks[i++](&n);
65+
printf("Never reached\n");
66+
}
67+
68+
task_join(&tasklist);
69+
}
70+
71+
/* A task yields control n times */
72+
73+
void task0(void *arg)
74+
{
75+
jmp_buf env;
76+
static int n;
77+
n = *(int *) arg;
78+
79+
printf("Task 0: n = %d\n", n);
80+
81+
if (setjmp(env) == 0) {
82+
task_add(&tasklist, env);
83+
// Jump back to scheduler
84+
longjmp(sched, 1);
85+
}
86+
87+
for (int i = 0; i < n; i++) {
88+
if (setjmp(env) == 0) {
89+
task_add(&tasklist, env);
90+
task_switch(&tasklist);
91+
}
92+
printf("Task 0: resume\n");
93+
}
94+
95+
printf("Task 0: complete\n");
96+
longjmp(sched, 1);
97+
}
98+
99+
void task1(void *arg)
100+
{
101+
jmp_buf env;
102+
static int n;
103+
n = *(int *) arg;
104+
105+
printf("Task 1: n = %d\n", n);
106+
107+
if (setjmp(env) == 0) {
108+
task_add(&tasklist, env);
109+
// Jump back to scheduler
110+
longjmp(sched, 1);
111+
}
112+
113+
for (int i = 0; i < n; i++) {
114+
if (setjmp(env) == 0) {
115+
task_add(&tasklist, env);
116+
task_switch(&tasklist);
117+
}
118+
printf("Task 1: resume\n");
119+
}
120+
121+
printf("Task 1: complete\n");
122+
longjmp(sched, 1);
123+
}
124+
125+
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
126+
int main(void)
127+
{
128+
void (*registered_task[])(void *) = {task0, task1};
129+
tasks = registered_task;
130+
ntasks = ARRAY_SIZE(registered_task);
131+
132+
schedule();
133+
134+
return 0;
135+
}

0 commit comments

Comments
 (0)