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