OSDN Git Service

2005-11-20 David Ayers <d.ayers@inode.at>
[pf3gnuchains/gcc-fork.git] / libobjc / thr.c
1 /* GNU Objective C Runtime Thread Interface
2    Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3    Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it under the
8 terms of the GNU General Public License as published by the Free Software
9 Foundation; either version 2, or (at your option) any later version.
10
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14 details.
15
16 You should have received a copy of the GNU General Public License along with
17 GCC; see the file COPYING.  If not, write to the Free Software
18 Foundation, 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.  */
20
21 /* As a special exception, if you link this library with files compiled with
22    GCC to produce an executable, this does not cause the resulting executable
23    to be covered by the GNU General Public License. This exception does not
24    however invalidate any other reasons why the executable file might be
25    covered by the GNU General Public License.  */
26
27 #include <stdlib.h>
28 #include "objc/runtime.h"
29
30 /* Global exit status. */
31 int __objc_thread_exit_status = 0;
32
33 /* Flag which lets us know if we ever became multi threaded */
34 int __objc_is_multi_threaded = 0;
35
36 /* The hook function called when the runtime becomes multi threaded */
37 objc_thread_callback _objc_became_multi_threaded = NULL;
38
39 /*
40   Use this to set the hook function that will be called when the 
41   runtime initially becomes multi threaded.
42   The hook function is only called once, meaning only when the 
43   2nd thread is spawned, not for each and every thread.
44
45   It returns the previous hook function or NULL if there is none.
46
47   A program outside of the runtime could set this to some function so
48   it can be informed; for example, the GNUstep Base Library sets it 
49   so it can implement the NSBecomingMultiThreaded notification.
50   */
51 objc_thread_callback objc_set_thread_callback (objc_thread_callback func)
52 {
53   objc_thread_callback temp = _objc_became_multi_threaded;
54   _objc_became_multi_threaded = func;
55   return temp;
56 }
57
58 /*
59   Private functions
60
61   These functions are utilized by the frontend, but they are not
62   considered part of the public interface.
63   */
64
65 /*
66   First function called in a thread, starts everything else.
67
68   This function is passed to the backend by objc_thread_detach
69   as the starting function for a new thread.
70  */
71 struct __objc_thread_start_state
72 {
73   SEL selector;
74   id object;
75   id argument;
76 };
77
78 static void __attribute__((noreturn))
79 __objc_thread_detach_function (struct __objc_thread_start_state *istate) 
80 {
81   /* Valid state? */
82   if (istate) {
83     id (*imp) (id, SEL, id);
84     SEL selector = istate->selector;
85     id object   = istate->object;
86     id argument = istate->argument;
87
88     /* Don't need anymore so free it */
89     objc_free (istate);
90
91     /* Clear out the thread local storage */
92     objc_thread_set_data (NULL);
93
94     /* Check to see if we just became multi threaded */
95     if (! __objc_is_multi_threaded)
96       {
97         __objc_is_multi_threaded = 1;
98
99         /* Call the hook function */
100         if (_objc_became_multi_threaded != NULL)
101           (*_objc_became_multi_threaded) ();
102       }
103
104     /* Call the method */
105     if ((imp = (id (*) (id, SEL, id))objc_msg_lookup (object, selector)))
106         (*imp) (object, selector, argument);
107     else
108       objc_error (object, OBJC_ERR_UNIMPLEMENTED,
109                   "objc_thread_detach called with bad selector.\n");
110   }
111   else
112     objc_error (nil, OBJC_ERR_BAD_STATE,
113                 "objc_thread_detach called with NULL state.\n");
114
115   /* Exit the thread */
116   objc_thread_exit ();
117 }
118
119 /*
120   Frontend functions
121
122   These functions constitute the public interface to the Objective-C thread
123   and mutex functionality.
124   */
125
126 /* Frontend thread functions */
127
128 /*
129   Detach a new thread of execution and return its id.  Returns NULL if fails.
130   Thread is started by sending message with selector to object.  Message
131   takes a single argument.
132   */
133 objc_thread_t
134 objc_thread_detach (SEL selector, id object, id argument)
135 {
136   struct __objc_thread_start_state *istate;
137   objc_thread_t        thread_id = NULL;
138
139   /* Allocate the state structure */
140   if (! (istate = (struct __objc_thread_start_state *)
141          objc_malloc (sizeof (*istate))))
142     return NULL;
143
144   /* Initialize the state structure */
145   istate->selector = selector;
146   istate->object = object;
147   istate->argument = argument;
148
149   /* lock access */
150   objc_mutex_lock (__objc_runtime_mutex);
151
152   /* Call the backend to spawn the thread */
153   if ((thread_id = __objc_thread_detach ((void *)__objc_thread_detach_function,
154                                          istate)) == NULL)
155     {
156       /* failed! */
157       objc_mutex_unlock (__objc_runtime_mutex);
158       objc_free (istate);
159       return NULL;
160     }
161
162   /* Increment our thread counter */
163   __objc_runtime_threads_alive++;
164   objc_mutex_unlock (__objc_runtime_mutex);
165
166   return thread_id;
167 }
168
169 /* Set the current thread's priority. */
170 int
171 objc_thread_set_priority (int priority)
172 {
173   /* Call the backend */
174   return __objc_thread_set_priority (priority);
175 }
176
177 /* Return the current thread's priority. */
178 int
179 objc_thread_get_priority (void)
180 {
181   /* Call the backend */
182   return __objc_thread_get_priority ();
183 }
184
185 /*
186   Yield our process time to another thread.  Any BUSY waiting that is done
187   by a thread should use this function to make sure that other threads can
188   make progress even on a lazy uniprocessor system.
189   */
190 void
191 objc_thread_yield (void)
192 {
193   /* Call the backend */
194   __objc_thread_yield ();
195 }
196
197 /*
198   Terminate the current tread.  Doesn't return.
199   Actually, if it failed returns -1.
200   */
201 int
202 objc_thread_exit (void)
203 {
204   /* Decrement our counter of the number of threads alive */
205   objc_mutex_lock (__objc_runtime_mutex);
206   __objc_runtime_threads_alive--;
207   objc_mutex_unlock (__objc_runtime_mutex);
208
209   /* Call the backend to terminate the thread */
210   return __objc_thread_exit ();
211 }
212
213 /*
214   Returns an integer value which uniquely describes a thread.  Must not be
215   NULL which is reserved as a marker for "no thread".
216   */
217 objc_thread_t
218 objc_thread_id (void)
219 {
220   /* Call the backend */
221   return __objc_thread_id ();
222 }
223
224 /*
225   Sets the thread's local storage pointer. 
226   Returns 0 if successful or -1 if failed.
227   */
228 int
229 objc_thread_set_data (void *value)
230 {
231   /* Call the backend */
232   return __objc_thread_set_data (value);
233 }
234
235 /*
236   Returns the thread's local storage pointer.  Returns NULL on failure.
237   */
238 void *
239 objc_thread_get_data (void)
240 {
241   /* Call the backend */
242   return __objc_thread_get_data ();
243 }
244
245 /* Frontend mutex functions */
246
247 /*
248   Allocate a mutex.  Return the mutex pointer if successful or NULL if the
249   allocation failed for any reason.
250   */
251 objc_mutex_t
252 objc_mutex_allocate (void)
253 {
254   objc_mutex_t mutex;
255
256   /* Allocate the mutex structure */
257   if (! (mutex = (objc_mutex_t)objc_malloc (sizeof (struct objc_mutex))))
258     return NULL;
259
260   /* Call backend to create the mutex */
261   if (__objc_mutex_allocate (mutex))
262     {
263       /* failed! */
264       objc_free (mutex);
265       return NULL;
266     }
267
268   /* Initialize mutex */
269   mutex->owner = NULL;
270   mutex->depth = 0;
271   return mutex;
272 }
273
274 /*
275   Deallocate a mutex.  Note that this includes an implicit mutex_lock to
276   insure that no one else is using the lock.  It is legal to deallocate
277   a lock if we have a lock on it, but illegal to deallocate a lock held
278   by anyone else.
279   Returns the number of locks on the thread.  (1 for deallocate).
280   */
281 int
282 objc_mutex_deallocate (objc_mutex_t mutex)
283 {
284   int depth;
285
286   /* Valid mutex? */
287   if (! mutex)
288     return -1;
289
290   /* Acquire lock on mutex */
291   depth = objc_mutex_lock (mutex);
292
293   /* Call backend to destroy mutex */
294   if (__objc_mutex_deallocate (mutex))
295     return -1;
296
297   /* Free the mutex structure */
298   objc_free (mutex);
299
300   /* Return last depth */
301   return depth;
302 }
303
304 /*
305   Grab a lock on a mutex.  If this thread already has a lock on this mutex
306   then we increment the lock count.  If another thread has a lock on the 
307   mutex we block and wait for the thread to release the lock.
308   Returns the lock count on the mutex held by this thread.
309   */
310 int
311 objc_mutex_lock (objc_mutex_t mutex)
312 {
313   objc_thread_t thread_id;
314   int status;
315
316   /* Valid mutex? */
317   if (! mutex)
318     return -1;
319
320   /* If we already own the lock then increment depth */
321   thread_id = __objc_thread_id ();
322   if (mutex->owner == thread_id)
323     return ++mutex->depth;
324
325   /* Call the backend to lock the mutex */
326   status = __objc_mutex_lock (mutex);
327
328   /* Failed? */
329   if (status)
330     return status;
331
332   /* Successfully locked the thread */
333   mutex->owner = thread_id;
334   return mutex->depth = 1;
335 }
336
337 /*
338   Try to grab a lock on a mutex.  If this thread already has a lock on
339   this mutex then we increment the lock count and return it.  If another
340   thread has a lock on the mutex returns -1.
341   */
342 int
343 objc_mutex_trylock (objc_mutex_t mutex)
344 {
345   objc_thread_t thread_id;
346   int status;
347
348   /* Valid mutex? */
349   if (! mutex)
350     return -1;
351
352   /* If we already own the lock then increment depth */ 
353   thread_id = __objc_thread_id ();
354   if (mutex->owner == thread_id)
355     return ++mutex->depth;
356     
357   /* Call the backend to try to lock the mutex */
358   status = __objc_mutex_trylock (mutex);
359
360   /* Failed? */
361   if (status)
362     return status;
363
364   /* Successfully locked the thread */
365   mutex->owner = thread_id;
366   return mutex->depth = 1;
367 }
368
369 /* 
370   Unlocks the mutex by one level.
371   Decrements the lock count on this mutex by one.
372   If the lock count reaches zero, release the lock on the mutex.
373   Returns the lock count on the mutex.
374   It is an error to attempt to unlock a mutex which this thread 
375   doesn't hold in which case return -1 and the mutex is unaffected.
376   */
377 int
378 objc_mutex_unlock (objc_mutex_t mutex)
379 {
380   objc_thread_t thread_id;
381   int status;
382
383   /* Valid mutex? */
384   if (! mutex)
385     return -1;
386
387   /* If another thread owns the lock then abort */
388   thread_id = __objc_thread_id ();
389   if (mutex->owner != thread_id)
390     return -1;
391
392   /* Decrement depth and return */
393   if (mutex->depth > 1)
394     return --mutex->depth;
395
396   /* Depth down to zero so we are no longer the owner */
397   mutex->depth = 0;
398   mutex->owner = NULL;
399
400   /* Have the backend unlock the mutex */
401   status = __objc_mutex_unlock (mutex);
402
403   /* Failed? */
404   if (status)
405     return status;
406
407   return 0;
408 }
409
410 /* Frontend condition mutex functions */
411
412 /*
413   Allocate a condition.  Return the condition pointer if successful or NULL
414   if the allocation failed for any reason.
415   */
416 objc_condition_t 
417 objc_condition_allocate (void)
418 {
419   objc_condition_t condition;
420     
421   /* Allocate the condition mutex structure */
422   if (! (condition = 
423          (objc_condition_t) objc_malloc (sizeof (struct objc_condition))))
424     return NULL;
425
426   /* Call the backend to create the condition mutex */
427   if (__objc_condition_allocate (condition))
428     {
429       /* failed! */
430       objc_free (condition);
431       return NULL;
432     }
433
434   /* Success! */
435   return condition;
436 }
437
438 /*
439   Deallocate a condition. Note that this includes an implicit 
440   condition_broadcast to insure that waiting threads have the opportunity
441   to wake.  It is legal to dealloc a condition only if no other
442   thread is/will be using it. Here we do NOT check for other threads
443   waiting but just wake them up.
444   */
445 int
446 objc_condition_deallocate (objc_condition_t condition)
447 {
448   /* Broadcast the condition */
449   if (objc_condition_broadcast (condition))
450     return -1;
451
452   /* Call the backend to destroy */
453   if (__objc_condition_deallocate (condition))
454     return -1;
455
456   /* Free the condition mutex structure */
457   objc_free (condition);
458
459   return 0;
460 }
461
462 /*
463   Wait on the condition unlocking the mutex until objc_condition_signal ()
464   or objc_condition_broadcast () are called for the same condition. The
465   given mutex *must* have the depth set to 1 so that it can be unlocked
466   here, so that someone else can lock it and signal/broadcast the condition.
467   The mutex is used to lock access to the shared data that make up the
468   "condition" predicate.
469   */
470 int
471 objc_condition_wait (objc_condition_t condition, objc_mutex_t mutex)
472 {
473   objc_thread_t thread_id;
474
475   /* Valid arguments? */
476   if (! mutex || ! condition)
477     return -1;
478
479   /* Make sure we are owner of mutex */
480   thread_id = __objc_thread_id ();
481   if (mutex->owner != thread_id)
482     return -1;
483
484   /* Cannot be locked more than once */
485   if (mutex->depth > 1)
486     return -1;
487
488   /* Virtually unlock the mutex */
489   mutex->depth = 0;
490   mutex->owner = (objc_thread_t)NULL;
491
492   /* Call the backend to wait */
493   __objc_condition_wait (condition, mutex);
494
495   /* Make ourselves owner of the mutex */
496   mutex->owner = thread_id;
497   mutex->depth = 1;
498
499   return 0;
500 }
501
502 /*
503   Wake up all threads waiting on this condition. It is recommended that 
504   the called would lock the same mutex as the threads in objc_condition_wait
505   before changing the "condition predicate" and make this call and unlock it
506   right away after this call.
507   */
508 int
509 objc_condition_broadcast (objc_condition_t condition)
510 {
511   /* Valid condition mutex? */
512   if (! condition)
513     return -1;
514
515   return __objc_condition_broadcast (condition);
516 }
517
518 /*
519   Wake up one thread waiting on this condition. It is recommended that 
520   the called would lock the same mutex as the threads in objc_condition_wait
521   before changing the "condition predicate" and make this call and unlock it
522   right away after this call.
523   */
524 int
525 objc_condition_signal (objc_condition_t condition)
526 {
527   /* Valid condition mutex? */
528   if (! condition)
529     return -1;
530
531   return __objc_condition_signal (condition);
532 }
533
534 /* Make the objc thread system aware that a thread which is managed
535    (started, stopped) by external code could access objc facilities
536    from now on.  This is used when you are interfacing with some
537    external non-objc-based environment/system - you must call
538    objc_thread_add () before an alien thread makes any calls to
539    Objective-C.  Do not cause the _objc_became_multi_threaded hook to
540    be executed. */
541 void 
542 objc_thread_add (void)
543 {
544   objc_mutex_lock (__objc_runtime_mutex);
545   __objc_is_multi_threaded = 1;
546   __objc_runtime_threads_alive++;
547   objc_mutex_unlock (__objc_runtime_mutex);  
548 }
549
550 /* Make the objc thread system aware that a thread managed (started,
551    stopped) by some external code will no longer access objc and thus
552    can be forgotten by the objc thread system.  Call
553    objc_thread_remove () when your alien thread is done with making
554    calls to Objective-C. */
555 void
556 objc_thread_remove (void)
557 {
558   objc_mutex_lock (__objc_runtime_mutex);
559   __objc_runtime_threads_alive--;
560   objc_mutex_unlock (__objc_runtime_mutex);  
561 }
562
563 /* End of File */