123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- /* GNU Objective C Runtime Thread Interface
- Copyright (C) 1996-2015 Free Software Foundation, Inc.
- Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
- GCC is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- details.
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
- #include "objc-private/common.h"
- #include "objc-private/error.h"
- #define _LIBOBJC
- #include "config.h"
- #include "tconfig.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "defaults.h"
- #include "objc/thr.h"
- #include "objc/message.h" /* For objc_msg_lookup(). */
- #include "objc/runtime.h"
- #include "objc-private/module-abi-8.h"
- #include "objc-private/runtime.h"
- #include <gthr.h>
- #include <stdlib.h>
- /* Global exit status. */
- int __objc_thread_exit_status = 0;
- /* Flag which lets us know if we ever became multi threaded. */
- int __objc_is_multi_threaded = 0;
- /* The hook function called when the runtime becomes multi
- threaded. */
- objc_thread_callback _objc_became_multi_threaded = NULL;
- /* Use this to set the hook function that will be called when the
- runtime initially becomes multi threaded. The hook function is
- only called once, meaning only when the 2nd thread is spawned, not
- for each and every thread.
- It returns the previous hook function or NULL if there is none.
- A program outside of the runtime could set this to some function so
- it can be informed; for example, the GNUstep Base Library sets it
- so it can implement the NSBecomingMultiThreaded notification. */
- objc_thread_callback objc_set_thread_callback (objc_thread_callback func)
- {
- objc_thread_callback temp = _objc_became_multi_threaded;
- _objc_became_multi_threaded = func;
- return temp;
- }
- /* Private functions.
-
- These functions are utilized by the runtime, but they are not
- considered part of the public interface. */
- /* Initialize the threads subsystem. */
- int
- __objc_init_thread_system(void)
- {
- return __gthread_objc_init_thread_system ();
- }
- /* First function called in a thread, starts everything else.
- This function is passed to the backend by objc_thread_detach as the
- starting function for a new thread. */
- struct __objc_thread_start_state
- {
- SEL selector;
- id object;
- id argument;
- };
- static void __attribute__((noreturn))
- __objc_thread_detach_function (struct __objc_thread_start_state *istate)
- {
- /* Valid state? */
- if (istate)
- {
- id (*imp) (id, SEL, id);
- SEL selector = istate->selector;
- id object = istate->object;
- id argument = istate->argument;
-
- /* Don't need anymore so free it. */
- objc_free (istate);
- /* Clear out the thread local storage. */
- objc_thread_set_data (NULL);
-
- /* Check to see if we just became multi threaded. */
- if (! __objc_is_multi_threaded)
- {
- __objc_is_multi_threaded = 1;
-
- /* Call the hook function. */
- if (_objc_became_multi_threaded != NULL)
- (*_objc_became_multi_threaded) ();
- }
-
- /* Call the method. */
- if ((imp = (id (*) (id, SEL, id))objc_msg_lookup (object, selector)))
- (*imp) (object, selector, argument);
- else
- {
- /* FIXME: Should we abort here ? */
- _objc_abort ("objc_thread_detach called with bad selector.\n");
- }
- }
- else
- {
- /* FIXME: Should we abort here ? */
- _objc_abort ("objc_thread_detach called with NULL state.\n");
- }
-
- /* Exit the thread. */
- objc_thread_exit ();
-
- /* Make sure compiler detects no return. */
- __builtin_trap ();
- }
- /* Public functions.
- These functions constitute the public interface to the Objective-C
- thread and mutex functionality. */
- /* Detach a new thread of execution and return its id. Returns NULL
- if fails. Thread is started by sending message with selector to
- object. Message takes a single argument. */
- objc_thread_t
- objc_thread_detach (SEL selector, id object, id argument)
- {
- struct __objc_thread_start_state *istate;
- objc_thread_t thread_id = NULL;
- /* Allocate the state structure. */
- if (!(istate = (struct __objc_thread_start_state *)objc_malloc
- (sizeof (*istate))))
- return NULL;
-
- /* Initialize the state structure. */
- istate->selector = selector;
- istate->object = object;
- istate->argument = argument;
- /* Lock access. */
- objc_mutex_lock (__objc_runtime_mutex);
- /* Call the backend to spawn the thread. */
- if ((thread_id = __gthread_objc_thread_detach ((void *)__objc_thread_detach_function,
- istate)) == NULL)
- {
- /* Failed! */
- objc_mutex_unlock (__objc_runtime_mutex);
- objc_free (istate);
- return NULL;
- }
- /* Increment our thread counter. */
- __objc_runtime_threads_alive++;
- objc_mutex_unlock (__objc_runtime_mutex);
- return thread_id;
- }
- /* Set the current thread's priority. */
- int
- objc_thread_set_priority (int priority)
- {
- return __gthread_objc_thread_set_priority (priority);
- }
- /* Return the current thread's priority. */
- int
- objc_thread_get_priority (void)
- {
- return __gthread_objc_thread_get_priority ();
- }
- /* Yield our process time to another thread. Any BUSY waiting that is
- done by a thread should use this function to make sure that other
- threads can make progress even on a lazy uniprocessor system. */
- void
- objc_thread_yield (void)
- {
- __gthread_objc_thread_yield ();
- }
- /* Terminate the current tread. Doesn't return. Actually, if it
- failed returns -1. */
- int
- objc_thread_exit (void)
- {
- /* Decrement our counter of the number of threads alive. */
- objc_mutex_lock (__objc_runtime_mutex);
- __objc_runtime_threads_alive--;
- objc_mutex_unlock (__objc_runtime_mutex);
- /* Call the backend to terminate the thread. */
- return __gthread_objc_thread_exit ();
- }
- /* Returns an integer value which uniquely describes a thread. Must
- not be NULL which is reserved as a marker for "no thread". */
- objc_thread_t
- objc_thread_id (void)
- {
- return __gthread_objc_thread_id ();
- }
- /* Sets the thread's local storage pointer. Returns 0 if successful
- or -1 if failed. */
- int
- objc_thread_set_data (void *value)
- {
- return __gthread_objc_thread_set_data (value);
- }
- /* Returns the thread's local storage pointer. Returns NULL on
- failure. */
- void *
- objc_thread_get_data (void)
- {
- return __gthread_objc_thread_get_data ();
- }
- /* Public mutex functions */
- /* Allocate a mutex. Return the mutex pointer if successful or NULL
- if the allocation failed for any reason. */
- objc_mutex_t
- objc_mutex_allocate (void)
- {
- objc_mutex_t mutex;
- /* Allocate the mutex structure. */
- if (! (mutex = (objc_mutex_t)objc_malloc (sizeof (struct objc_mutex))))
- return NULL;
- /* Call backend to create the mutex. */
- if (__gthread_objc_mutex_allocate (mutex))
- {
- /* Failed! */
- objc_free (mutex);
- return NULL;
- }
- /* Initialize mutex. */
- mutex->owner = NULL;
- mutex->depth = 0;
- return mutex;
- }
- /* Deallocate a mutex. Note that this includes an implicit mutex_lock
- to insure that no one else is using the lock. It is legal to
- deallocate a lock if we have a lock on it, but illegal to
- deallocate a lock held by anyone else. Returns the number of locks
- on the thread. (1 for deallocate). */
- int
- objc_mutex_deallocate (objc_mutex_t mutex)
- {
- int depth;
- /* Valid mutex? */
- if (! mutex)
- return -1;
- /* Acquire lock on mutex. */
- depth = objc_mutex_lock (mutex);
- /* Call backend to destroy mutex. */
- if (__gthread_objc_mutex_deallocate (mutex))
- return -1;
- /* Free the mutex structure. */
- objc_free (mutex);
- /* Return last depth. */
- return depth;
- }
- /* Grab a lock on a mutex. If this thread already has a lock on this
- mutex then we increment the lock count. If another thread has a
- lock on the mutex we block and wait for the thread to release the
- lock. Returns the lock count on the mutex held by this thread. */
- int
- objc_mutex_lock (objc_mutex_t mutex)
- {
- objc_thread_t thread_id;
- int status;
- /* Valid mutex? */
- if (! mutex)
- return -1;
- /* If we already own the lock then increment depth. */
- thread_id = __gthread_objc_thread_id ();
- if (mutex->owner == thread_id)
- return ++mutex->depth;
- /* Call the backend to lock the mutex. */
- status = __gthread_objc_mutex_lock (mutex);
- /* Failed? */
- if (status)
- return status;
- /* Successfully locked the thread. */
- mutex->owner = thread_id;
- return mutex->depth = 1;
- }
- /* Try to grab a lock on a mutex. If this thread already has a lock
- on this mutex then we increment the lock count and return it. If
- another thread has a lock on the mutex returns -1. */
- int
- objc_mutex_trylock (objc_mutex_t mutex)
- {
- objc_thread_t thread_id;
- int status;
- /* Valid mutex? */
- if (! mutex)
- return -1;
- /* If we already own the lock then increment depth. */
- thread_id = __gthread_objc_thread_id ();
- if (mutex->owner == thread_id)
- return ++mutex->depth;
-
- /* Call the backend to try to lock the mutex. */
- status = __gthread_objc_mutex_trylock (mutex);
- /* Failed? */
- if (status)
- return status;
- /* Successfully locked the thread. */
- mutex->owner = thread_id;
- return mutex->depth = 1;
- }
- /* Unlocks the mutex by one level. Decrements the lock count on this
- mutex by one. If the lock count reaches zero, release the lock on
- the mutex. Returns the lock count on the mutex. It is an error to
- attempt to unlock a mutex which this thread doesn't hold in which
- case return -1 and the mutex is unaffected. */
- int
- objc_mutex_unlock (objc_mutex_t mutex)
- {
- objc_thread_t thread_id;
- int status;
- /* Valid mutex? */
- if (! mutex)
- return -1;
- /* If another thread owns the lock then abort. */
- thread_id = __gthread_objc_thread_id ();
- if (mutex->owner != thread_id)
- return -1;
- /* Decrement depth and return. */
- if (mutex->depth > 1)
- return --mutex->depth;
- /* Depth down to zero so we are no longer the owner. */
- mutex->depth = 0;
- mutex->owner = NULL;
- /* Have the backend unlock the mutex. */
- status = __gthread_objc_mutex_unlock (mutex);
- /* Failed? */
- if (status)
- return status;
- return 0;
- }
- /* Public condition mutex functions */
- /* Allocate a condition. Return the condition pointer if successful
- or NULL if the allocation failed for any reason. */
- objc_condition_t
- objc_condition_allocate (void)
- {
- objc_condition_t condition;
-
- /* Allocate the condition mutex structure. */
- if (! (condition =
- (objc_condition_t) objc_malloc (sizeof (struct objc_condition))))
- return NULL;
- /* Call the backend to create the condition mutex. */
- if (__gthread_objc_condition_allocate (condition))
- {
- /* Failed! */
- objc_free (condition);
- return NULL;
- }
- /* Success! */
- return condition;
- }
- /* Deallocate a condition. Note that this includes an implicit
- condition_broadcast to insure that waiting threads have the
- opportunity to wake. It is legal to dealloc a condition only if no
- other thread is/will be using it. Here we do NOT check for other
- threads waiting but just wake them up. */
- int
- objc_condition_deallocate (objc_condition_t condition)
- {
- /* Broadcast the condition. */
- if (objc_condition_broadcast (condition))
- return -1;
- /* Call the backend to destroy. */
- if (__gthread_objc_condition_deallocate (condition))
- return -1;
- /* Free the condition mutex structure. */
- objc_free (condition);
- return 0;
- }
- /* Wait on the condition unlocking the mutex until
- objc_condition_signal () or objc_condition_broadcast () are called
- for the same condition. The given mutex *must* have the depth set
- to 1 so that it can be unlocked here, so that someone else can lock
- it and signal/broadcast the condition. The mutex is used to lock
- access to the shared data that make up the "condition"
- predicate. */
- int
- objc_condition_wait (objc_condition_t condition, objc_mutex_t mutex)
- {
- objc_thread_t thread_id;
- /* Valid arguments? */
- if (! mutex || ! condition)
- return -1;
- /* Make sure we are owner of mutex. */
- thread_id = __gthread_objc_thread_id ();
- if (mutex->owner != thread_id)
- return -1;
- /* Cannot be locked more than once. */
- if (mutex->depth > 1)
- return -1;
- /* Virtually unlock the mutex. */
- mutex->depth = 0;
- mutex->owner = (objc_thread_t)NULL;
- /* Call the backend to wait. */
- __gthread_objc_condition_wait (condition, mutex);
- /* Make ourselves owner of the mutex. */
- mutex->owner = thread_id;
- mutex->depth = 1;
- return 0;
- }
- /* Wake up all threads waiting on this condition. It is recommended
- that the called would lock the same mutex as the threads in
- objc_condition_wait before changing the "condition predicate" and
- make this call and unlock it right away after this call. */
- int
- objc_condition_broadcast (objc_condition_t condition)
- {
- /* Valid condition mutex? */
- if (! condition)
- return -1;
- return __gthread_objc_condition_broadcast (condition);
- }
- /* Wake up one thread waiting on this condition. It is recommended
- that the called would lock the same mutex as the threads in
- objc_condition_wait before changing the "condition predicate" and
- make this call and unlock it right away after this call. */
- int
- objc_condition_signal (objc_condition_t condition)
- {
- /* Valid condition mutex? */
- if (! condition)
- return -1;
- return __gthread_objc_condition_signal (condition);
- }
- /* Make the objc thread system aware that a thread which is managed
- (started, stopped) by external code could access objc facilities
- from now on. This is used when you are interfacing with some
- external non-objc-based environment/system - you must call
- objc_thread_add () before an alien thread makes any calls to
- Objective-C. Do not cause the _objc_became_multi_threaded hook to
- be executed. */
- void
- objc_thread_add (void)
- {
- objc_mutex_lock (__objc_runtime_mutex);
- __objc_is_multi_threaded = 1;
- __objc_runtime_threads_alive++;
- objc_mutex_unlock (__objc_runtime_mutex);
- }
- /* Make the objc thread system aware that a thread managed (started,
- stopped) by some external code will no longer access objc and thus
- can be forgotten by the objc thread system. Call
- objc_thread_remove () when your alien thread is done with making
- calls to Objective-C. */
- void
- objc_thread_remove (void)
- {
- objc_mutex_lock (__objc_runtime_mutex);
- __objc_runtime_threads_alive--;
- objc_mutex_unlock (__objc_runtime_mutex);
- }
|