Skip to content

Commit e2cdfe2

Browse files
committed
Add fiber as an example of clone system call
1 parent a05fe04 commit e2cdfe2

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Project Listing
44
- [tpool](tpool/): A lightweight thread pool.
55
- [tinync](tinync/): A tiny `nc` implementation using coroutine.
6+
- [fiber](fiber/): A user-level thread (fiber) using `clone` system call.
67
- [picosh](picosh/): A minimalist UNIX shell.
78
- [httpd](httpd/): A multi-threaded web server.
89
- [ringbuffer](ringbuffer/): A lock-less ring buffer.

fiber/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
all:
2+
$(CC) -Wall -Wextra -o fiber fiber.c
3+
4+
clean:
5+
rm -f fiber

fiber/fiber.c

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#define FIBER_NOERROR 0
2+
#define FIBER_MAXFIBERS 1
3+
#define FIBER_MALLOC_ERROR 2
4+
#define FIBER_CLONE_ERROR 3
5+
#define FIBER_INFIBER 4
6+
7+
/* The maximum number of fibers that can be active at once. */
8+
#define MAX_FIBERS 10
9+
10+
/* The size of the stack for each fiber. */
11+
#define FIBER_STACK (1024 * 1024)
12+
13+
#define _GNU_SOURCE
14+
15+
#include <sched.h> /* For clone */
16+
#include <stdio.h>
17+
#include <stdlib.h>
18+
#include <sys/types.h> /* For pid_t */
19+
#include <sys/wait.h> /* For wait */
20+
#include <unistd.h> /* For getpid */
21+
22+
typedef struct {
23+
pid_t pid; /* The pid of the child thread as returned by clone */
24+
void *stack; /* The stack pointer */
25+
} fiber_t;
26+
27+
/* The fiber "queue" */
28+
static fiber_t fiber_list[MAX_FIBERS];
29+
30+
/* The pid of the parent process */
31+
static pid_t parent;
32+
33+
/* The number of active fibers */
34+
static int num_fibers = 0;
35+
36+
void fiber_init()
37+
{
38+
for (int i = 0; i < MAX_FIBERS; ++i)
39+
fiber_list[i].pid = 0, fiber_list[i].stack = 0;
40+
parent = getpid();
41+
}
42+
43+
/* Yield control to another execution context */
44+
void fiber_yield()
45+
{
46+
/* move the current process to the end of the process queue. */
47+
sched_yield();
48+
}
49+
50+
struct fiber_args {
51+
void (*func)(void);
52+
};
53+
54+
static int fiber_start(void *arg)
55+
{
56+
struct fiber_args *args = (struct fiber_args *) arg;
57+
void (*func)() = args->func;
58+
free(args);
59+
60+
func();
61+
return 0;
62+
}
63+
64+
/* Creates a new fiber, running the func that is passed as an argument. */
65+
int fiber_spawn(void (*func)(void))
66+
{
67+
if (num_fibers == MAX_FIBERS)
68+
return FIBER_MAXFIBERS;
69+
70+
if ((fiber_list[num_fibers].stack = malloc(FIBER_STACK)) == 0)
71+
return FIBER_MALLOC_ERROR;
72+
73+
struct fiber_args *args;
74+
if ((args = malloc(sizeof(*args))) == 0) {
75+
free(fiber_list[num_fibers].stack);
76+
return FIBER_MALLOC_ERROR;
77+
}
78+
args->func = func;
79+
80+
fiber_list[num_fibers].pid = clone(
81+
fiber_start, (char *) fiber_list[num_fibers].stack + FIBER_STACK,
82+
SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM, args);
83+
if (fiber_list[num_fibers].pid == -1) {
84+
free(fiber_list[num_fibers].stack);
85+
free(args);
86+
return FIBER_CLONE_ERROR;
87+
}
88+
89+
num_fibers++;
90+
return FIBER_NOERROR;
91+
}
92+
93+
/* Execute the fibers until they all quit. */
94+
int fiber_wait_all()
95+
{
96+
/* Check to see if we are in a fiber, since we do not get signals in the
97+
* child threads
98+
*/
99+
pid_t pid = getpid();
100+
if (pid != parent)
101+
return FIBER_INFIBER;
102+
103+
/* Wait for the fibers to quit, then free the stacks */
104+
while (num_fibers > 0) {
105+
if ((pid = wait(0)) == -1)
106+
exit(1);
107+
108+
/* Find the fiber, free the stack, and swap it with the last one */
109+
for (int i = 0; i < num_fibers; ++i) {
110+
if (fiber_list[i].pid == pid) {
111+
free(fiber_list[i].stack);
112+
if (i != --num_fibers)
113+
fiber_list[i] = fiber_list[num_fibers];
114+
break;
115+
}
116+
}
117+
}
118+
119+
return FIBER_NOERROR;
120+
}
121+
122+
static void fibonacci()
123+
{
124+
int fib[2] = {0, 1};
125+
printf("Fib(0) = 0\nFib(1) = 1\n");
126+
for (int i = 2; i < 15; ++i) {
127+
int next = fib[0] + fib[1];
128+
printf("Fib(%d) = %d\n", i, next);
129+
fib[0] = fib[1];
130+
fib[1] = next;
131+
fiber_yield();
132+
}
133+
}
134+
135+
static void squares()
136+
{
137+
for (int i = 1; i < 10; ++i) {
138+
printf("%d * %d = %d\n", i, i, i * i);
139+
fiber_yield();
140+
}
141+
}
142+
143+
int main()
144+
{
145+
fiber_init();
146+
147+
fiber_spawn(&fibonacci);
148+
fiber_spawn(&squares);
149+
150+
/* Since these are non-preemptive, we must allow them to run */
151+
fiber_wait_all();
152+
153+
return 0;
154+
}

0 commit comments

Comments
 (0)