mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			544 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			544 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
/* GNU Objective C Runtime Thread Interface
 | 
						|
   Copyright (C) 1996-2015 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);  
 | 
						|
}
 | 
						|
 |