summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/gcc/objc/thr.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/gcc/objc/thr.c')
-rw-r--r--gnu/usr.bin/gcc/objc/thr.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/gnu/usr.bin/gcc/objc/thr.c b/gnu/usr.bin/gcc/objc/thr.c
new file mode 100644
index 00000000000..f1c957aaa15
--- /dev/null
+++ b/gnu/usr.bin/gcc/objc/thr.c
@@ -0,0 +1,534 @@
+/* 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 */