fixed a number of bugs, added support for cothread-private data and functions of...
authorAndy Wingo <wingo@pobox.com>
Tue, 29 Jan 2002 05:49:57 +0000 (05:49 +0000)
committerAndy Wingo <wingo@pobox.com>
Tue, 29 Jan 2002 05:49:57 +0000 (05:49 +0000)
Original commit message from CVS:
fixed a number of bugs, added support for cothread-private data and
functions of the form void (*func) (int argc, void **argv)

the test app demonstrates the thread-safety of the cothreads lib

this is shaping up to be pretty solid.

gst/cothreads/cothread-stack.c
gst/cothreads/cothreads-private.h
gst/cothreads/cothreads.c
gst/cothreads/cothreads.h
gst/cothreads/test-cothreads.c

index 9e963ab..10a822a 100644 (file)
@@ -52,7 +52,12 @@ static cothread_chunk*       cothread_chunk_new_linuxthreads (cothread_chunk* old);
 gboolean
 cothread_stack_alloc_on_heap (char **low, char **high)
 {
-  *low = g_malloc (_cothread_attr_global->chunk_size / _cothread_attr_global->blocks_per_chunk);
+  if (posix_memalign (low, _cothread_attr_global->chunk_size / _cothread_attr_global->blocks_per_chunk,
+                      _cothread_attr_global->chunk_size / _cothread_attr_global->blocks_per_chunk) != NULL) {
+    g_error ("could not memalign stack");
+    return FALSE;
+  }
+  
   *high = *low + sizeof (*low) - 1;
   return TRUE;
 }
@@ -79,6 +84,7 @@ cothread_stack_alloc_linuxthreads (char **low, char **high)
   
   if (!(chunk = g_static_private_get(&chunk_key))) {
     chunk = cothread_chunk_new (_cothread_attr_global->chunk_size, FALSE);
+    g_message ("created new chunk, %p, size=0x%x", chunk->chunk, chunk->size);
     g_static_private_set (&chunk_key, chunk, cothread_chunk_free);
   }
   
@@ -98,7 +104,7 @@ cothread_chunk_new (unsigned long size, gboolean allocate)
   ret->block_states = g_new0 (cothread_block_state, ret->nblocks);
   
   if (allocate) {
-    if (!posix_memalign(&ret->chunk, size, size))
+    if (posix_memalign(&ret->chunk, size, size))
       g_error ("memalign operation failed");
   } else {
     /* if we don't allocate the chunk, we must already be in it. */
@@ -107,7 +113,7 @@ cothread_chunk_new (unsigned long size, gboolean allocate)
 #if PTH_STACK_GROWTH > 0
     ret->reserved_bottom = sp - ret->chunk;
 #else
-    ret->reserved_bottom = sp + size - ret->chunk;
+    ret->reserved_bottom = ret->chunk + size - sp;
 #endif
   }
   
@@ -148,10 +154,11 @@ cothread_stack_alloc_chunked (cothread_chunk *chunk, char **low, char **high,
     
     for (block = 1; block < walk->nblocks; block++) {
       if (walk->block_states[block] == COTHREAD_BLOCK_STATE_UNUSED) {
+      walk->block_states[block] = COTHREAD_BLOCK_STATE_IN_USE;
 #if PTH_STACK_GROWTH > 0
-        *low  = walk->chunk + walk->size * (walk->nblocks - block - 1) / walk->nblocks;
+        *low  = walk->chunk + walk->size * block / walk->nblocks;
 #else
-        *low  = walk->chunk + walk->size * (block - 1) / walk->nblocks;
+        *low  = walk->chunk + walk->size * (walk->nblocks - block - 1) / walk->nblocks;
 #endif
         *high = *low + walk->size / walk->nblocks;
         return TRUE;
index a2d49b4..5425f76 100644 (file)
 
 #include <cothreads.h>
 
+typedef struct _cothread_private cothread_private;
+
+struct _cothread_private {
+  int argc;
+  char **argv;
+  void (*func) (int argc, char **argv);
+};
 
 extern cothread_attr *_cothread_attr_global;
 
-gboolean cothread_stack_alloc_on_gthread_stack (char **low, char **high);
-gboolean cothread_stack_alloc_linuxthreads (char **low, char **high);
-gboolean cothread_stack_alloc_on_heap (char **low, char **high);
+
+gboolean       cothread_stack_alloc_on_gthread_stack   (char **low, char **high);
+gboolean       cothread_stack_alloc_linuxthreads       (char **low, char **high);
+gboolean       cothread_stack_alloc_on_heap            (char **low, char **high);
 
 
 #endif /* __COTHREAD_PRIVATE_H__ */
index 5dc6cce..ead9ce2 100644 (file)
@@ -1,7 +1,7 @@
 /* Pthread-friendly coroutines with pth
  * Copyright (C) 2002 Andy Wingo <wingo@pobox.com>
  *
- * cothread.c: public API implementation
+ * cothreads.c: public API implementation
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -45,12 +45,37 @@ cothread_attr *_cothread_attr_global = NULL;
 
 static gboolean (*stack_alloc_func) (char**, char**);
 
-cothread*
-cothread_init (cothread_attr *attr)
+static void    cothread_private_set    (char *sp, void *priv, size_t size);
+static void    cothread_private_get    (char *sp, void *priv, size_t size);
+static void    cothread_stub           (void);
+
+
+/**
+ * cothreads_initialized:
+ *
+ * Query the state of the cothreads system.
+ *
+ * Returns: TRUE if cothreads_init() has already been called, FALSE otherwise
+ */
+gboolean
+cothreads_initialized (void) 
+{
+  return (_cothread_attr_global != NULL);
+}
+
+/**
+ * cothreads_init:
+ * @attr: attributes for creation of cothread stacks
+ *
+ * Initialize the cothreads system. If @attr is NULL, use the default parameters
+ * detected at compile-time.
+ */
+void
+cothreads_init (cothread_attr *attr)
 {
   static cothread_attr _attr;
   
-  if (_cothread_attr_global) {
+  if (cothreads_initialized()) {
     g_warning ("cothread system has already been initialized");
     return;
   }
@@ -75,14 +100,25 @@ cothread_init (cothread_attr *attr)
   default:
     g_error ("unexpected value for attr method %d", _cothread_attr_global->method);
   }
-  
-  return cothread_create (NULL);
 }
 
+/**
+ * cothread_create:
+ * @func: function to start with this cothread
+ * @argc: argument count
+ * @argv: argument vector
+ *
+ * Create a new cothread running a given function. You must explictly switch
+ * into this cothread to give it control. If @func is NULL, a cothread is
+ * created on the current stack with the current stack pointer.
+ *
+ * Returns: A pointer to the new cothread
+ */
 cothread*
-cothread_create (void (*func)(void))
+cothread_create (void (*func)(int, void **), int argc, void **argv)
 {
   char *low, *high;
+  cothread_private priv;
   cothread *ret = g_new0 (cothread, 1);
   
   if (!func) {
@@ -91,6 +127,10 @@ cothread_create (void (*func)(void))
     if (_cothread_attr_global->alloc_cothread_0)
       if (!stack_alloc_func (&low, &high))
         g_error ("couldn't create cothread 0");
+      else
+        g_message ("created cothread 0 with low=%p, high=%p", low, high);
+    else
+      g_message ("created cothread 0");
     
     pth_mctx_save (ret);
     return ret;
@@ -99,11 +139,24 @@ cothread_create (void (*func)(void))
   if (!stack_alloc_func (&low, &high))
     g_error ("could not allocate a new cothread stack");
   
-  pth_mctx_set (ret, func, low, high);
+  g_message ("created a cothread with low=%p, high=%p", low, high);
+  
+  pth_mctx_set (ret, cothread_stub, low, high);
+  
+  priv.argc = argc;
+  priv.argv = argv;
+  priv.func = func;
+  cothread_private_set (low, &priv, sizeof(priv));
   
   return ret;
 }
 
+/**
+ * cothread_destroy:
+ * @thread: cothread to destroy
+ *
+ * Deallocate any memory used by the cothread data structures.
+ */
 void
 cothread_destroy (cothread *thread)
 {
@@ -111,3 +164,51 @@ cothread_destroy (cothread *thread)
   
   g_free (thread);
 }
+
+/* the whole 'page size' thing is to allow for the last page of a stack or chunk
+ * to be mmap'd as a boundary page */
+
+static void
+cothread_private_set (char *sp, void *priv, size_t size)
+{
+  char *dest;
+  
+#if PTH_STACK_GROWTH > 0
+  dest = ((gulong)sp | (_cothread_attr_global->chunk_size / _cothread_attr_global->blocks_per_chunk - 1))
+    - size + 1 - getpagesize();
+#else
+  dest = ((gulong)sp &~ (_cothread_attr_global->chunk_size / _cothread_attr_global->blocks_per_chunk - 1))
+    + getpagesize();
+#endif
+  
+  memcpy (dest, priv, size);
+}
+
+static void
+cothread_private_get (char *sp, void *priv, size_t size)
+{
+  char *src;
+  
+#if PTH_STACK_GROWTH > 0
+  src = ((gulong)sp | (_cothread_attr_global->chunk_size / _cothread_attr_global->blocks_per_chunk - 1))
+    - size + 1 - getpagesize();
+#else
+  src = ((gulong)sp &~ (_cothread_attr_global->chunk_size / _cothread_attr_global->blocks_per_chunk - 1))
+    + getpagesize();
+#endif
+  
+  memcpy (priv, src, size);
+}
+
+static void
+cothread_stub (void)
+{
+  cothread_private priv;
+  
+  cothread_private_get (CURRENT_STACK_FRAME, &priv, sizeof (priv));
+  
+  priv.func (priv.argc, priv.argv);
+  
+  g_warning ("we really shouldn't get here");
+}
+
index 9429e6a..acdb268 100644 (file)
 #include <pth_p.h>
 
 
+#ifndef CURRENT_STACK_FRAME
+#define CURRENT_STACK_FRAME  ({ char __csf; &__csf; })
+#endif /* CURRENT_STACK_FRAME */
+
+
 typedef pth_mctx_t cothread;
 typedef enum _cothread_attr_method cothread_attr_method;
 typedef struct _cothread_attr cothread_attr;
@@ -39,17 +44,21 @@ enum _cothread_attr_method
 };
 
 struct _cothread_attr {
-  cothread_attr_method method;
-  int chunk_size;
-  int blocks_per_chunk;
-  gboolean alloc_cothread_0;
+  cothread_attr_method method;        /* the method of allocating new cothread stacks */
+  int chunk_size;                     /* size of contiguous chunk of memory for cothread stacks */
+  int blocks_per_chunk;               /* cothreads per chunk */
+  gboolean alloc_cothread_0;          /* if the first cothread needs to be allocated */
 };
 
 
-cothread*      cothread_init           (cothread_attr *attr);
-cothread*      cothread_create         (void (*func)(void));
+gboolean       cothreads_initialized   (void);
+void           cothreads_init          (cothread_attr *attr);
+
+cothread*      cothread_create         (void (*func)(int, void**), int argc, void **argv);
 void           cothread_destroy        (cothread *thread);
 
+/* 'old' and 'new' are of type (cothread*) */
 #define cothread_switch(old,new) pth_mctx_switch(old,new)
+#define        cothread_yield(new) pth_mctx_restore(new);
 
 #endif /* __COTHREAD_H__ */
index 009c8f2..d734d88 100644 (file)
@@ -1,49 +1,66 @@
 #include <cothreads.h>
 
-cothread *main_context;
-cothread *ctx;
-int threadnum = 0;
-
-void co_thread (void)
+void co_thread (int argc, void **argv)
 {
-  printf ("1.%d: sleeping 1s in thread %d...\n", threadnum, threadnum);
+  int pthreadnum =  *(int*)argv[0];
+  int cothreadnum = *(int*)argv[1];
+  cothread *main = argv[2];
+  cothread *self = argv[3];
+  
+  printf ("%d.%d: sleeping 1s...\n", pthreadnum, cothreadnum);
   sleep (1);
-  printf ("1.%d: returning to cothread 0\n", threadnum);
-  cothread_switch (ctx, main_context);
+  printf ("%d.%d: returning to cothread 0\n", pthreadnum, cothreadnum);
+  
+  cothread_switch (self, main);
 }
 
-void pthread (void* unused
+void pthread (void* _pthreadnum
 {
-  char *skaddr;
+  int pthreadnum = *(int*) _pthreadnum;
+  int cothreadnum = 0;
+  cothread *main, *new;
+  char *argv[4];
   
-  printf ("1: saving the main context\n");
-  main_context = cothread_init(NULL);
+  main = cothread_create (NULL, 0, NULL);
   
-  while (threadnum < 25) {
-    printf ("1: spawning a new cothread\n");
-    ctx = cothread_create (co_thread);
+  while (cothreadnum++ < 25) {
+    printf ("%d: spawning a new cothread\n", pthreadnum);
     
-    printf ("1: switching to cothread %d...\n", ++threadnum);
-    cothread_switch (main_context, ctx);
-  
-    printf ("1: back now, looping\n");
+    argv[0] = &pthreadnum;
+    argv[1] = &cothreadnum;
+    argv[2] = main;
+    argv[3] = cothread_create (co_thread, 4, argv);
+    new = argv[3];
+    
+    printf ("%d: switching to cothread %d...\n", pthreadnum, cothreadnum);
+    cothread_switch (main, new);
+    
+    printf ("%d: back now, looping\n", pthreadnum);
   }
 }
 
+#define NTHREADS 2
 
 int main (int argc, char *argv[])
 {
-  GThread *thread;
+  GThread *thread[NTHREADS];
+  int pthreadnum[4], i;
   
   g_thread_init(NULL);
+  cothreads_init(NULL);
+  
+  printf ("0: creating the gthreads\n");
+  
+  for (i=0; i<NTHREADS; i++) {
+    pthreadnum[i] = i+1;
+    thread[i] = g_thread_create (pthread, &pthreadnum[i], TRUE, NULL);
+  }
+  
+  printf ("0: joining the gthreads\n");
+  for (i=0; i<NTHREADS; i++) {
+    g_thread_join (thread[i]);
+  }
   
-  printf ("0: creating the gthread\n");
-
-  thread = g_thread_create (pthread, NULL, TRUE, NULL);
-
-  printf ("joining the gthread\n");
-  g_thread_join (thread);
-
   printf ("exiting\n");
   
   exit (0);