summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/sbc_harness/config/config.h2
-rw-r--r--libcr/coroutine.c51
-rw-r--r--libcr_ipc/CMakeLists.txt4
-rw-r--r--libcr_ipc/include/libcr_ipc/_common.h41
-rw-r--r--libcr_ipc/include/libcr_ipc/chan.h1
-rw-r--r--libcr_ipc/include/libcr_ipc/mutex.h32
-rw-r--r--libcr_ipc/include/libcr_ipc/rpc.h6
-rw-r--r--libcr_ipc/include/libcr_ipc/sema.h89
-rw-r--r--libcr_ipc/mutex.c39
-rw-r--r--libcr_ipc/sema.c81
-rw-r--r--libnetio/netio_posix.c17
11 files changed, 194 insertions, 169 deletions
diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h
index 20499bb..b12eb2c 100644
--- a/cmd/sbc_harness/config/config.h
+++ b/cmd/sbc_harness/config/config.h
@@ -20,7 +20,7 @@
# define CONFIG_COROUTINE_DEFAULT_STACK_SIZE (16*1024)
# define CONFIG_COROUTINE_MEASURE_STACK 1 /* bool */
# define CONFIG_COROUTINE_PROTECT_STACK 1 /* bool */
-# define CONFIG_COROUTINE_DEBUG 1 /* bool */
+# define CONFIG_COROUTINE_DEBUG 0 /* bool */
# define CONFIG_COROUTINE_NUM (1 /* usb_common */ +\
1 /* usb_keyboard */ +\
CONFIG_NETIO_NUM_CONNS /* accept+read */ +\
diff --git a/libcr/coroutine.c b/libcr/coroutine.c
index d221ca7..79ba894 100644
--- a/libcr/coroutine.c
+++ b/libcr/coroutine.c
@@ -126,7 +126,7 @@ enum coroutine_state {
CR_INITIALIZING, /* running, before cr_begin() */
CR_RUNNING, /* running, after cr_begin() */
CR_RUNNABLE, /* not running, but runnable */
- CR_PAUSED, /* not running, and not runnable */
+ CR_PAUSED, /* not running, and not runnable */
};
struct coroutine {
@@ -139,6 +139,14 @@ struct coroutine {
/* constants ******************************************************************/
+const char *coroutine_state_strs[] = {
+ [CR_NONE] = "CR_NONE",
+ [CR_INITIALIZING] = "CR_INITIALIZING",
+ [CR_RUNNING] = "CR_RUNNING",
+ [CR_RUNNABLE] = "CR_RUNNABLE",
+ [CR_PAUSED] = "CR_PAUSED",
+};
+
#define STACK_ALIGNMENT ({ __attribute__((aligned)) void fn(void) {}; __alignof__(fn); })
#if CONFIG_COROUTINE_MEASURE_STACK || CONFIG_COROUTINE_PROTECT_STACK
@@ -174,12 +182,36 @@ static cid_t coroutine_running = 0;
/* utility functions **********************************************************/
+#define errorf(...) fprintf(stderr, "error: " __VA_ARGS__)
+#define infof(...) printf("info: " __VA_ARGS__)
#if CONFIG_COROUTINE_DEBUG
# define debugf(...) printf("dbg: " __VA_ARGS__)
#else
# define debugf(...)
#endif
+#ifdef __GLIBC__
+# define assertf(expr, ...) \
+ ((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \
+ if (expr) \
+ ; /* empty */ \
+ else { \
+ errorf("assertion: " __VA_ARGS__); \
+ __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
+ } \
+ }))
+#else
+# define assertf(expr, ...) assert(expr)
+#endif
+
+/** Return `n` rounded up to the nearest multiple of `d` */
+#define round_up(n, d) ( ( ((n)+(d)-1) / (d) ) * (d) )
+
+static inline const char* coroutine_state_str(enum coroutine_state state) {
+ assert(state < (sizeof(coroutine_state_strs)/sizeof(coroutine_state_strs[0])));
+ return coroutine_state_strs[state];
+}
+
static inline void assert_cid(cid_t cid) {
assert(cid > 0);
assert(cid <= CONFIG_COROUTINE_NUM);
@@ -199,10 +231,6 @@ static inline void assert_cid(cid_t cid) {
assert(coroutine_table[(cid)-1].state opstate); \
} while (0)
-
-/* Return `n` rounded up to the nearest multiple of `d` */
-#define round_up(n, d) ( ( ((n)+(d)-1) / (d) ) * (d) )
-
/* call_with_stack() **********************************************************/
static void call_with_stack(void *stack, cr_fn_t fn, void *args) {
@@ -351,7 +379,7 @@ void coroutine_main(void) {
for (;;) {
cid_t next = next_coroutine();
if (!next) {
- fprintf(stderr, "error: no coroutines\n");
+ errorf("no coroutines\n");
return;
}
if (!setjmp(coroutine_main_env)) { /* point=b */
@@ -363,7 +391,7 @@ void coroutine_main(void) {
#if CONFIG_COROUTINE_MEASURE_STACK
struct stack_stats sizes;
measure_stack(coroutine_running, &sizes);
- printf("info: cid=%zu: exited having used %zu B stack space\n", coroutine_running, sizes.max);
+ infof("cid=%zu: exited having used %zu B stack space\n", coroutine_running, sizes.max);
#endif
free(coroutine_table[coroutine_running-1].stack);
coroutine_table[coroutine_running-1] = (struct coroutine){0};
@@ -381,7 +409,10 @@ void cr_begin(void) {
}
static inline void _cr_transition(enum coroutine_state state) {
- debugf("cid=%zu: transition %i->%i\n", coroutine_running, coroutine_table[coroutine_running-1].state, state);
+ debugf("cid=%zu: transition %s->%s\n",
+ coroutine_running,
+ coroutine_state_str(coroutine_table[coroutine_running-1].state),
+ coroutine_state_str(state));
coroutine_table[coroutine_running-1].state = state;
@@ -438,6 +469,7 @@ void cr_unpause(cid_t cid) {
void cr_unpause_from_sighandler(cid_t cid) {
assert_cid(cid);
+ debugf("cr_unpause_from_sighandler(%zu)\n", cid);
switch (coroutine_table[cid-1].state) {
case CR_RUNNING:
@@ -447,7 +479,8 @@ void cr_unpause_from_sighandler(cid_t cid) {
coroutine_table[cid-1].state = CR_RUNNABLE;
break;
default:
- assert(false);
+ assertf(false, "cid=%zu state=%s\n",
+ cid, coroutine_state_str(coroutine_table[cid-1].state));
}
}
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index 957b638..509e5a6 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -5,10 +5,6 @@
add_library(libcr_ipc INTERFACE)
target_include_directories(libcr_ipc SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
-target_sources(libcr_ipc INTERFACE
- sema.c
- mutex.c
-)
target_link_libraries(libcr_ipc INTERFACE
libcr
)
diff --git a/libcr_ipc/include/libcr_ipc/_common.h b/libcr_ipc/include/libcr_ipc/_common.h
index 5efe964..547ab81 100644
--- a/libcr_ipc/include/libcr_ipc/_common.h
+++ b/libcr_ipc/include/libcr_ipc/_common.h
@@ -7,13 +7,46 @@
#ifndef _COROUTINE__COMMON_H_
#define _COROUTINE__COMMON_H_
-struct _cr_ipc_cid_list {
- cid_t val;
- struct _cr_ipc_cid_list *next;
-};
+#include <assert.h>
+
+#include <libcr/coroutine.h>
#define _cr_ipc_static_assert_sametype(a, b, msg) \
static_assert(_Generic(a, typeof(b): 1, default: 0), msg)
#define _cr_ipc_str(a) #a
+struct _cr_ipc_cid_fifo_node {
+ cid_t val;
+ struct _cr_ipc_cid_fifo_node *next;
+};
+
+struct _cr_ipc_cid_fifo {
+ struct _cr_ipc_cid_fifo_node *head, **tail;
+};
+
+
+#define _cr_ipc_cid_fifo_self(name) \
+ struct _cr_ipc_cid_fifo_node name = { \
+ .val = cr_getcid(), \
+ .next = NULL, \
+ }
+
+static inline void _cr_ipc_cid_fifo_push(struct _cr_ipc_cid_fifo *fifo, struct _cr_ipc_cid_fifo_node *self) {
+ if (!fifo->tail)
+ fifo->tail = &(fifo->head);
+ *(fifo->tail) = self;
+}
+
+static inline cid_t _cr_ipc_cid_fifo_pop(struct _cr_ipc_cid_fifo *fifo) {
+ if (!fifo->tail)
+ fifo->tail = &(fifo->head);
+ cid_t cid = 0;
+ if (fifo->head) {
+ cid = fifo->head->val;
+ if (!(( fifo->head = fifo->head->next )) )
+ fifo->tail = &(fifo->head);
+ }
+ return cid;
+}
+
#endif /* _COROUTINE__COMMON_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h
index ca53fc2..4bb8f66 100644
--- a/libcr_ipc/include/libcr_ipc/chan.h
+++ b/libcr_ipc/include/libcr_ipc/chan.h
@@ -13,7 +13,6 @@
#define _COROUTINE_CHAN_H_
#include <assert.h>
-
#include <libcr/coroutine.h>
/**
diff --git a/libcr_ipc/include/libcr_ipc/mutex.h b/libcr_ipc/include/libcr_ipc/mutex.h
index 1a82423..79424e3 100644
--- a/libcr_ipc/include/libcr_ipc/mutex.h
+++ b/libcr_ipc/include/libcr_ipc/mutex.h
@@ -1,4 +1,4 @@
-/* libcr_ipc/mutex.h - Simple mutexes for libcr (header file)
+/* libcr_ipc/mutex.h - Simple mutexes for libcr
*
* Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-Licence-Identifier: AGPL-3.0-or-later
@@ -7,18 +7,15 @@
#ifndef _COROUTINE_MUTEX_H_
#define _COROUTINE_MUTEX_H_
-#include <stdbool.h> /* for bool */
-
-#include <libcr/coroutine.h> /* for cid_t */
-
-#include <libcr_ipc/_common.h> /* for struct _cr_ipc_list_t */
+#include <stdbool.h>
+#include <libcr_ipc/_common.h>
/**
* A cr_mutex_t is a fair mutex.
*/
typedef struct {
bool locked;
- struct _cr_ipc_cid_list *head, **tail;
+ struct _cr_ipc_cid_fifo waiters;
} cr_mutex_t;
/**
@@ -28,7 +25,16 @@ typedef struct {
* @yields maybe
* @run_in coroutine
*/
-void cr_mutex_lock(cr_mutex_t *);
+static inline void cr_mutex_lock(cr_mutex_t *mu) {
+ assert(mu);
+ if (!mu->locked) {
+ mu->locked = true;
+ return;
+ }
+ _cr_ipc_cid_fifo_self(self);
+ _cr_ipc_cid_fifo_push(&mu->waiters, &self);
+ cr_pause_and_yield();
+}
/**
* Unlock the mutex. Unblocks a coroutine that is blocked on
@@ -38,6 +44,14 @@ void cr_mutex_lock(cr_mutex_t *);
* @yields never
* @may_run_in coroutine
*/
-void cr_mutex_unlock(cr_mutex_t *);
+static inline void cr_mutex_unlock(cr_mutex_t *mu) {
+ assert(mu);
+ assert(mu->locked);
+ cid_t next = _cr_ipc_cid_fifo_pop(&mu->waiters);
+ if (next)
+ cr_unpause(next);
+ else
+ mu->locked = false;
+}
#endif /* _COROUTINE_MUTEX_H_ */
diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h
index 2525ba8..abdb3b6 100644
--- a/libcr_ipc/include/libcr_ipc/rpc.h
+++ b/libcr_ipc/include/libcr_ipc/rpc.h
@@ -11,11 +11,7 @@
#ifndef _COROUTINE_RPC_H_
#define _COROUTINE_RPC_H_
-#include <assert.h>
-
-#include <libcr/coroutine.h>
-
-#include <libcr_ipc/_common.h> /* for _cr_ipc_* */
+#include <libcr_ipc/_common.h>
/**
* cr_rpc_t(req_t, resp_t) returns the type definition for a
diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h
index 236e7e5..8d71c69 100644
--- a/libcr_ipc/include/libcr_ipc/sema.h
+++ b/libcr_ipc/include/libcr_ipc/sema.h
@@ -1,4 +1,4 @@
-/* libcr_ipc/sema.h - Simple semaphores for libcr (header file)
+/* libcr_ipc/sema.h - Simple semaphores for libcr
*
* Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-Licence-Identifier: AGPL-3.0-or-later
@@ -7,35 +7,81 @@
#ifndef _COROUTINE_SEMA_H_
#define _COROUTINE_SEMA_H_
-#include <stdbool.h> /* for bool */
-
-#include <libcr/coroutine.h> /* for cid_t */
-
-#include <libcr_ipc/_common.h> /* for struct _cr_ipc_list_t */
+#include <stdbool.h>
+#include <libcr_ipc/_common.h>
/**
* A cr_sema_t is a fair unbounded[1] counting semaphore.
*
* [1]: Well, INT_MAX
*/
-typedef volatile struct {
- int cnt;
+typedef struct {
+ volatile int cnt;
- struct _cr_ipc_cid_list *head, **tail;
+ struct _cr_ipc_cid_fifo waiters;
/* locked indicates that a call from within a coroutine is is
- * messing with ->{head,tail}, so a signal handler can't read
- * it. */
- bool locked;
+ * messing with ->waiters, so a signal handler can't read it.
+ * Use `asm volatile ("":::"memory")` after setting =true and
+ * before setting =false to make sure the compiler doesn't
+ * re-order writes! */
+ volatile bool locked;
} cr_sema_t;
/**
+ * Drain a maximum of sema->cnt coroutines from the sema->waiters
+ * list. Returns true if cr_getcid() was drained.
+ */
+static inline bool _cr_sema_drain(cr_sema_t *sema) {
+ assert(!sema->locked);
+ cid_t self = cr_getcid();
+
+ enum drain_result {
+ DRAINING,
+ DRAINED_SELF, /* stopped because drained `self` */
+ DRAINED_ALL, /* stopped because sema->waiters is empty */
+ DRAINED_SOME, /* stopped because sema->cnt == 0 */
+ } state = DRAINING;
+ do {
+ sema->locked = true;
+ asm volatile ("":::"memory");
+ while (state == DRAINING) {
+ if (!sema->waiters.head) {
+ state = DRAINED_ALL;
+ } else if (!sema->cnt) {
+ state = DRAINED_SOME;
+ } else {
+ sema->cnt--;
+ cid_t cid = _cr_ipc_cid_fifo_pop(&sema->waiters);
+ if (cid == self)
+ state = DRAINED_SELF;
+ else
+ cr_unpause_from_sighandler(cid);
+ }
+ }
+ asm volatile ("":::"memory");
+ sema->locked = false;
+ /* If there are still coroutines in sema->waiters,
+ * check that sema->cnt wasn't incremented between `if
+ * (!sema->cnt)` and `sema->locked = false`. */
+ } while (state == DRAINED_SOME && sema->cnt);
+ /* If state == DRAINED_SELF, then we better have been the last
+ * item in the list! */
+ assert(state != DRAINED_SELF || !sema->waiters.head);
+ return state == DRAINED_SELF;
+}
+
+/**
* Increment the semaphore,
*
* @blocks never
* @yields never
* @run_in anywhere (coroutine, sighandler)
*/
-void cr_sema_signal(cr_sema_t *sema);
+static inline void cr_sema_signal(cr_sema_t *sema) {
+ sema->cnt++;
+ if (!sema->locked)
+ _cr_sema_drain(sema);
+}
/**
* Wait until the semaphore is >0, then decrement it.
@@ -44,6 +90,21 @@ void cr_sema_signal(cr_sema_t *sema);
* @yields maybe
* @may_run_in coroutine
*/
-void cr_sema_wait(cr_sema_t *sema);
+static inline void cr_sema_wait(cr_sema_t *sema) {
+ _cr_ipc_cid_fifo_self(self);
+
+ sema->locked = true;
+ asm volatile ("":::"memory");
+ _cr_ipc_cid_fifo_push(&sema->waiters, &self);
+ asm volatile ("":::"memory");
+ sema->locked = false;
+
+ if (_cr_sema_drain(sema))
+ /* DRAINED_SELF: (1) No need to pause+yield, (2) we
+ * better have been the last item in the list! */
+ assert(!self.next);
+ else
+ cr_pause_and_yield();
+}
#endif /* _COROUTINE_SEMA_H_ */
diff --git a/libcr_ipc/mutex.c b/libcr_ipc/mutex.c
deleted file mode 100644
index 198c738..0000000
--- a/libcr_ipc/mutex.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/* libcr_ipc/mutex.c - Simple mutexes for libcr (implementation file)
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-Licence-Identifier: AGPL-3.0-or-later
- */
-
-#include <assert.h>
-
-#include <libcr_ipc/mutex.h>
-
-void cr_mutex_lock(cr_mutex_t *mu) {
- assert(mu);
- if (!mu->tail)
- mu->tail = &mu->head;
- if (!mu->locked) {
- mu->locked = true;
- return;
- }
- struct _cr_ipc_cid_list self = {
- .val = cr_getcid(),
- .next = NULL,
- };
- *(mu->tail) = &self;
- mu->tail = &(self.next);
- cr_pause_and_yield();
-}
-
-void cr_mutex_unlock(cr_mutex_t *mu) {
- assert(mu);
- assert(mu->tail);
- assert(mu->locked);
- if (mu->head) {
- cr_unpause(mu->head->val);
- mu->head = mu->head->next;
- if (!mu->head)
- mu->tail = &mu->head;
- } else
- mu->locked = false;
-}
diff --git a/libcr_ipc/sema.c b/libcr_ipc/sema.c
deleted file mode 100644
index a3efb57..0000000
--- a/libcr_ipc/sema.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/* libcr_ipc/sema.c - Simple semaphores for libcr (implementation file)
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-Licence-Identifier: AGPL-3.0-or-later
- */
-
-#include <assert.h>
-
-#include <libcr_ipc/sema.h>
-
-/** Drain the sema->{head,tail} list. Returns true if cr_getcid() was drained. */
-static inline bool drain(cr_sema_t *sema) {
- assert(!sema->locked);
- cid_t self = cr_getcid();
-
- enum drain_result {
- DRAINING,
- DRAINED_SELF, /* stopped because drained `self` */
- DRAINED_ALL, /* stopped because sema->head == NULL */
- DRAINED_SOME, /* stopped because sema->cnt == 0 */
- } state = DRAINING;
- do {
- sema->locked = true;
- while (state == DRAINING) {
- if (!sema->head) {
- state = DRAINED_ALL;
- } else if (!sema->cnt) {
- state = DRAINED_SOME;
- } else {
- sema->cnt--;
- cid_t cid = sema->head->val;
- if (cid == self)
- state = DRAINED_SELF;
- else
- cr_unpause(sema->head->val);
- sema->head = sema->head->next;
- if (!sema->head)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
- sema->tail = &sema->head;
-#pragma GCC diagnostic pop
- }
- }
- sema->locked = false;
- /* If there are still coroutines in sema->head, check
- * that sema->cnt wasn't incremented between `if
- * (!sema->cnt)` and `sema->locked = false`. */
- } while (state == DRAINED_SOME && sema->cnt);
- /* If state == DRAINED_SELF, then we better have been the last
- * item in the list! */
- assert(state != DRAINED_SELF || !sema->head);
- return state == DRAINED_SELF;
-}
-
-void cr_sema_signal(cr_sema_t *sema) {
- sema->cnt++;
- if (!sema->locked)
- drain(sema);
-}
-
-void cr_sema_wait(cr_sema_t *sema) {
- struct _cr_ipc_cid_list self = {
- .val = cr_getcid(),
- .next = NULL,
- };
-
- sema->locked = true;
- if (!sema->tail)
- sema->head = &self;
- else
- *(sema->tail) = &self;
- sema->tail = &(self.next);
- sema->locked = false;
-
- if (drain(sema))
- /* DRAINED_SELF: (1) No need to pause+yield, (2) we
- * better have been the last item in the list! */
- assert(!self.next);
- else
- cr_pause_and_yield();
-}
diff --git a/libnetio/netio_posix.c b/libnetio/netio_posix.c
index 4522683..34eac80 100644
--- a/libnetio/netio_posix.c
+++ b/libnetio/netio_posix.c
@@ -8,6 +8,9 @@
#include <stdlib.h> /* for shutdown(), SHUT_RD, SHUT_WR, SHUT_RDWR */
#include <string.h> /* for memset() */
#include <sys/socket.h> /* for struct sockaddr, socket(), SOCK_* flags, setsockopt(), SOL_SOCKET, SO_REUSEADDR, bind(), listen(), accept() */
+#include <unistd.h> /* for getpid() */
+
+#include <stdio.h>
#define USE_CONFIG_NETIO_POSIX
#include "config.h"
@@ -113,6 +116,8 @@ int netio_listen(uint16_t port) {
error(1, errno, "fcntl(F_SETFL)");
if (fcntl(sock->fd, F_SETSIG, sig_accept) < 0)
error(1, errno, "fcntl(F_SETSIG)");
+ if (fcntl(sock->fd, F_SETOWN, getpid()) < 0)
+ error(1, errno, "fcntl(F_SETOWN)");
#endif
if (bind(sock->fd, &addr.gen, sizeof addr) < 0)
error(1, errno, "bind");
@@ -133,9 +138,11 @@ int netio_accept(int sock) {
* while waiting for us to accept(). */
for (;;) {
#if CONFIG_NETIO_ISLINUX
+ printf("accept...\n");
cr_sema_wait(&socket_table[sock].accept_waiters);
#endif
int conn = accept(socket_table[sock].fd, NULL, NULL);
+ printf("... accept => %d\n", conn);
if (conn < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
#if !CONFIG_NETIO_ISLINUX
@@ -167,8 +174,11 @@ ssize_t netio_read(int conn, void *buf, size_t count) {
if (aio_read(&ctl_block) < 0)
return -errno;
- while ((r = aio_error(&ctl_block)) == EINPROGRESS)
+ while ((r = aio_error(&ctl_block)) == EINPROGRESS) {
+ printf("read %zu...\n", count);
cr_pause_and_yield();
+ }
+ printf("...read => n=%zd errno=%d\n", aio_return(&ctl_block), r);
return r ? -abs(r) : aio_return(&ctl_block);
}
@@ -192,8 +202,11 @@ ssize_t netio_write(int conn, void *buf, size_t goal) {
if (aio_write(&ctl_block) < 0)
return -errno;
- while ((r = aio_error(&ctl_block)) == EINPROGRESS)
+ while ((r = aio_error(&ctl_block)) == EINPROGRESS) {
+ printf("write %zu...\n", goal-done);
cr_pause_and_yield();
+ }
+ printf("...write => n=%zd errno=%d", aio_return(&ctl_block), r);
if (r < 0)
return -abs(r);
done += aio_return(&ctl_block);