diff options
Diffstat (limited to 'libobjc/thr.c')
-rw-r--r-- | libobjc/thr.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/libobjc/thr.c b/libobjc/thr.c new file mode 100644 index 000000000..3f7e5c525 --- /dev/null +++ b/libobjc/thr.c @@ -0,0 +1,549 @@ +/* GNU Objective C Runtime Thread Interface + Copyright (C) 1996, 1997, 2009, 2010 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 +/* The line below is needed for declarations of functions such as + pthread_mutexattr_settype, without which gthr-posix.h may fail to + compile within libobjc. Unfortunately, this breaks compilation on + Tru64 UNIX V4.0F, so disable it there. */ +#ifndef __osf__ +#define _XOPEN_SOURCE 500 +#endif +#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/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); +} + |