]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Another implementation of coroutines using pthreads
authorMartin Mares <mj@ucw.cz>
Tue, 18 Jul 2017 22:21:42 +0000 (00:21 +0200)
committerMartin Mares <mj@ucw.cz>
Tue, 18 Jul 2017 22:21:42 +0000 (00:21 +0200)
sysdep/unix/coroutine.c

index 3d648eb44b8dfc490de14ca2c2be82725fc4eb2d..5e986a7ca4dcb02e792a3e43324a82d0f07921fa 100644 (file)
@@ -11,7 +11,6 @@
 #endif
 
 #include <stdlib.h>
-#include <ucontext.h>
 
 #include "nest/bird.h"
 #include "lib/coroutine.h"
 #include "lib/socket.h"
 #include "sysdep/unix/unix.h"
 
+#define CORO_STACK_SIZE 65536
+
+#if 1
+
+/*
+ *     Implementation of coroutines based on <ucontext.h>
+ */
+
+#include <ucontext.h>
+
 struct coroutine {
   resource r;
   ucontext_t ctx;
@@ -30,8 +39,6 @@ struct coroutine {
 static ucontext_t *main_context;
 static coroutine *coro_current;                // NULL for main context
 
-#define CORO_STACK_SIZE 65536
-
 static void
 coro_free(resource *r)
 {
@@ -112,6 +119,118 @@ coro_resume(coroutine *c)
   ASSERT(!coro_current);
 }
 
+#else
+
+/*
+ *     Implementation of coroutines based on POSIX threads
+ */
+
+#include <pthread.h>
+#include <semaphore.h>
+
+struct coroutine {
+  resource r;
+  pthread_t thread;
+  void (*entry_point)(void *arg);
+  void *arg;
+  sem_t sem;
+};
+
+static coroutine *coro_current;                // NULL for main context
+static int coro_inited;
+static sem_t coro_main_sem;
+static pthread_attr_t coro_thread_attrs;
+
+static void
+coro_free(resource *r)
+{
+  coroutine *c = (coroutine *) r;
+  pthread_cancel(c->thread);
+  pthread_join(c->thread, NULL);
+}
+
+static void
+coro_dump(resource *r UNUSED)
+{
+  debug("\n");
+}
+
+static size_t
+coro_memsize(resource *r)
+{
+  coroutine *c = (coroutine *) r;
+  return sizeof(*c) + CORO_STACK_SIZE + 2*ALLOC_OVERHEAD;
+}
+
+static struct resclass coro_class = {
+  .name = "Coroutine",
+  .size = sizeof(struct coroutine),
+  .free = coro_free,
+  .dump = coro_dump,
+  .memsize = coro_memsize,
+};
+
+static void *
+coro_do_start(void *c_)
+{
+  coroutine *c = c_;
+  while (sem_wait(&c->sem) < 0)
+    ;
+  coro_current = c;
+  c->entry_point(c->arg);
+  bug("Coroutine returned unexpectedly");
+}
+
+struct coroutine *
+coro_new(pool *p, void (*entry_point)(void *), void *arg)
+{
+  if (!coro_inited)
+    {
+      if (sem_init(&coro_main_sem, 0, 0) < 0)
+       bug("sem_init() failed");
+      if (pthread_attr_init(&coro_thread_attrs))
+       bug("pthread_attr_init() failed");
+      if (pthread_attr_setstacksize(&coro_thread_attrs, CORO_STACK_SIZE))
+       bug("pthread_attr_setstacksize() failed");
+      coro_inited = 1;
+    }
+
+  coroutine *c = ralloc(p, &coro_class);
+  c->entry_point = entry_point;
+  c->arg = arg;
+  if (sem_init(&c->sem, 0, 0) < 0)
+    bug("sem_init() failed");
+  if (pthread_create(&c->thread, &coro_thread_attrs, coro_do_start, c))
+    bug("pthread_create() failed");
+
+  return c;
+}
+
+void
+coro_suspend(void)
+{
+  ASSERT(coro_inited);
+  ASSERT(coro_current);
+  coroutine *c = coro_current;
+  sem_post(&coro_main_sem);
+  while (sem_wait(&c->sem) < 0)
+    ;
+  coro_current = c;
+}
+
+void
+coro_resume(coroutine *c)
+{
+  ASSERT(coro_inited);
+  ASSERT(!coro_current);
+  sem_post(&c->sem);
+  while (sem_wait(&coro_main_sem) < 0)
+    ;
+  coro_current = NULL;
+}
+
+#endif
+
 /* Coroutine-based I/O */
 
 static int