diff options
Diffstat (limited to 'gcc/objc/thr.c')
-rw-r--r-- | gcc/objc/thr.c | 505 |
1 files changed, 443 insertions, 62 deletions
diff --git a/gcc/objc/thr.c b/gcc/objc/thr.c index 2770b7d..f1c957a 100644 --- a/gcc/objc/thr.c +++ b/gcc/objc/thr.c @@ -27,20 +27,15 @@ Boston, MA 02111-1307, USA. */ #include <stdlib.h> #include "runtime.h" -/************************************************************************* - * Universal static variables: - */ -int __objc_thread_exit_status = 0; /* Global exit status. */ +/* 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; -/***************************************************************************** - * Universal Functionality - */ - /* Use this to set the hook function that will be called when the runtime initially becomes multi threaded. @@ -60,94 +55,480 @@ objc_thread_callback objc_set_thread_callback(objc_thread_callback func) return temp; } -/******** - * First function called in a thread, starts everything else. +/* + Private functions + + These functions are utilized by the frontend, but they are not + considered part of the public interface. + */ + +/* + 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; + SEL selector; + id object; + id argument; }; static volatile void __objc_thread_detach_function(struct __objc_thread_start_state *istate) { - if (istate) { /* Is state valid? */ - id (*imp)(id,SEL,id); - SEL selector = istate->selector; - id object = istate->object; - id argument = istate->argument; + /* Valid state? */ + if (istate) { + id (*imp)(id,SEL,id); + SEL selector = istate->selector; + id object = istate->object; + id argument = istate->argument; - objc_free(istate); + /* Don't need anymore so free it */ + objc_free(istate); - /* Clear out the thread local storage */ - objc_thread_set_data(NULL); + /* 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; + /* 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 hook function */ + if (_objc_became_multi_threaded != NULL) + (*_objc_became_multi_threaded)(); + } - if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector))) { - (*imp)(object, selector, argument); - } - else - fprintf(stderr, "__objc_thread_start called with bad selector.\n"); - } - else { - fprintf(stderr, "__objc_thread_start called with NULL state.\n"); - } - objc_thread_exit(); + /* Call the method */ + if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector))) + (*imp)(object, selector, argument); + else + objc_error(object, OBJC_ERR_UNIMPLEMENTED, + "objc_thread_detach called with bad selector.\n"); + } + else + objc_error(nil, OBJC_ERR_BAD_STATE, + "objc_thread_detach called with NULL state.\n"); + + /* Exit the thread */ + objc_thread_exit(); } -/******** - * 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. - */ +/* + Frontend functions + + These functions constitute the public interface to the Objective-C thread + and mutex functionality. + */ + +/* Frontend thread functions */ + +/* + 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; /* Initialial thread state. */ - objc_thread_t thread_id = NULL; /* Detached thread id. */ + 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)))) /* Can we allocate state? */ - return NULL; /* No, abort. */ + objc_malloc(sizeof(*istate)))) + return NULL; - istate->selector = selector; /* Initialize the thread's */ - istate->object = object; /* state structure. */ + /* Initialize the state structure */ + istate->selector = selector; + istate->object = object; istate->argument = argument; - if ((thread_id = objc_thread_create((void *)__objc_thread_detach_function, - istate)) == NULL) { - objc_free(istate); /* Release state if failed. */ - return thread_id; - } + /* lock access */ + objc_mutex_lock(__objc_runtime_mutex); + + /* Call the backend to spawn the thread */ + if ((thread_id = __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; } -#undef objc_mutex_lock() -#undef objc_mutex_unlock() +/* Set the current thread's priority. */ +int +objc_thread_set_priority(int priority) +{ + /* Call the backend */ + return __objc_thread_set_priority(priority); +} +/* Return the current thread's priority. */ int -objc_mutex_unlock_x(objc_mutex_t mutex, const char *f, int l) +objc_thread_get_priority(void) { - printf("%16.16s#%4d < unlock", f, l); - return objc_mutex_unlock(mutex); + /* Call the backend */ + return __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) +{ + /* Call the backend */ + __objc_thread_yield(); +} + +/* + Terminate the current tread. Doesn't return. + Actually, if it failed returns -1. + */ int -objc_mutex_lock_x(objc_mutex_t mutex, const char *f, int l) +objc_thread_exit(void) { - printf("%16.16s#%4d < lock", f, l); - return objc_mutex_lock(mutex); + /* 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 __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) +{ + /* Call the backend */ + return __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) +{ + /* Call the backend */ + return __objc_thread_set_data(value); +} + +/* + Returns the thread's local storage pointer. Returns NULL on failure. + */ +void * +objc_thread_get_data(void) +{ + /* Call the backend */ + return __objc_thread_get_data(); +} + +/* Frontend 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 (__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 (__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 = objc_thread_id(); + if (mutex->owner == thread_id) + return ++mutex->depth; + + /* Call the backend to lock the mutex */ + status = __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 = objc_thread_id(); + if (mutex->owner == thread_id) + return ++mutex->depth; + + /* Call the backend to try to lock the mutex */ + status = __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 = 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 = __objc_mutex_unlock(mutex); + + /* Failed? */ + if (status) + return status; + + return 0; +} + +/* Frontend 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 (__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 (__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 = 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 */ + __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 __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 __objc_condition_signal(condition); } /* End of File */ |