fix doc build fix autogen
[platform/upstream/gstreamer.git] / gst / cothreads.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * cothreads.c: Cothreading routines
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 #include <glib.h>
24
25 #include <sys/time.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <signal.h>
29 #include <setjmp.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/mman.h>
33
34 #include "gst_private.h"
35
36 #include "cothreads.h"
37 #include "gstarch.h"
38 #include "gstinfo.h"
39 #include "gstutils.h"
40
41 #ifdef HAVE_UCONTEXT_H
42 #include <ucontext.h>
43 #endif
44
45 /* older glibc's have MAP_ANON instead of MAP_ANONYMOUS */
46 #ifndef MAP_ANONYMOUS
47 #define MAP_ANONYMOUS MAP_ANON
48 #endif
49
50 #define STACK_SIZE 0x200000
51
52 #define COTHREAD_MAGIC_NUMBER 0xabcdef
53
54 #define COTHREAD_MAXTHREADS 16
55 #define COTHREAD_STACKSIZE (STACK_SIZE/COTHREAD_MAXTHREADS)
56
57 static void     cothread_destroy        (cothread_state *cothread);
58
59 struct _cothread_context
60 {
61   cothread_state *cothreads[COTHREAD_MAXTHREADS]; /* array of cothread states */
62   int ncothreads;
63   int current;
64   unsigned long stack_top;
65   GHashTable *data;
66   GThread *thread;
67 };
68
69 /* Disabling this define allows you to shut off a few checks in
70  * cothread_switch.  This likely will speed things up fractionally */
71 #define COTHREAD_PARANOID
72
73
74 /* this _cothread_ctx_key is used as a GThread key to the thread's context
75  * a GThread key is a "pointer" to memory space that is/can be different
76  * (ie. private) for each thread.  The key itself is shared among threads,
77  * so it only needs to be initialized once.
78  */
79 static GStaticPrivate _cothread_ctx_key = G_STATIC_PRIVATE_INIT;
80
81 /*
82  * This should only after context init, since we do checking.
83  */
84 static cothread_context *
85 cothread_get_current_context (void)
86 {
87   cothread_context *ctx;
88
89   ctx = g_static_private_get (&_cothread_ctx_key);
90   g_assert(ctx);
91
92 #ifdef COTHREAD_PARANOID
93   g_assert (ctx->thread == g_thread_self());
94 #endif
95
96   return ctx;
97 }
98
99 /**
100  * cothread_context_init:
101  *
102  * Create and initialize a new cothread context 
103  *
104  * Returns: the new cothread context
105  */
106 cothread_context *
107 cothread_context_init (void)
108 {
109   char __csf;
110   void *current_stack_frame = &__csf;  /* Get pointer inside current stack frame */
111   cothread_context *ctx;
112
113   /* if there already is a cotread context for this thread,
114    * just return it */
115   ctx = g_static_private_get (&_cothread_ctx_key);
116   if (ctx) {
117     GST_CAT_INFO (GST_CAT_COTHREADS, 
118               "returning private _cothread_ctx_key %p", ctx);
119     return ctx;
120   }
121
122   /*
123    * initalize the whole of the cothreads context 
124    */
125   ctx = (cothread_context *) g_malloc (sizeof (cothread_context));
126
127   /* we consider the initiating process to be cothread 0 */
128   ctx->ncothreads = 1;
129   ctx->current = 0;
130   ctx->data = g_hash_table_new (g_str_hash, g_str_equal);
131   ctx->thread = g_thread_self();
132
133   GST_CAT_INFO (GST_CAT_COTHREADS, "initializing cothreads");
134
135   /* set this thread's context pointer */
136   GST_CAT_INFO (GST_CAT_COTHREADS, "setting private _cothread_ctx_key to %p in thread %p",
137             ctx,g_thread_self());
138   g_static_private_set (&_cothread_ctx_key, ctx, NULL);
139
140   g_assert(ctx == cothread_get_current_context());
141
142   /* clear the cothread data */
143   memset (ctx->cothreads, 0, sizeof (ctx->cothreads));
144
145   /* FIXME this may not be 64bit clean
146    *       could use casts to uintptr_t from inttypes.h
147    *       if only all platforms had inttypes.h
148    */
149   /* stack_top is the address of the first byte past our stack segment. */
150   /* FIXME: an assumption is made that the stack segment is STACK_SIZE
151    * aligned. */
152   ctx->stack_top = ((gulong) current_stack_frame | (STACK_SIZE - 1)) + 1;
153   GST_CAT_DEBUG (GST_CAT_COTHREADS, "stack top is 0x%08lx", ctx->stack_top);
154
155   /*
156    * initialize the 0th cothread
157    */
158   ctx->cothreads[0] = (cothread_state *) g_malloc0 (sizeof (cothread_state));
159   ctx->cothreads[0]->ctx = ctx;
160   ctx->cothreads[0]->cothreadnum = 0;
161   ctx->cothreads[0]->func = NULL;
162   ctx->cothreads[0]->argc = 0;
163   ctx->cothreads[0]->argv = NULL;
164   ctx->cothreads[0]->priv = NULL;
165   ctx->cothreads[0]->flags = COTHREAD_STARTED;
166   ctx->cothreads[0]->sp = (void *) current_stack_frame;
167
168   GST_CAT_INFO (GST_CAT_COTHREADS, "0th cothread is %p at sp:%p", 
169             ctx->cothreads[0], ctx->cothreads[0]->sp);
170
171   return ctx;
172 }
173
174 /**
175  * cothread_context_free:
176  * @ctx: the cothread context to free
177  *
178  * Free the cothread context.
179  */
180 void
181 cothread_context_free (cothread_context *ctx)
182 {
183   gint i;
184
185   g_return_if_fail (ctx != NULL);
186   g_assert (ctx->thread == g_thread_self());
187   g_assert (ctx->current == 0);
188
189   GST_CAT_INFO (GST_CAT_COTHREADS, "free cothread context");
190
191   for (i = 1; i < COTHREAD_MAXTHREADS; i++) {
192     if (ctx->cothreads[i]) {
193       cothread_destroy (ctx->cothreads[i]);
194     }
195   }
196   if (ctx->cothreads[0]) {
197     g_free(ctx->cothreads[0]);
198     ctx->cothreads[0] = NULL;
199   }
200   g_hash_table_destroy (ctx->data);
201   /* make sure we free the private key for cothread context */
202   GST_CAT_INFO (GST_CAT_COTHREADS, "setting private _cothread_ctx_key to NULL in thread %p",
203             g_thread_self());
204   g_static_private_set (&_cothread_ctx_key, NULL, NULL);
205   g_free (ctx);
206 }
207
208 /**
209  * cothread_create:
210  * @ctx: the cothread context
211  *
212  * Create a new cothread state in the given context
213  *
214  * Returns: the new cothread state or NULL on error
215  */
216 cothread_state*
217 cothread_create (cothread_context *ctx)
218 {
219   cothread_state *cothread;
220   void *mmaped = 0;
221   gint slot = 0;
222   unsigned long page_size;
223
224   g_return_val_if_fail (ctx != NULL, NULL);
225
226   GST_CAT_DEBUG (GST_CAT_COTHREADS, "manager sef %p, cothread self %p", 
227                     ctx->thread, g_thread_self());
228
229   if (ctx->ncothreads == COTHREAD_MAXTHREADS) {
230     /* this is pretty fatal */
231     g_warning ("cothread_create: attempt to create > COTHREAD_MAXTHREADS");
232     return NULL;
233   }
234   /* find a free spot in the stack, note slot 0 has the main thread */
235   for (slot = 1; slot < ctx->ncothreads; slot++) {
236     if (ctx->cothreads[slot] == NULL)
237       break;
238     else if (ctx->cothreads[slot]->flags & COTHREAD_DESTROYED &&
239                     slot != ctx->current) {
240       cothread_destroy (ctx->cothreads[slot]);
241       break;
242     }
243   }
244
245   GST_CAT_DEBUG (GST_CAT_COTHREADS, "Found free cothread slot %d", slot);
246
247   /* cothread stack space of the thread is mapped in reverse, with cothread 0
248    * stack space at the top */
249   cothread = (cothread_state *) (ctx->stack_top - (slot + 1) * COTHREAD_STACKSIZE);
250   GST_CAT_DEBUG (GST_CAT_COTHREADS, "cothread pointer is %p", cothread);
251
252 #if 0
253   /* This tests to see whether or not we can grow down the stack */
254   {
255     unsigned long ptr;
256     for(ptr=ctx->stack_top - 4096; ptr > (unsigned long)cothread; ptr -= 4096){
257       GST_CAT_DEBUG (GST_CAT_COTHREADS, "touching location 0x%08lx", ptr);
258       *(volatile unsigned int *)ptr = *(volatile unsigned int *)ptr;
259       GST_CAT_DEBUG (GST_CAT_COTHREADS, "ok (0x%08x)", *(unsigned int *)ptr);
260     }
261   }
262 #endif
263
264 #ifdef _SC_PAGESIZE
265   page_size = sysconf(_SC_PAGESIZE);
266 #else
267   page_size = getpagesize();
268 #endif
269
270   /* The mmap is necessary on Linux/i386, and possibly others, since the
271    * kernel is picky about when we can expand our stack. */
272   GST_CAT_DEBUG (GST_CAT_COTHREADS, "mmaping %p, size 0x%08x", cothread,
273              COTHREAD_STACKSIZE);
274   /* Remap with a guard page. This decreases our stack size by 8 kB (for
275    * 4 kB pages) and also wastes almost 4 kB for the cothreads
276    * structure */
277   munmap((void *)cothread, COTHREAD_STACKSIZE);
278   mmaped = mmap ((void *) cothread, page_size,
279                  PROT_READ | PROT_WRITE,
280                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
281   mmaped = mmap (((void *) cothread) + page_size * 2,
282                  COTHREAD_STACKSIZE - page_size * 2,
283                  PROT_READ | PROT_WRITE | PROT_EXEC,
284                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
285   GST_CAT_DEBUG (GST_CAT_COTHREADS, "coming out of mmap");
286   if (mmaped == MAP_FAILED) {
287     perror ("mmap'ing cothread stack space");
288     return NULL;
289   }
290   if (mmaped != (void *)cothread + page_size * 2) {
291     g_warning ("could not mmap requested memory for cothread");
292     return NULL;
293   }
294
295   cothread->magic_number = COTHREAD_MAGIC_NUMBER;
296   GST_CAT_DEBUG (GST_CAT_COTHREADS, "create  cothread %d with magic number 0x%x",
297              slot, cothread->magic_number);
298   cothread->ctx = ctx;
299   cothread->cothreadnum = slot;
300   cothread->flags = 0;
301   cothread->priv = NULL;
302   cothread->sp = ((guchar *) cothread + COTHREAD_STACKSIZE);
303   cothread->stack_size = COTHREAD_STACKSIZE - page_size * 2;
304   cothread->stack_base = (void *)cothread + 2 * page_size;
305
306   GST_CAT_INFO (GST_CAT_COTHREADS, 
307             "created cothread #%d in slot %d: %p at sp:%p", 
308             ctx->ncothreads, slot, cothread, cothread->sp);
309
310   ctx->cothreads[slot] = cothread;
311   ctx->ncothreads++;
312
313   return cothread;
314 }
315
316 /**
317  * cothread_free:
318  * @cothread: the cothread state
319  *
320  * Free the given cothread state
321  */
322 void
323 cothread_free (cothread_state *cothread)
324 {
325   g_return_if_fail (cothread != NULL);
326
327   GST_CAT_INFO (GST_CAT_COTHREADS, "flag cothread %d for destruction", 
328             cothread->cothreadnum);
329
330   /* we simply flag the cothread for destruction here */
331   if (cothread)
332     cothread->flags |= COTHREAD_DESTROYED;
333   else
334     g_warning ("somebody set up us the bomb");
335 }
336
337 static void
338 cothread_destroy (cothread_state *cothread)
339 {
340   cothread_context *ctx;
341   gint cothreadnum;
342
343   g_return_if_fail (cothread != NULL);
344
345   cothreadnum = cothread->cothreadnum;
346   ctx = cothread->ctx;
347   g_assert (ctx->thread == g_thread_self());
348   g_assert (ctx == cothread_get_current_context());
349
350   GST_CAT_INFO (GST_CAT_COTHREADS, "destroy cothread %d %p %d", 
351             cothreadnum, cothread, ctx->current);
352
353   /* cothread 0 needs to be destroyed specially */
354   g_assert(cothreadnum != 0);
355
356   /* we have to unlock here because we might be switched out 
357    * with the lock held */
358   cothread_unlock (cothread);
359
360   /* doing cleanups of the cothread create */
361   GST_CAT_DEBUG (GST_CAT_COTHREADS, "destroy cothread %d with magic number 0x%x",
362            cothreadnum, cothread->magic_number);
363   g_assert (cothread->magic_number == COTHREAD_MAGIC_NUMBER);
364
365   g_assert (cothread->priv == NULL);
366
367   memset(cothread,0,sizeof(*cothread));
368
369   ctx->cothreads[cothreadnum] = NULL;
370   ctx->ncothreads--;
371 }
372
373 /**
374  * cothread_setfunc:
375  * @cothread: the cothread state
376  * @func: the function to call
377  * @argc: argument count for the cothread function
378  * @argv: arguments for the cothread function
379  *
380  * Set the cothread function
381  */
382 void
383 cothread_setfunc (cothread_state* cothread, cothread_func func, int argc, char **argv)
384 {
385   cothread->func = func;
386   cothread->argc = argc;
387   cothread->argv = argv;
388 }
389
390 /**
391  * cothread_stop:
392  * @cothread: the cothread to stop
393  *
394  * Stop the cothread and reset the stack and program counter.
395  */
396 void
397 cothread_stop (cothread_state* cothread)
398 {
399   cothread->flags &= ~COTHREAD_STARTED;
400 }
401
402 /**
403  * cothread_main:
404  * @ctx: cothread context to find main cothread of.
405  *
406  * Gets the main thread.
407  *
408  * Returns: the #cothread_state of the main (0th) cothread.
409  */
410 cothread_state*
411 cothread_main (cothread_context* ctx)
412 {
413   g_assert (ctx->thread == g_thread_self());
414
415   GST_CAT_DEBUG (GST_CAT_COTHREADS, "returning %p, the 0th cothread", 
416              ctx->cothreads[0]);
417   return ctx->cothreads[0];
418 }
419
420 /**
421  * cothread_current_main:
422  *
423  * Get the main thread in the current GThread.
424  *
425  * Returns: the #cothread_state of the main (0th) thread in the current GThread
426  */
427 cothread_state*
428 cothread_current_main (void)
429 {
430   cothread_context *ctx = cothread_get_current_context();
431
432   return ctx->cothreads[0];
433 }
434
435 /**
436  * cothread_current:
437  *
438  * Get the currenttly executing cothread
439  *
440  * Returns: the #cothread_state of the current cothread
441  */
442 cothread_state*
443 cothread_current (void)
444 {
445   cothread_context *ctx = cothread_get_current_context();
446
447   return ctx->cothreads[ctx->current];
448 }
449
450 static void
451 cothread_stub (void)
452 {
453   cothread_context *ctx = cothread_get_current_context();
454   cothread_state *cothread = ctx->cothreads[ctx->current];
455 #ifndef GST_DISABLE_GST_DEBUG 
456   char __csf;
457   void *current_stack_frame = &__csf;
458 #endif
459
460   GST_CAT_DEBUG (GST_CAT_COTHREADS, "stack addr %p", &ctx);
461
462   cothread->flags |= COTHREAD_STARTED;
463
464   while (TRUE) {
465     cothread->func (cothread->argc, cothread->argv);
466
467     GST_CAT_DEBUG (GST_CAT_COTHREADS, "cothread[%d] thread->func exited", ctx->current);
468
469     GST_CAT_DEBUG (GST_CAT_COTHREADS, "sp=%p", current_stack_frame);
470     GST_CAT_DEBUG (GST_CAT_COTHREADS, "ctx=%p current=%p", ctx,cothread_get_current_context());
471     g_assert (ctx == cothread_get_current_context());
472
473     g_assert (ctx->current != 0);
474
475     /* we do this to avoid ever returning, we just switch to 0th thread */
476     cothread_switch (cothread_main (ctx));
477   }
478 }
479
480 /**
481  * cothread_getcurrent:
482  *
483  * Get the current cothread id
484  *
485  * Returns: the current cothread id
486  */
487 int cothread_getcurrent (void) G_GNUC_NO_INSTRUMENT;
488
489 int
490 cothread_getcurrent (void)
491 {
492   cothread_context *ctx = cothread_get_current_context();
493
494   if (!ctx)
495     return -1;
496
497   return ctx->current;
498 }
499
500 /**
501  * cothread_set_private:
502  * @cothread: the cothread state
503  * @data: the data
504  *
505  * set private data for the cothread.
506  */
507 void
508 cothread_set_private (cothread_state *cothread, gpointer data)
509 {
510   cothread->priv = data;
511 }
512
513 /**
514  * cothread_context_set_data:
515  * @cothread: the cothread state
516  * @key: a key for the data
517  * @data: the data
518  *
519  * adds data to a cothread
520  */
521 void
522 cothread_context_set_data (cothread_state *cothread, gchar *key, gpointer data)
523 {
524   cothread_context *ctx = cothread_get_current_context();
525
526   g_hash_table_insert (ctx->data, key, data);
527 }
528
529 /**
530  * cothread_get_private:
531  * @cothread: the cothread state
532  *
533  * get the private data from the cothread
534  *
535  * Returns: the private data of the cothread
536  */
537 gpointer
538 cothread_get_private (cothread_state *cothread)
539 {
540   return cothread->priv;
541 }
542
543 /**
544  * cothread_context_get_data:
545  * @cothread: the cothread state
546  * @key: a key for the data
547  *
548  * get data from the cothread
549  *
550  * Returns: the data associated with the key
551  */
552 gpointer
553 cothread_context_get_data (cothread_state *cothread, gchar *key)
554 {
555   cothread_context *ctx = cothread_get_current_context();
556
557   return g_hash_table_lookup (ctx->data, key);
558 }
559
560 /**
561  * cothread_switch:
562  * @cothread: cothread state to switch to
563  *
564  * Switches to the given cothread state
565  */
566 void
567 cothread_switch (cothread_state *cothread)
568 {
569   cothread_context *ctx;
570   cothread_state *current;
571   int enter;
572
573 #ifdef COTHREAD_PARANOID
574   if (cothread == NULL)
575     goto nothread;
576 #endif
577   ctx = cothread->ctx;
578
579   /* paranoia check to make sure we're in the right thread */
580   g_assert (ctx->thread == g_thread_self());
581
582 #ifdef COTHREAD_PARANOID
583   if (ctx == NULL)
584     goto nocontext;
585 #endif
586
587   current = ctx->cothreads[ctx->current];
588 #ifdef COTHREAD_PARANOID
589   if (current == NULL)
590     goto nocurrent;
591 #endif
592   if (current == cothread)
593     goto selfswitch;
594
595
596   /* find the number of the thread to switch to */
597   GST_CAT_INFO (GST_CAT_COTHREAD_SWITCH, 
598             "switching from cothread #%d to cothread #%d",
599             ctx->current, cothread->cothreadnum);
600   ctx->current = cothread->cothreadnum;
601
602   /* save the current stack pointer, frame pointer, and pc */
603 #ifdef GST_ARCH_PRESETJMP
604   GST_ARCH_PRESETJMP ();
605 #endif
606   enter = setjmp (current->jmp);
607   if (enter != 0) {
608     GST_CAT_DEBUG (GST_CAT_COTHREADS, 
609                "enter cothread #%d %d sp=%p jmpbuf=%p", 
610                current->cothreadnum, enter, current->sp, current->jmp);
611     return;
612   }
613   GST_CAT_DEBUG (GST_CAT_COTHREADS, "exit cothread #%d %d sp=%p jmpbuf=%p", 
614              current->cothreadnum, enter, current->sp, current->jmp);
615   enter = 1;
616
617   if (current->flags & COTHREAD_DESTROYED) {
618     cothread_destroy (current);
619   }
620
621   GST_CAT_DEBUG (GST_CAT_COTHREADS, "set stack to %p", cothread->sp);
622   /* restore stack pointer and other stuff of new cothread */
623   if (cothread->flags & COTHREAD_STARTED) {
624     GST_CAT_DEBUG (GST_CAT_COTHREADS, "via longjmp() jmpbuf %p", cothread->jmp);
625     /* switch to it */
626     longjmp (cothread->jmp, 1);
627   }
628   else {
629 #ifdef HAVE_MAKECONTEXT
630     ucontext_t ucp;
631
632     GST_CAT_DEBUG (GST_CAT_COTHREADS, "making context");
633
634     g_assert (cothread != cothread_main(ctx));
635
636     getcontext (&ucp);
637     ucp.uc_stack.ss_sp = (void *)cothread->stack_base;
638     ucp.uc_stack.ss_size = cothread->stack_size;
639     makecontext (&ucp, cothread_stub, 0);
640     setcontext (&ucp);
641 #else
642     GST_ARCH_SETUP_STACK ((char*)cothread->sp);
643     GST_ARCH_SET_SP (cothread->sp);
644     /* start it */
645     GST_ARCH_CALL (cothread_stub);
646 #endif
647
648     GST_CAT_DEBUG (GST_CAT_COTHREADS, "exit thread ");
649     ctx->current = 0;
650   }
651
652   return;
653
654 #ifdef COTHREAD_PARANOID
655 nothread:
656   g_warning ("cothread: can't switch to NULL cothread!");
657   return;
658 nocontext:
659   g_warning ("cothread: there's no context, help!");
660   exit (2);
661 nocurrent:
662   g_warning ("cothread: there's no current thread, help!");
663   exit (2);
664 #endif /* COTHREAD_PARANOID */
665 selfswitch:
666   g_warning ("cothread: trying to switch to same thread, legal but not necessary");
667   return;
668 }
669
670 /**
671  * cothread_lock:
672  * @cothread: cothread state to lock
673  *
674  * Locks the cothread state.
675  */
676 void
677 cothread_lock (cothread_state *cothread)
678 {
679 }
680
681 /**
682  * cothread_trylock:
683  * @cothread: cothread state to try to lock
684  *
685  * Try to lock the cothread state
686  *
687  * Returns: TRUE if the cothread could be locked.
688  */
689 gboolean
690 cothread_trylock (cothread_state *cothread)
691 {
692   return TRUE;
693 }
694
695 /**
696  * cothread_unlock:
697  * @cothread: cothread state to unlock
698  *
699  * Unlock the cothread state.
700  */
701 void
702 cothread_unlock (cothread_state *cothread)
703 {
704 }