summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-18 00:04:10 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-09-18 00:04:10 -0600
commit690a9d955e65608b96fad34a2b31e639223798aa (patch)
tree60bf87a326b6433297ee86e8d039199e4d4becdb
parent4412b908755c7b9899cf8cce4e0985aa6816121a (diff)
wip
-rw-r--r--.gitignore1
-rw-r--r--9psrv.c11
-rw-r--r--cfg_limits.h13
-rw-r--r--coroutine.c211
-rw-r--r--coroutine.h77
-rw-r--r--net9p.c53
-rw-r--r--net9p.h8
7 files changed, 237 insertions, 137 deletions
diff --git a/.gitignore b/.gitignore
index 6d924f4..6cb7dbc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
/tusb_helpers.h
*.o
*.log
+/9psrv
diff --git a/9psrv.c b/9psrv.c
index e120eff..5b8cf82 100644
--- a/9psrv.c
+++ b/9psrv.c
@@ -1,13 +1,14 @@
+#include <error.h>
+#include <stdio.h>
#include "coroutine.h"
#include "net9p.h"
int main() {
coroutine_init();
- net9p_listen_stack_t stack = {0};
- coroutine_add(net9p_listen_cr, &stack);
+ if (!coroutine_add(net9p_listen_cr, NULL))
+ error(1, 0, "coroutine_add(net9p_listen_cr, NULL)");
- for (;;) {
- coroutine_task();
- }
+ coroutine_main();
+ return 1;
}
diff --git a/cfg_limits.h b/cfg_limits.h
new file mode 100644
index 0000000..f6ebaf0
--- /dev/null
+++ b/cfg_limits.h
@@ -0,0 +1,13 @@
+/* cfg_limits.h - Compile-time hard-coded limits
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-Licence-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _CFG_LIMITS_H_
+#define _CFG_LIMITS_H_
+
+#define COROUTINE_NUM 5
+#define COROUTINE_STACK_SIZE 0x100000 /* 1MiB */
+
+#endif /* _CFG_LIMITS_H_ */
diff --git a/coroutine.c b/coroutine.c
index 3062ad9..65971d4 100644
--- a/coroutine.c
+++ b/coroutine.c
@@ -4,79 +4,164 @@
* SPDX-Licence-Identifier: AGPL-3.0-or-later
*/
-#include <stdio.h>
-#include <stdlib.h> /* for malloc(pico_malloc) and realloc(pico_malloc) */
+#include <stdint.h> /* for uint8_t */
+#include <string.h> /* for memset() */
+#include <stdio.h> /* for fprintf(), stderr */
+#include <assert.h>
+#include <setjmp.h>
+#include "cfg_limits.h"
#include "coroutine.h"
-static cid_t coroutine_table_len = 0;
-_cr_entry_t *_coroutine_table = NULL;
-cid_t _cur_cid = 0;
+enum coroutine_state {
+ CR_ZERO = 0,
+ CR_GARBAGE,
+ CR_INITIALIZING,
+ CR_RUNNING,
+ CR_RUNNABLE,
+ CR_PAUSED,
+};
-void coroutine_init(void) {
- printf("coroutine_table_len = %u\n", (int)coroutine_table_len);
- if (coroutine_table_len) {
- printf("return\n");
- return;
+struct coroutine {
+ enum coroutine_state state;
+ jmp_buf env;
+ uint8_t stack[COROUTINE_STACK_SIZE];
+};
+
+static struct coroutine coroutine_table[COROUTINE_NUM] = {0};
+static cid_t coroutine_running = 0;
+static jmp_buf scheduler_env;
+
+void coroutine_init(void) {}
+
+void *__sp;
+#define SP ({ asm("movq %%rsp,%0" : "=r"(__sp)); __sp; })
+
+static void print_addr(char *name, void *addr) {
+ if ((void*)&coroutine_table <= addr && addr < ((void*)&coroutine_table) + sizeof(coroutine_table)) {
+ for (size_t i = 0; i < COROUTINE_NUM; i++) {
+ if ((void*)&coroutine_table[i] <= addr && addr < ((void*)&coroutine_table[i]) + sizeof(coroutine_table[i])) {
+ printf("%s=%p (in coroutine_table[%zu] [%p, %p])\n", name, addr, i,
+ (void*)&coroutine_table[i], ((void*)&coroutine_table[i]) + sizeof(coroutine_table[i]));
+ break;
+ }
+ }
+ } else {
+ printf("%s=%p (outside coroutine_table [%p, %p])\n", name, addr,
+ (void*)&coroutine_table, ((void*)&coroutine_table) + sizeof(coroutine_table));
}
- coroutine_table_len = 1;
- printf("coroutine_table_len = %u\n", (int)coroutine_table_len);
- _coroutine_table = malloc(sizeof _coroutine_table[0]);
- _coroutine_table[0] = (_cr_entry_t){
- .fn = (cr_fn_t)0xDEAD,
- .stack = NULL,
- .state = 0,
- .blocked = true,
- };
}
-cid_t coroutine_add(cr_fn_t fn, void *stack) {
- cid_t cid = 0;
- for (cid_t i = 1; cid == 0 && i < coroutine_table_len; i++)
- if (_coroutine_table[i].fn == NULL)
- cid = i;
- if (cid == 0) {
- cid = coroutine_table_len++;
- _coroutine_table = realloc(_coroutine_table, (sizeof _coroutine_table[0]) * coroutine_table_len);
+static void call_with_stack(void *stack, cr_fn_t fn, void *args) {
+ static void *scheduler_sp = NULL;
+
+ /* I don't intend to run nThis only really exists for running on ARM-32, but being
+ * able to run it on x86-64 is useful for debugging. */
+#if __x86_64__
+#define STACK_GROWS_DOWNWARD 1
+ print_addr("sp", SP);
+ asm volatile ("movq %%rsp , %0\n\t" /* scheduler_sp = sp */
+ "movq %1 , %%rsp\n\t" /* sp = stack */
+ "movq %3 , %%rdi\n\t" /* arg0 = args */
+ "call *%2\n\t" /* fn() */
+ "movq %0 , %%rsp" /* sp = scheduler_sp */
+ :
+ : /* %0 */"m"(scheduler_sp),
+ /* %1 */"r"(stack),
+ /* %2 */"r"(fn),
+ /* %3 */"r"(args)
+ :
+ );
+#elif __arm__
+#define STACK_GROWS_DOWNWARD 1
+ asm volatile ("mov r0, coroutine_table[_cur_cid].arg"
+ "mov _saved_stack sp"
+ "mov sp, coroutine_table[_cur_cid].stack"
+ "bl coroutine_table[_cur_cid].fn"
+ "mov _saved_stack sp");
+#else
+#error unsupported architecture
+#endif
+}
+
+cid_t coroutine_add(cr_fn_t fn, void *args) {
+ static cid_t last_created = 0;
+
+ cid_t child;
+ {
+ size_t idx_base = last_created;
+ for (size_t idx_shift = 0; idx_shift < COROUTINE_NUM; idx_shift++) {
+ child = ((idx_base + idx_shift) % COROUTINE_NUM) + 1;
+ if (coroutine_table[child-1].state == CR_ZERO || coroutine_table[child-1].state == CR_GARBAGE)
+ goto found;
+ }
+ return 0;
+ found:
+ }
+
+ if (coroutine_table[child-1].state == CR_GARBAGE)
+ memset(&coroutine_table[child-1], 0, sizeof(struct coroutine));
+ last_created = child;
+
+ cid_t parent = coroutine_running;
+ assert(parent == 0 || coroutine_table[parent-1].state == CR_RUNNING);
+ coroutine_running = child;
+ coroutine_table[child-1].state = CR_INITIALIZING;
+ if (child == 2) {
+ print_addr("&child", &child);
}
- _coroutine_table[cid] = (_cr_entry_t){
- .fn = fn,
- .stack = stack,
- .state = 0,
- .blocked = false,
- };
- printf("added coroutine %u\n", (int)cid);
- return cid;
+ if (!setjmp(scheduler_env)) {
+ call_with_stack(coroutine_table[child-1].stack + (STACK_GROWS_DOWNWARD ? COROUTINE_STACK_SIZE : 0), fn, args);
+ assert(coroutine_table[child-1].state == CR_INITIALIZING);
+ coroutine_table[child-1].state = CR_RUNNABLE;
+ }
+ assert(coroutine_table[child-1].state == CR_RUNNABLE);
+ coroutine_running = parent;
+
+ return child;
}
-void coroutine_task(void) {
- cid_t start = (_cur_cid + 1) % coroutine_table_len;
- cid_t shift;
- for (shift = 0;
- shift < coroutine_table_len &&
- (_coroutine_table[(start+shift)%coroutine_table_len].fn == NULL ||
- _coroutine_table[(start+shift)%coroutine_table_len].blocked);
- shift++) {}
- if (shift == coroutine_table_len) {
- printf("idle\n");
- return;
+void coroutine_main(void) {
+ bool ran;
+ for (coroutine_running = 1;; coroutine_running = (coroutine_running%COROUTINE_NUM)+1) {
+ if (coroutine_running == 1)
+ ran = false;
+ if (coroutine_table[coroutine_running-1].state == CR_RUNNABLE) {
+ ran = true;
+ coroutine_table[coroutine_running-1].state = CR_RUNNING;
+ if (!setjmp(scheduler_env)) {
+ longjmp(coroutine_table[coroutine_running-1].env, 1);
+ coroutine_table[coroutine_running-1].state = CR_GARBAGE;
+ }
+ }
+ if (coroutine_running == COROUTINE_NUM+1 && !ran) {
+ fprintf(stderr, "error: no runnable coroutines\n");
+ return;
+ }
}
- _cur_cid = (start + shift) % coroutine_table_len;
- printf("running %u\n", (int)_cur_cid);
- if (_cur_cid == 0)
- exit(1);
- _coroutine_table[_cur_cid].fn(_coroutine_table[_cur_cid].stack);
}
-// arm32
-mov r0, coroutine_table[_cur_cid].arg
-mov _saved_stack sp
-mov sp, coroutine_table[_cur_cid].stack
-bl coroutine_table[_cur_cid].fn
-mov _saved_stack sp
-// x86-64
-movl coroutine_table[_cur_cid].arg %edi
-movq %rsp _saved_stack
-movq coroutine_table[_cur_cid].stack %rsp
-call coroutine_table[_cur_cid].fn
-movq _saved_stack %rsp
+bool _cr_begin(void) {
+ assert(coroutine_table[coroutine_running-1].state == CR_INITIALIZING);
+ return setjmp(coroutine_table[coroutine_running-1].env);
+}
+
+static inline void _cr_transition(enum coroutine_state state) {
+ assert(coroutine_table[coroutine_running-1].state == CR_RUNNING);
+ coroutine_table[coroutine_running-1].state = state;
+ if (state == CR_GARBAGE || !setjmp(coroutine_table[coroutine_running-1].env))
+ longjmp(scheduler_env, 1);
+}
+
+void cr_exit(void) { _cr_transition(CR_GARBAGE); }
+void cr_yield(void) { _cr_transition(CR_RUNNABLE); }
+void cr_pause_and_yield(void) { _cr_transition(CR_PAUSED); }
+
+void cr_unpause(cid_t cid) {
+ assert(coroutine_table[cid-1].state == CR_PAUSED);
+ coroutine_table[cid-1].state = CR_RUNNABLE;
+}
+
+cid_t cr_getcid(void) {
+ return coroutine_running;
+}
diff --git a/coroutine.h b/coroutine.h
index d22eb2e..ae52f22 100644
--- a/coroutine.h
+++ b/coroutine.h
@@ -8,65 +8,60 @@
#define _COROUTINE_H_
#include <stddef.h> /* for size_t */
-#include <stdbool.h> /* for bool, true, false */
+#include <stdbool.h> /* for bool */
/* typedefs *******************************************************************/
+
typedef size_t cid_t; /* 0=none; otherwise 1-indexed */
-typedef void (*cr_fn_t)(void *stack);
-typedef struct {
- cr_fn_t fn;
- void *stack;
- unsigned int state;
- bool blocked;
-} _cr_entry_t;
+typedef void (*cr_fn_t)(void *args);
-/* globals ********************************************************************/
-extern _cr_entry_t *_coroutine_table;
-extern cid_t _cur_cid;
+/* managing coroutines ********************************************************/
-/* bona-fide functions ********************************************************/
void coroutine_init(void);
-cid_t coroutine_add(cr_fn_t fn, void *stack);
-void coroutine_task(void);
+cid_t coroutine_add(cr_fn_t fn, void *args);
+void coroutine_main(void);
+
+/* inside of coroutines *******************************************************/
-/* core macros for use in a cr_fn_t() *****************************************/
-#define cr_begin() switch(_coroutine_table[_cur_cid].state) { case 0:
-#define cr_exit() do { _coroutine_table[_cur_cid] = (cr_entry_t){0}; return; } while (0)
-#define cr_yield() _cr_yield(__COUNTER__+1)
-#define _cr_yield(_state) do { _coroutine_table[_cur_cid].state = _state; return; case _state:; } while (0)
-#define cr_end() }
+bool _cr_begin(void);
+#define cr_begin() do { if (!_cr_begin()) return; } while (0)
+void cr_exit(void);
+void cr_yield(void);
+void cr_pause_and_yield(void);
+void cr_unpause(cid_t);
+#define cr_end()
+
+cid_t cr_getcid(void);
/* request/response channels **************************************************/
+
#define cr_chan_t(req_t, resp_t) struct { \
cid_t requester; \
cid_t responder; \
req_t req; \
resp_t resp; \
}
-#define cr_chan_req(ch, _resp_p, _req) do { \
- (ch)->requester = _cur_cid; \
- (ch)->req = (_req); \
- if ((ch)->responder != 0) \
- _coroutine_table[(ch)->responder].blocked = false; \
- _coroutine_table[_cur_cid].blocked = true; \
- cr_yield(); \
- if ((typeof(&(ch)->resp))(_resp_p)) \
- *((typeof(&(ch)->resp))(_resp_p)) = (ch)->resp; \
+#define cr_chan_req(ch, _resp_p, _req) do { \
+ (ch)->requester = cr_getcid(); \
+ (ch)->req = (_req); \
+ if ((ch)->responder != 0) \
+ cr_unpause((ch)->responder); \
+ cr_pause_and_yield(); \
+ if ((typeof(&(ch)->resp))(_resp_p)) \
+ *((typeof(&(ch)->resp))(_resp_p)) = (ch)->resp; \
} while (0)
#define cr_chan_have_req(ch) ((ch)->requester != 0)
-#define cr_chan_recv_req(ch, _req_p) do { \
- (ch)->responder = _cur_cid; \
- if ((ch)->requester == 0) { \
- _coroutine_table[_cur_cid].blocked = true; \
- cr_yield(); \
- } \
- *(_req_p) = (ch)->req; \
+#define cr_chan_recv_req(ch, _req_p) do { \
+ (ch)->responder = cr_getcid(); \
+ if ((ch)->requester == 0) { \
+ cr_pause_and_yield(); \
+ *(_req_p) = (ch)->req; \
} while (0)
-#define cr_chan_send_resp(ch, _resp) do { \
- (ch)->responder = 0; \
- (ch)->requester = 0; \
- (ch)->resp = (_resp); \
- _coroutine_table[(ch)->requester].blocked = false; \
+#define cr_chan_send_resp(ch, _resp) do { \
+ cr_unpause((ch)->requester); \
+ (ch)->responder = 0; \
+ (ch)->requester = 0; \
+ (ch)->resp = (_resp); \
} while (0)
#endif /* _COROUTINE_H_ */
diff --git a/net9p.c b/net9p.c
index 99d3d72..894b4fe 100644
--- a/net9p.c
+++ b/net9p.c
@@ -1,3 +1,7 @@
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <error.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -7,36 +11,45 @@
#include "net9p.h"
#include "coroutine.h"
-void net9p_listen_cr(void *_stack) {
- static union {
+void net9p_listen_cr(void *_arg) {
+ (void)_arg;
+ printf("listen initializng...\n");
+ cr_begin();
+ printf("listen running...\n");
+
+ union {
struct sockaddr_in in;
struct sockaddr gen;
} addr = {0};
- if (!addr.in.sin_family) {
- addr.in.sin_family = AF_INET;
- addr.in.sin_port = 0x2823;
- }
- net9p_listen_stack_t *stack = _stack;
-
- cr_begin();
+ addr.in.sin_family = AF_INET;
+ addr.in.sin_port = htons(9001);
- stack->fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);
- bind(stack->fd, &addr.gen, sizeof addr);
- listen(stack->fd, 5);
+ int fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ error(1, errno, "socket");
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0)
+ error(1, errno, "setsockopt");
+ if (bind(fd, &addr.gen, sizeof addr) < 0)
+ error(1, errno, "bind");
+ if (listen(fd, 5) < 0)
+ error(1, errno, "listen");
+ int conn = 9;
+ if (!coroutine_add(net9p_worker_cr, &conn))
+ error(1, 0, "coroutine_add(net9p_worker_cr, &%d)", conn);
for (;;) {
- int conn = accept(stack->fd, NULL, NULL);
- if (conn >= 0) {
- net9p_worker_stack_t *connstack = malloc(sizeof(net9p_worker_stack_t));
- connstack->fd = conn;
- coroutine_add(net9p_worker_cr, connstack);
- }
cr_yield();
}
cr_end();
}
-void net9p_worker_cr(void *_stack) {
- net9p_worker_stack_t *stack = _stack;
+void net9p_worker_cr(void *_arg) {
+ int fd = *((int *)_arg);
+ cr_begin();
+
+ printf("worker %zu\n", cr_getcid());
+ close(fd);
+
+ cr_end();
}
diff --git a/net9p.h b/net9p.h
index c7b0d65..a017774 100644
--- a/net9p.h
+++ b/net9p.h
@@ -1,14 +1,6 @@
#ifndef _NET9P_H_
#define _NET9P_H_
-typedef struct {
- int fd;
-} net9p_listen_stack_t;
-
-typedef struct {
- int fd;
-} net9p_worker_stack_t;
-
void net9p_listen_cr(void *);
void net9p_worker_cr(void *);