| /* GNU Objective C Runtime Thread Interface |
| Copyright (C) 1996, 1997 Free Software Foundation, Inc. |
| Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) |
| |
| This file is part of GNU CC. |
| |
| GNU CC 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 2, or (at your option) any later version. |
| |
| GNU CC 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. |
| |
| You should have received a copy of the GNU General Public License along with |
| GNU CC; see the file COPYING. If not, write to the Free Software |
| Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| /* As a special exception, if you link this library with files compiled with |
| GCC to produce an executable, this does not cause the resulting executable |
| to be covered by the GNU General Public License. This exception does not |
| however invalidate any other reasons why the executable file might be |
| covered by the GNU General Public License. */ |
| |
| #include <stdlib.h> |
| #include "runtime.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 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; |
| }; |
| |
| static volatile void |
| __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 |
| 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(); |
| } |
| |
| /* |
| 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; |
| 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 = __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) |
| { |
| /* Call the backend */ |
| return __objc_thread_set_priority(priority); |
| } |
| |
| /* Return the current thread's priority. */ |
| int |
| objc_thread_get_priority(void) |
| { |
| /* 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_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 __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 */ |