moved cothread parameters into .c to avoid rebuilding libgst.la every time
[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 <pthread.h>
24 #include <stdio.h>   
25 #include <stdlib.h>
26 #include <signal.h>   
27 #include <setjmp.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30
31 /* we make too much noise for normal debugging... */
32 //#define GST_DEBUG_FORCE_DISABLE
33 #include "gst_private.h"
34
35 #include "cothreads.h"
36 #include "gstarch.h"
37
38
39 #define COTHREAD_STACKSIZE 8192
40 #define COTHREAD_MAXTHREADS 64
41 #define STACK_SIZE 0x200000
42
43
44 pthread_key_t _cothread_key = -1;
45
46 /* Disablig this define allows you to shut off a few checks in
47  * cothread_switch.  This likely will speed things up fractionally */
48 #define COTHREAD_PARANOID
49
50 /**
51  * cothread_init:
52  *
53  * Create and initialize a new cothread context 
54  *
55  * Returns: the new cothread context
56  */
57 cothread_context*
58 cothread_init (void) 
59 {
60   cothread_context *ctx = (cothread_context *)malloc(sizeof(cothread_context));
61
62   GST_INFO (GST_CAT_COTHREADS,"initializing cothreads");
63
64   if (_cothread_key == -1) {
65     if (pthread_key_create (&_cothread_key,NULL) != 0) {
66       perror ("pthread_key_create");
67       return NULL;
68     }
69   }
70   pthread_setspecific (_cothread_key,ctx);
71
72   memset (ctx->threads,0,sizeof(ctx->threads));
73
74   ctx->threads[0] = (cothread_state *)malloc(sizeof(cothread_state));
75   ctx->threads[0]->ctx = ctx;
76   ctx->threads[0]->threadnum = 0;
77   ctx->threads[0]->func = NULL;
78   ctx->threads[0]->argc = 0;
79   ctx->threads[0]->argv = NULL;
80   ctx->threads[0]->flags = COTHREAD_STARTED;
81   ctx->threads[0]->sp = (int *)CURRENT_STACK_FRAME;
82   ctx->threads[0]->pc = 0;
83
84   GST_INFO (GST_CAT_COTHREADS,"0th thread is %p at sp:%p",ctx->threads[0], ctx->threads[0]->sp);
85
86   // we consider the initiating process to be cothread 0
87   ctx->nthreads = 1;
88   ctx->current = 0;
89   ctx->data = g_hash_table_new(g_str_hash, g_str_equal);
90
91   return ctx;
92 }
93
94 /**
95  * cothread_create:
96  * @ctx: the cothread context
97  *
98  * Create a new cothread state in the given context
99  *
100  * Returns: the new cothread state
101  */
102 cothread_state*
103 cothread_create (cothread_context *ctx) 
104 {
105   cothread_state *s;
106
107   GST_DEBUG (0,"pthread_self() %ld\n",pthread_self());
108   //if (0) {
109   if (pthread_self() == 0) {
110     s = (cothread_state *)malloc(sizeof(int) * COTHREAD_STACKSIZE);
111     GST_DEBUG (0,"new stack (case 1) at %p\n",s);
112   } else {
113     char *sp = CURRENT_STACK_FRAME;
114     unsigned long *stack_end = (unsigned long *)((unsigned long)sp &
115       ~(STACK_SIZE - 1));
116     s = (cothread_state *)(stack_end + ((ctx->nthreads - 1) *
117                            COTHREAD_STACKSIZE));
118     GST_DEBUG (0,"new stack (case 2) at %p\n",s);
119     if (mmap((char *)s,COTHREAD_STACKSIZE*(sizeof(int)),
120              PROT_READ|PROT_WRITE|PROT_EXEC,MAP_FIXED|MAP_PRIVATE|MAP_ANON,
121              -1,0) < 0) {
122       perror("mmap'ing cothread stack space");
123       return NULL;
124     }
125   }
126
127   s->ctx = ctx;
128   s->threadnum = ctx->nthreads;
129   s->flags = 0;
130   s->sp = ((int *)s + COTHREAD_STACKSIZE);
131   // is this needed anymore?
132   s->top_sp = s->sp;
133
134   GST_INFO (GST_CAT_COTHREADS,"created cothread #%d: %p at sp:%p", ctx->nthreads, s, s->sp);
135
136   ctx->threads[ctx->nthreads++] = s;
137
138   return s;
139 }
140
141 /**
142  * cothread_setfunc:
143  * @thread: the cothread state
144  * @func: the function to call
145  * @argc: argument count for the cothread function
146  * @argv: arguments for the cothread function
147  *
148  * Set the cothread function
149  */
150 void 
151 cothread_setfunc (cothread_state *thread,
152                   cothread_func func,
153                   int argc,
154                   char **argv) 
155 {
156   thread->func = func;
157   thread->argc = argc;
158   thread->argv = argv;
159   thread->pc = (int *)func;
160 }
161
162 /**
163  * cothread_main:
164  * @ctx: cothread context to find main thread of
165  *
166  * Returns: the #cothread_state of the main (0th) thread
167  */
168 cothread_state*
169 cothread_main(cothread_context *ctx) 
170 {
171   GST_DEBUG (0,"returning %p, the 0th cothread\n",ctx->threads[0]);
172   return ctx->threads[0];
173 }
174
175 static void 
176 cothread_stub (void) 
177 {
178   cothread_context *ctx = pthread_getspecific(_cothread_key);
179   register cothread_state *thread = ctx->threads[ctx->current];
180
181   GST_DEBUG_ENTER("");
182
183   thread->flags |= COTHREAD_STARTED;
184   while (1) {
185     thread->func(thread->argc,thread->argv);
186     // we do this to avoid ever returning, we just switch to 0th thread
187     cothread_switch(cothread_main(ctx));
188   }
189   thread->flags &= ~COTHREAD_STARTED;
190   thread->pc = 0;
191   thread->sp = thread->top_sp;
192   fprintf(stderr,"uh, yeah, we shouldn't be here, but we should deal anyway\n");
193   GST_DEBUG_LEAVE("");
194 }
195
196 /**
197  * cothread_getcurrent:
198  *
199  * Returns: the current cothread id
200  */
201 int cothread_getcurrent(void) {
202   cothread_context *ctx = pthread_getspecific(_cothread_key);
203   if (!ctx) return -1;
204   return ctx->current;
205 }
206
207 /**
208  * cothread_set_data:
209  * @thread: the cothread state
210  * @key: a key for the data
211  * @data: the data
212  *
213  * adds data to a cothread
214  */
215 void
216 cothread_set_data (cothread_state *thread, 
217                    gchar *key,
218                    gpointer data)
219 {
220   cothread_context *ctx = pthread_getspecific(_cothread_key);
221
222   g_hash_table_insert(ctx->data, key, data);
223 }
224
225 /**
226  * cothread_get_data:
227  * @thread: the cothread state
228  * @key: a key for the data
229  *
230  * get data from the cothread
231  *
232  * Returns: the data assiciated with the key
233  */
234 gpointer
235 cothread_get_data (cothread_state *thread, 
236                    gchar *key)
237 {
238   cothread_context *ctx = pthread_getspecific(_cothread_key);
239
240   return g_hash_table_lookup(ctx->data, key);
241 }
242
243 /**
244  * cothread_switch:
245  * @thread: cothread state to switch to
246  *
247  * Switches to the given cothread state
248  */
249 void 
250 cothread_switch (cothread_state *thread) 
251 {
252   cothread_context *ctx;
253   cothread_state *current;
254   int enter;
255
256 #ifdef COTHREAD_PARANOID
257   if (thread == NULL) goto nothread;
258 #endif
259   ctx = thread->ctx;
260 #ifdef COTHREAD_PARANOID
261   if (ctx == NULL) goto nocontext;
262 #endif
263
264   current = ctx->threads[ctx->current];
265 #ifdef COTHREAD_PARANOID
266   if (current == NULL) goto nocurrent;
267 #endif
268   if (current == thread) goto selfswitch;
269
270   // find the number of the thread to switch to
271   GST_INFO (GST_CAT_COTHREAD_SWITCH,"switching from cothread %d to to cothread #%d",
272             ctx->current,thread->threadnum);
273   ctx->current = thread->threadnum;
274
275   /* save the current stack pointer, frame pointer, and pc */
276 #ifdef GST_ARCH_PRESETJMP
277   GST_ARCH_PRESETJMP();
278 #endif
279   enter = setjmp(current->jmp);
280   if (enter != 0) {
281     GST_DEBUG (0,"enter thread #%d %d %p<->%p (%d)\n",current->threadnum, enter, 
282                     current->sp, current->top_sp, current->top_sp-current->sp);
283     return;
284   }
285   GST_DEBUG (0,"exit thread #%d %d %p<->%p (%d)\n",current->threadnum, enter, 
286                     current->sp, current->top_sp, current->top_sp-current->sp);
287   enter = 1;
288
289   GST_DEBUG (0,"set stack to %p\n", thread->sp);
290   /* restore stack pointer and other stuff of new cothread */
291   if (thread->flags & COTHREAD_STARTED) {
292     GST_DEBUG (0,"in thread \n");
293     // switch to it
294     longjmp(thread->jmp,1);
295   } else {
296     GST_ARCH_SETUP_STACK(thread->sp);
297     GST_ARCH_SET_SP(thread->sp);
298     // start it
299     GST_ARCH_CALL(cothread_stub);
300     GST_DEBUG (0,"exit thread \n");
301     ctx->current = 0;
302   }
303
304   return;
305
306 #ifdef COTHREAD_PARANOID
307 nothread:
308   g_print("cothread: there's no thread, strange...\n");
309   return;
310 nocontext:
311   g_print("cothread: there's no context, help!\n");
312   exit(2);
313 nocurrent:
314   g_print("cothread: there's no current thread, help!\n");
315   exit(2);
316 #endif /* COTHREAD_PARANOID */
317 selfswitch:
318   g_print("cothread: trying to switch to same thread, legal but not necessary\n");
319   return;
320 }