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