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;
}
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);
}
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. */
#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
}
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;
#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__ */
/* 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
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;
}
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) {
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;
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)
{
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");
+}
+
#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;
};
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__ */
#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);