2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
5 * cothreads.c: Cothreading routines
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
31 #include "gst_private.h"
33 #include "cothreads.h"
38 #define STACK_SIZE 0x200000
40 #define COTHREAD_MAXTHREADS 16
41 #define COTHREAD_STACKSIZE (STACK_SIZE/COTHREAD_MAXTHREADS)
43 static void cothread_destroy (cothread_state *thread);
45 struct _cothread_context
47 cothread_state *threads[COTHREAD_MAXTHREADS];
54 static pthread_key_t _cothread_key = -1;
56 /* Disablig this define allows you to shut off a few checks in
57 * cothread_switch. This likely will speed things up fractionally */
58 #define COTHREAD_PARANOID
61 * cothread_context_init:
63 * Create and initialize a new cothread context
65 * Returns: the new cothread context
68 cothread_context_init (void)
70 cothread_context *ctx = (cothread_context *) g_malloc (sizeof (cothread_context));
72 /* we consider the initiating process to be cothread 0 */
75 ctx->data = g_hash_table_new (g_str_hash, g_str_equal);
77 GST_INFO (GST_CAT_COTHREADS, "initializing cothreads");
79 if (_cothread_key == (pthread_key_t)-1) {
80 if (pthread_key_create (&_cothread_key, NULL) != 0) {
81 perror ("pthread_key_create");
85 pthread_setspecific (_cothread_key, ctx);
87 memset (ctx->threads, 0, sizeof (ctx->threads));
89 ctx->threads[0] = (cothread_state *) g_malloc (sizeof (cothread_state));
90 ctx->threads[0]->ctx = ctx;
91 ctx->threads[0]->threadnum = 0;
92 ctx->threads[0]->func = NULL;
93 ctx->threads[0]->argc = 0;
94 ctx->threads[0]->argv = NULL;
95 ctx->threads[0]->priv = NULL;
96 ctx->threads[0]->flags = COTHREAD_STARTED;
97 ctx->threads[0]->sp = (void *) CURRENT_STACK_FRAME;
98 ctx->threads[0]->pc = 0;
100 /* initialize the lock */
101 #ifdef COTHREAD_ATOMIC
102 atomic_set (&ctx->threads[0]->lock, 0);
104 ctx->threads[0]->lock = g_mutex_new ();
107 GST_INFO (GST_CAT_COTHREADS, "0th thread is %p at sp:%p", ctx->threads[0], ctx->threads[0]->sp);
113 * cothread_context_free:
114 * @ctx: the cothread context to free
116 * Free the cothread context.
119 cothread_context_free (cothread_context *ctx)
123 g_return_if_fail (ctx != NULL);
125 GST_INFO (GST_CAT_COTHREADS, "free cothread context");
127 for (i = 0; i < COTHREAD_MAXTHREADS; i++) {
128 if (ctx->threads[i]) {
129 cothread_destroy (ctx->threads[i]);
132 g_hash_table_destroy (ctx->data);
138 * @ctx: the cothread context
140 * Create a new cothread state in the given context
142 * Returns: the new cothread state or NULL on error
145 cothread_create (cothread_context *ctx)
147 cothread_state *thread;
152 g_return_val_if_fail (ctx != NULL, NULL);
154 if (ctx->nthreads == COTHREAD_MAXTHREADS) {
155 /* this is pretty fatal */
156 g_warning ("cothread_create: attempt to create > COTHREAD_MAXTHREADS\n");
159 /* find a free spot in the stack, note slot 0 has the main thread */
160 for (slot = 1; slot < ctx->nthreads; slot++) {
161 if (ctx->threads[slot] == NULL)
163 else if (ctx->threads[slot]->flags & COTHREAD_DESTROYED &&
164 slot != ctx->current) {
165 cothread_destroy (ctx->threads[slot]);
170 sp = CURRENT_STACK_FRAME;
171 /* FIXME this may not be 64bit clean
172 * could use casts to uintptr_t from inttypes.h
173 * if only all platforms had inttypes.h
175 stack_end = (guchar *) ((gulong) sp & ~(STACK_SIZE - 1));
177 thread = (cothread_state *) (stack_end + ((slot - 1) * COTHREAD_STACKSIZE));
178 GST_DEBUG (0, "new stack at %p", thread);
180 if (mmap ((void *) thread, COTHREAD_STACKSIZE,
181 PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == MAP_FAILED) {
182 perror ("mmap'ing cothread stack space");
187 thread->threadnum = slot;
190 thread->sp = ((guchar *) thread + COTHREAD_STACKSIZE);
191 thread->top_sp = thread->sp; /* for debugging purposes to detect stack overruns */
193 /* initialize the lock */
194 #ifdef COTHREAD_ATOMIC
195 atomic_set (thread->lock, 0);
197 thread->lock = g_mutex_new ();
200 GST_INFO (GST_CAT_COTHREADS, "created cothread #%d in slot %d: %p at sp:%p lock:%p",
201 ctx->nthreads, slot, thread, thread->sp, thread->lock);
203 ctx->threads[slot] = thread;
211 * @thread: the cothread state
213 * Free the given cothread state
216 cothread_free (cothread_state *thread)
218 g_return_if_fail (thread != NULL);
220 GST_INFO (GST_CAT_COTHREADS, "flag cothread %d for destruction", thread->threadnum);
222 /* we simply flag the cothread for destruction here */
223 thread->flags |= COTHREAD_DESTROYED;
227 cothread_destroy (cothread_state *thread)
229 cothread_context *ctx;
232 g_return_if_fail (thread != NULL);
234 threadnum = thread->threadnum;
237 GST_INFO (GST_CAT_COTHREADS, "destroy cothread %d %p %d", threadnum, thread, ctx->current);
239 /* we have to unlock here because we might be switched out with the lock held */
240 cothread_unlock (thread);
242 #ifndef COTHREAD_ATOMIC
243 g_mutex_free (thread->lock);
246 if (threadnum == 0) {
250 /* this doesn't seem to work very well */
251 /* munmap ((void *) thread, COTHREAD_STACKSIZE); */
254 ctx->threads[threadnum] = NULL;
260 * @thread: the cothread state
261 * @func: the function to call
262 * @argc: argument count for the cothread function
263 * @argv: arguments for the cothread function
265 * Set the cothread function
268 cothread_setfunc (cothread_state * thread, cothread_func func, int argc, char **argv)
273 thread->pc = (void *) func;
278 * @thread: the cothread to stop
280 * Stop the cothread and reset the stack and program counter.
283 cothread_stop (cothread_state * thread)
285 thread->flags &= ~COTHREAD_STARTED;
287 thread->sp = thread->top_sp;
292 * @ctx: cothread context to find main thread of
294 * Get the main thread.
296 * Returns: the #cothread_state of the main (0th) thread
299 cothread_main (cothread_context * ctx)
301 GST_DEBUG (0, "returning %p, the 0th cothread", ctx->threads[0]);
302 return ctx->threads[0];
306 * cothread_current_main:
308 * Get the main thread in the current pthread.
310 * Returns: the #cothread_state of the main (0th) thread in the current pthread
313 cothread_current_main (void)
315 cothread_context *ctx = pthread_getspecific (_cothread_key);
317 return ctx->threads[0];
323 * Get the currenttly executing cothread
325 * Returns: the #cothread_state of the current cothread
328 cothread_current (void)
330 cothread_context *ctx = pthread_getspecific (_cothread_key);
332 return ctx->threads[ctx->current];
338 cothread_context *ctx = pthread_getspecific (_cothread_key);
339 register cothread_state *thread = ctx->threads[ctx->current];
341 GST_DEBUG_ENTER ("");
343 thread->flags |= COTHREAD_STARTED;
345 * ifdef COTHREAD_ATOMIC
346 * do something here to lock
348 * g_mutex_lock(thread->lock);
353 thread->func (thread->argc, thread->argv);
354 /* we do this to avoid ever returning, we just switch to 0th thread */
355 cothread_switch (cothread_main (ctx));
357 GST_DEBUG_LEAVE ("");
361 * cothread_getcurrent:
363 * Get the current cothread id
365 * Returns: the current cothread id
367 int cothread_getcurrent (void) __attribute__ ((no_instrument_function));
369 cothread_getcurrent (void)
371 cothread_context *ctx = pthread_getspecific (_cothread_key);
379 * cothread_set_private:
380 * @thread: the cothread state
383 * set private data for the cothread.
386 cothread_set_private (cothread_state *thread, gpointer data)
392 * cothread_context_set_data:
393 * @thread: the cothread state
394 * @key: a key for the data
397 * adds data to a cothread
400 cothread_context_set_data (cothread_state *thread, gchar *key, gpointer data)
402 cothread_context *ctx = pthread_getspecific (_cothread_key);
404 g_hash_table_insert (ctx->data, key, data);
408 * cothread_get_private:
409 * @thread: the cothread state
411 * get the private data from the cothread
413 * Returns: the private data of the cothread
416 cothread_get_private (cothread_state *thread)
422 * cothread_context_get_data:
423 * @thread: the cothread state
424 * @key: a key for the data
426 * get data from the cothread
428 * Returns: the data associated with the key
431 cothread_context_get_data (cothread_state * thread, gchar * key)
433 cothread_context *ctx = pthread_getspecific (_cothread_key);
435 return g_hash_table_lookup (ctx->data, key);
440 * @thread: cothread state to switch to
442 * Switches to the given cothread state
445 cothread_switch (cothread_state * thread)
447 cothread_context *ctx;
448 cothread_state *current;
451 #ifdef COTHREAD_PARANOID
456 #ifdef COTHREAD_PARANOID
461 current = ctx->threads[ctx->current];
462 #ifdef COTHREAD_PARANOID
466 if (current == thread)
469 /* unlock the current thread, we're out of that context now */
470 #ifdef COTHREAD_ATOMIC
471 /* do something to unlock the cothread */
473 g_mutex_unlock (current->lock);
476 /* lock the next cothread before we even switch to it */
477 #ifdef COTHREAD_ATOMIC
478 /* do something to lock the cothread */
480 g_mutex_lock (thread->lock);
483 /* find the number of the thread to switch to */
484 GST_INFO (GST_CAT_COTHREAD_SWITCH, "switching from cothread #%d to cothread #%d",
485 ctx->current, thread->threadnum);
486 ctx->current = thread->threadnum;
488 /* save the current stack pointer, frame pointer, and pc */
489 #ifdef GST_ARCH_PRESETJMP
490 GST_ARCH_PRESETJMP ();
492 enter = setjmp (current->jmp);
494 GST_DEBUG (0, "enter thread #%d %d %p<->%p (%d)", current->threadnum, enter,
495 current->sp, current->top_sp, (char*)current->top_sp - (char*)current->sp);
498 GST_DEBUG (0, "exit thread #%d %d %p<->%p (%d)", current->threadnum, enter,
499 current->sp, current->top_sp, (char*)current->top_sp - (char*)current->sp);
502 if (current->flags & COTHREAD_DESTROYED)
503 cothread_destroy (current);
505 GST_DEBUG (0, "set stack to %p", thread->sp);
506 /* restore stack pointer and other stuff of new cothread */
507 if (thread->flags & COTHREAD_STARTED) {
508 GST_DEBUG (0, "in thread ");
510 longjmp (thread->jmp, 1);
513 GST_ARCH_SETUP_STACK ((char*)thread->sp);
514 GST_ARCH_SET_SP (thread->sp);
516 GST_ARCH_CALL (cothread_stub);
517 GST_DEBUG (0, "exit thread ");
523 #ifdef COTHREAD_PARANOID
525 g_print ("cothread: can't switch to NULL cothread!\n");
528 g_print ("cothread: there's no context, help!\n");
531 g_print ("cothread: there's no current thread, help!\n");
533 #endif /* COTHREAD_PARANOID */
535 g_print ("cothread: trying to switch to same thread, legal but not necessary\n");
541 * @thread: cothread state to lock
543 * Locks the cothread state.
546 cothread_lock (cothread_state * thread)
548 #ifdef COTHREAD_ATOMIC
549 /* do something to lock the cothread */
552 g_mutex_lock (thread->lock);
558 * @thread: cothread state to try to lock
560 * Try to lock the cothread state
562 * Returns: TRUE if the cothread could be locked.
565 cothread_trylock (cothread_state * thread)
567 #ifdef COTHREAD_ATOMIC
568 /* do something to try to lock the cothread */
571 return g_mutex_trylock (thread->lock);
579 * @thread: cothread state to unlock
581 * Unlock the cothread state.
584 cothread_unlock (cothread_state * thread)
586 #ifdef COTHREAD_ATOMIC
587 /* do something to unlock the cothread */
590 g_mutex_unlock (thread->lock);