OSDN Git Service

PR c/39495
[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   /* Make sure compiler detects no return.  */
119   __builtin_trap ();
120 }
121
122 /*
123   Frontend functions
124
125   These functions constitute the public interface to the Objective-C thread
126   and mutex functionality.
127   */
128
129 /* Frontend thread functions */
130
131 /*
132   Detach a new thread of execution and return its id.  Returns NULL if fails.
133   Thread is started by sending message with selector to object.  Message
134   takes a single argument.
135   */
136 objc_thread_t
137 objc_thread_detach (SEL selector, id object, id argument)
138 {
139   struct __objc_thread_start_state *istate;
140   objc_thread_t        thread_id = NULL;
141
142   /* Allocate the state structure */
143   if (! (istate = (struct __objc_thread_start_state *)
144          objc_malloc (sizeof (*istate))))
145     return NULL;
146
147   /* Initialize the state structure */
148   istate->selector = selector;
149   istate->object = object;
150   istate->argument = argument;
151
152   /* lock access */
153   objc_mutex_lock (__objc_runtime_mutex);
154
155   /* Call the backend to spawn the thread */
156   if ((thread_id = __objc_thread_detach ((void *)__objc_thread_detach_function,
157                                          istate)) == NULL)
158     {
159       /* failed! */
160       objc_mutex_unlock (__objc_runtime_mutex);
161       objc_free (istate);
162       return NULL;
163     }
164
165   /* Increment our thread counter */
166   __objc_runtime_threads_alive++;
167   objc_mutex_unlock (__objc_runtime_mutex);
168
169   return thread_id;
170 }
171
172 /* Set the current thread's priority. */
173 int
174 objc_thread_set_priority (int priority)
175 {
176   /* Call the backend */
177   return __objc_thread_set_priority (priority);
178 }
179
180 /* Return the current thread's priority. */
181 int
182 objc_thread_get_priority (void)
183 {
184   /* Call the backend */
185   return __objc_thread_get_priority ();
186 }
187
188 /*
189   Yield our process time to another thread.  Any BUSY waiting that is done
190   by a thread should use this function to make sure that other threads can
191   make progress even on a lazy uniprocessor system.
192   */
193 void
194 objc_thread_yield (void)
195 {
196   /* Call the backend */
197   __objc_thread_yield ();
198 }
199
200 /*
201   Terminate the current tread.  Doesn't return.
202   Actually, if it failed returns -1.
203   */
204 int
205 objc_thread_exit (void)
206 {
207   /* Decrement our counter of the number of threads alive */
208   objc_mutex_lock (__objc_runtime_mutex);
209   __objc_runtime_threads_alive--;
210   objc_mutex_unlock (__objc_runtime_mutex);
211
212   /* Call the backend to terminate the thread */
213   return __objc_thread_exit ();
214 }
215
216 /*
217   Returns an integer value which uniquely describes a thread.  Must not be
218   NULL which is reserved as a marker for "no thread".
219   */
220 objc_thread_t
221 objc_thread_id (void)
222 {
223   /* Call the backend */
224   return __objc_thread_id ();
225 }
226
227 /*
228   Sets the thread's local storage pointer. 
229   Returns 0 if successful or -1 if failed.
230   */
231 int
232 objc_thread_set_data (void *value)
233 {
234   /* Call the backend */
235   return __objc_thread_set_data (value);
236 }
237
238 /*
239   Returns the thread's local storage pointer.  Returns NULL on failure.
240   */
241 void *
242 objc_thread_get_data (void)
243 {
244   /* Call the backend */
245   return __objc_thread_get_data ();
246 }
247
248 /* Frontend mutex functions */
249
250 /*
251   Allocate a mutex.  Return the mutex pointer if successful or NULL if the
252   allocation failed for any reason.
253   */
254 objc_mutex_t
255 objc_mutex_allocate (void)
256 {
257   objc_mutex_t mutex;
258
259   /* Allocate the mutex structure */
260   if (! (mutex = (objc_mutex_t)objc_malloc (sizeof (struct objc_mutex))))
261     return NULL;
262
263   /* Call backend to create the mutex */
264   if (__objc_mutex_allocate (mutex))
265     {
266       /* failed! */
267       objc_free (mutex);
268       return NULL;
269     }
270
271   /* Initialize mutex */
272   mutex->owner = NULL;
273   mutex->depth = 0;
274   return mutex;
275 }
276
277 /*
278   Deallocate a mutex.  Note that this includes an implicit mutex_lock to
279   insure that no one else is using the lock.  It is legal to deallocate
280   a lock if we have a lock on it, but illegal to deallocate a lock held
281   by anyone else.
282   Returns the number of locks on the thread.  (1 for deallocate).
283   */
284 int
285 objc_mutex_deallocate (objc_mutex_t mutex)
286 {
287   int depth;
288
289   /* Valid mutex? */
290   if (! mutex)
291     return -1;
292
293   /* Acquire lock on mutex */
294   depth = objc_mutex_lock (mutex);
295
296   /* Call backend to destroy mutex */
297   if (__objc_mutex_deallocate (mutex))
298     return -1;
299
300   /* Free the mutex structure */
301   objc_free (mutex);
302
303   /* Return last depth */
304   return depth;
305 }
306
307 /*
308   Grab a lock on a mutex.  If this thread already has a lock on this mutex
309   then we increment the lock count.  If another thread has a lock on the 
310   mutex we block and wait for the thread to release the lock.
311   Returns the lock count on the mutex held by this thread.
312   */
313 int
314 objc_mutex_lock (objc_mutex_t mutex)
315 {
316   objc_thread_t thread_id;
317   int status;
318
319   /* Valid mutex? */
320   if (! mutex)
321     return -1;
322
323   /* If we already own the lock then increment depth */
324   thread_id = __objc_thread_id ();
325   if (mutex->owner == thread_id)
326     return ++mutex->depth;
327
328   /* Call the backend to lock the mutex */
329   status = __objc_mutex_lock (mutex);
330
331   /* Failed? */
332   if (status)
333     return status;
334
335   /* Successfully locked the thread */
336   mutex->owner = thread_id;
337   return mutex->depth = 1;
338 }
339
340 /*
341   Try to grab a lock on a mutex.  If this thread already has a lock on
342   this mutex then we increment the lock count and return it.  If another
343   thread has a lock on the mutex returns -1.
344   */
345 int
346 objc_mutex_trylock (objc_mutex_t mutex)
347 {
348   objc_thread_t thread_id;
349   int status;
350
351   /* Valid mutex? */
352   if (! mutex)
353     return -1;
354
355   /* If we already own the lock then increment depth */ 
356   thread_id = __objc_thread_id ();
357   if (mutex->owner == thread_id)
358     return ++mutex->depth;
359     
360   /* Call the backend to try to lock the mutex */
361   status = __objc_mutex_trylock (mutex);
362
363   /* Failed? */
364   if (status)
365     return status;
366
367   /* Successfully locked the thread */
368   mutex->owner = thread_id;
369   return mutex->depth = 1;
370 }
371
372 /* 
373   Unlocks the mutex by one level.
374   Decrements the lock count on this mutex by one.
375   If the lock count reaches zero, release the lock on the mutex.
376   Returns the lock count on the mutex.
377   It is an error to attempt to unlock a mutex which this thread 
378   doesn't hold in which case return -1 and the mutex is unaffected.
379   */
380 int
381 objc_mutex_unlock (objc_mutex_t mutex)
382 {
383   objc_thread_t thread_id;
384   int status;
385
386   /* Valid mutex? */
387   if (! mutex)
388     return -1;
389
390   /* If another thread owns the lock then abort */
391   thread_id = __objc_thread_id ();
392   if (mutex->owner != thread_id)
393     return -1;
394
395   /* Decrement depth and return */
396   if (mutex->depth > 1)
397     return --mutex->depth;
398
399   /* Depth down to zero so we are no longer the owner */
400   mutex->depth = 0;
401   mutex->owner = NULL;
402
403   /* Have the backend unlock the mutex */
404   status = __objc_mutex_unlock (mutex);
405
406   /* Failed? */
407   if (status)
408     return status;
409
410   return 0;
411 }
412
413 /* Frontend condition mutex functions */
414
415 /*
416   Allocate a condition.  Return the condition pointer if successful or NULL
417   if the allocation failed for any reason.
418   */
419 objc_condition_t 
420 objc_condition_allocate (void)
421 {
422   objc_condition_t condition;
423     
424   /* Allocate the condition mutex structure */
425   if (! (condition = 
426          (objc_condition_t) objc_malloc (sizeof (struct objc_condition))))
427     return NULL;
428
429   /* Call the backend to create the condition mutex */
430   if (__objc_condition_allocate (condition))
431     {
432       /* failed! */
433       objc_free (condition);
434       return NULL;
435     }
436
437   /* Success! */
438   return condition;
439 }
440
441 /*
442   Deallocate a condition. Note that this includes an implicit 
443   condition_broadcast to insure that waiting threads have the opportunity
444   to wake.  It is legal to dealloc a condition only if no other
445   thread is/will be using it. Here we do NOT check for other threads
446   waiting but just wake them up.
447   */
448 int
449 objc_condition_deallocate (objc_condition_t condition)
450 {
451   /* Broadcast the condition */
452   if (objc_condition_broadcast (condition))
453     return -1;
454
455   /* Call the backend to destroy */
456   if (__objc_condition_deallocate (condition))
457     return -1;
458
459   /* Free the condition mutex structure */
460   objc_free (condition);
461
462   return 0;
463 }
464
465 /*
466   Wait on the condition unlocking the mutex until objc_condition_signal ()
467   or objc_condition_broadcast () are called for the same condition. The
468   given mutex *must* have the depth set to 1 so that it can be unlocked
469   here, so that someone else can lock it and signal/broadcast the condition.
470   The mutex is used to lock access to the shared data that make up the
471   "condition" predicate.
472   */
473 int
474 objc_condition_wait (objc_condition_t condition, objc_mutex_t mutex)
475 {
476   objc_thread_t thread_id;
477
478   /* Valid arguments? */
479   if (! mutex || ! condition)
480     return -1;
481
482   /* Make sure we are owner of mutex */
483   thread_id = __objc_thread_id ();
484   if (mutex->owner != thread_id)
485     return -1;
486
487   /* Cannot be locked more than once */
488   if (mutex->depth > 1)
489     return -1;
490
491   /* Virtually unlock the mutex */
492   mutex->depth = 0;
493   mutex->owner = (objc_thread_t)NULL;
494
495   /* Call the backend to wait */
496   __objc_condition_wait (condition, mutex);
497
498   /* Make ourselves owner of the mutex */
499   mutex->owner = thread_id;
500   mutex->depth = 1;
501
502   return 0;
503 }
504
505 /*
506   Wake up all threads waiting on this condition. It is recommended that 
507   the called would lock the same mutex as the threads in objc_condition_wait
508   before changing the "condition predicate" and make this call and unlock it
509   right away after this call.
510   */
511 int
512 objc_condition_broadcast (objc_condition_t condition)
513 {
514   /* Valid condition mutex? */
515   if (! condition)
516     return -1;
517
518   return __objc_condition_broadcast (condition);
519 }
520
521 /*
522   Wake up one thread waiting on this condition. It is recommended that 
523   the called would lock the same mutex as the threads in objc_condition_wait
524   before changing the "condition predicate" and make this call and unlock it
525   right away after this call.
526   */
527 int
528 objc_condition_signal (objc_condition_t condition)
529 {
530   /* Valid condition mutex? */
531   if (! condition)
532     return -1;
533
534   return __objc_condition_signal (condition);
535 }
536
537 /* Make the objc thread system aware that a thread which is managed
538    (started, stopped) by external code could access objc facilities
539    from now on.  This is used when you are interfacing with some
540    external non-objc-based environment/system - you must call
541    objc_thread_add () before an alien thread makes any calls to
542    Objective-C.  Do not cause the _objc_became_multi_threaded hook to
543    be executed. */
544 void 
545 objc_thread_add (void)
546 {
547   objc_mutex_lock (__objc_runtime_mutex);
548   __objc_is_multi_threaded = 1;
549   __objc_runtime_threads_alive++;
550   objc_mutex_unlock (__objc_runtime_mutex);  
551 }
552
553 /* Make the objc thread system aware that a thread managed (started,
554    stopped) by some external code will no longer access objc and thus
555    can be forgotten by the objc thread system.  Call
556    objc_thread_remove () when your alien thread is done with making
557    calls to Objective-C. */
558 void
559 objc_thread_remove (void)
560 {
561   objc_mutex_lock (__objc_runtime_mutex);
562   __objc_runtime_threads_alive--;
563   objc_mutex_unlock (__objc_runtime_mutex);  
564 }
565
566 /* End of File */