|  | /* GNU Objective C Runtime Thread Interface | 
|  | Copyright (C) 1996-2019 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); | 
|  | } | 
|  |  |