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