File:  [Apple Darwin 0.x] / objc / except.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 19:13:57 2018 UTC (8 years, 2 months ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03, Darwin01
Darwin 0.1 In-kernel Objective-C runtime

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
#ifdef SHLIB
#include "shlib.h"
#endif
/*
    except.c

    This file implements the exception raising scheme.
	It is thread safe, although Alt nodes may not behave
	as expected.

    Copyright (c) 1988, 1991 NeXT, Inc. as an unpublished work.
    All rights reserved.
*/

#ifdef KERNEL
#import <mach/mach_types.h>
#import <kernserv/prototypes.h>
#else /* KERNEL */
#import <mach/cthreads.h>
#import <stdio.h>
#endif /* KERNEL */

#import "error.h"
#ifndef	KERNEL
#import <stdlib.h>
#endif

extern void _NXLogError (const char *format, ...);

#ifdef KERNEL

#import <mach/machine/simple_lock.h>

#ifdef hppa
#define SIMPLE_LOCK_INITIALIZER { 1, 1, 1, 1 }
#else
#define SIMPLE_LOCK_INITIALIZER { 1 }
#endif

#define LOCK_T			simple_lock_data_t
#define LOCK_INITIALIZER SIMPLE_LOCK_INITIALIZER
#define LOCK(x)			simple_lock(&(x))
#define UNLOCK(x)		simple_unlock(&(x))

#define cthread_t	thread_t
#define cthread_self()	current_thread()

#else  /* KERNEL */

#define	LOCK_T				struct mutex
#define LOCK_INITIALIZER	MUTEX_INITIALIZER
#define LOCK(x)				mutex_lock(&x)
#define UNLOCK(x) 			mutex_unlock(&x)

#endif /* KERNEL */

#ifdef KERNEL
//int _setjmp(jmp_buf env) { return setjmp(env); }
//void _longjmp(jmp_buf env, int val) { longjmp(env, val); }
#define _setjmp(env) { return setjmp(env); }
#define _longjmp(env, val) { longjmp(env, val); }

NXUncaughtExceptionHandler *_NXUncaughtExceptionHandler;

#endif /* KERNEL */

typedef void AltProc(void *context, int code, const void *data1, const void *data2);

/*  These nodes represent handlers that are called as normal procedures
    instead of longjmp'ed to.  When these procs return, the next handler
    in the chain is processed.
 */
typedef struct {		/* an alternative node in the handler chain */
    struct _NXHandler *next;		/* ptr to next handler */
    AltProc *proc;			/* proc to call */
    void *context;			/* blind data for client */
} AltHandler;

static int ErrorBufferSize = 0;
static NXExceptionRaiser *ExceptionRaiser = &NXDefaultExceptionRaiser;

 /* Multiple thread support.
	Basic strategy is to collect globals into a struct; singly link them
	(probably not worth it put them in a hashtable) using the cthread id
	as a key.  Upon use, look up proper stack of handlers...
 */

typedef struct xxx {
	NXHandler *handlerStack;	/* start of handler chain */
	AltHandler *altHandlers;
	int altHandlersAlloced;
	int altHandlersUsed;
	cthread_t	thread;			/* key */
	struct xxx	*next;			/* link */
} ExceptionHandlerStack;

/* Allocate the handler stack for the first thread statically so that
   we don't keep an extra page in the heap hot (the libsys data is
   already hot).  Also statically allocate a small number of altHandlers.
   This should be the maximum typical depth of lockFocus'es. */

static AltHandler BaseAltHandlers[16] = { 0 };

static ExceptionHandlerStack Base =
{
  NULL,
  BaseAltHandlers,
  sizeof (BaseAltHandlers) / sizeof (BaseAltHandlers[0]),
  0,
  0,
  NULL
};

static LOCK_T Lock = LOCK_INITIALIZER;

#ifndef	KERNEL
static void _NXClearExceptionStack (void);
extern void _set_cthread_free_callout (void (*) (void));
#endif	KERNEL

static ExceptionHandlerStack *addme (cthread_t self)
{
  ExceptionHandlerStack *stack;
  
  LOCK (Lock);			// lookup is thread safe; addition isn't
  
#ifdef	KERNEL
  for (stack = &Base; stack; stack = stack->next)
    if (stack->thread == 0) {
      stack->thread = self;
      UNLOCK (Lock);
      return stack;
    }
#else	KERNEL
  if (Base.thread == 0)		// try statically allocated stack
    {
      Base.thread = self;
      UNLOCK (Lock);
      return &Base;
    }
  
  /* Pass exception handler cleanup routine to cthread package.  */
  _set_cthread_free_callout (&_NXClearExceptionStack);
#endif	KERNEL
  
  stack = calloc (sizeof (ExceptionHandlerStack), 1);
  stack->thread = self;
  stack->next = Base.next;
  Base.next = stack;		// insert atomically
  
  UNLOCK (Lock);
  
  return stack;
}


/*
	Get callers thread & find | allocate a stack.  Allocation is only
	proper for some usages, but we don't check for them...
	Also, these structs are not recovered upon thread death. XXX
*/
static inline ExceptionHandlerStack *findme (void)
{
  ExceptionHandlerStack *stack;
  cthread_t self = cthread_self ();
  
  for (stack = &Base; stack; stack = stack->next)
    if (stack->thread == self)
      return stack;
  
  return addme (self);
}

#ifdef	KERNEL
/*
 * Call back from thread_deallocate()
 * to indicate that a thread is being
 * destroyed.  Kernel thread ids aren't
 * reused the way that cthread ids are,
 * so we have to nuke the value in the
 * structure.
 *
 * XXX Should free data structures here.
 */
void
_threadFreeExceptionStack(
    thread_t		thread
)
{
  ExceptionHandlerStack *stack;

  // Don't allocate a stack for this thread if it doesn't already have one.
  for (stack = &Base; stack; stack = stack->next)
    if (stack->thread == thread)
      break;

  if (stack)
    {
      stack->handlerStack = 0;    	 // reset the handler chain
      stack->altHandlersUsed = 0; 	 // reset the alt handler count
      stack->thread = 0;
    }
}
#else	KERNEL		
/* cthreads will reuse a stack and a cthread_id.
 * We provide this call-in to clean up matters
 */
static void _NXClearExceptionStack (void)
{
  ExceptionHandlerStack *stack;
  cthread_t self = cthread_self ();
  
  // Don't allocate a stack for this thread if it doesn't already have one.
  for (stack = &Base; stack; stack = stack->next)
    if (stack->thread == self)
      break;

  if (stack)
    {
      stack->handlerStack = 0;    	 // reset the handler chain
      stack->altHandlersUsed = 0; 	 // reset the alt handler count
    }
}
#endif	KERNEL

#define IS_ALT(ptr)			((int)(ptr) % 2)
#define NEXT_HANDLER(ptr)		\
	(IS_ALT(ptr) ? ALT_CODE_TO_PTR(ptr)->next : ((NXHandler *)ptr)->next)
#define ALT_PTR_TO_CODE(ptr)		(((ptr) - me->altHandlers) * 2 + 1)
#define ALT_CODE_TO_PTR(code)		(me->altHandlers + ((int)(code) - 1) / 2)


/* if the node passed in isnt on top of the stack, something's fishy */

static void trickyRemoveHandler (NXHandler *handler, int removingAlt)
{
  AltHandler *altNode;
  NXHandler *node;
  NXHandler **nodePtr;
  ExceptionHandlerStack *me = findme ();
  
  /* try to find the node anywhere on the stack */
  node = me->handlerStack;
  while (node != handler && node)
    {
      /* Watch for attempts to remove handlers which are outside the
	 active portion of the stack.  This happens when you return
	 from an NX_DURING context without removing the handler.
	 This code assumes the stack grows downward. */
#if hppa
	/* stack grows upward */
      if (IS_ALT (node) || (void *) node < (void *) &altNode)
	node = NEXT_HANDLER (node);
      else
#else
      if (IS_ALT (node) || (void *) node > (void *) &altNode)
	node = NEXT_HANDLER (node);
      else
#endif
	{
	  _NXLogError ("Exception handlers were not properly removed.");
	  abort ();
	}
    }
  
  if (node)
    {
      /* 
      * Clean off the stack up to the out of place node.  If we are trying
      * to remove an non-alt handler, we pop eveything off the stack
      * including that handler, calling any alt procs along the way.  If
      * we are removing an alt handler, we leave all the non-alt handlers
      * alone as we clean off the stack, but pop off all the alt handlers
      * we find, including the node we were asked to remove.
      */
      if (!removingAlt)
	_NXLogError ("Exception handlers were not properly removed.");
      
      nodePtr = &me->handlerStack;
      do
	{
	  node = *nodePtr;
	  if (IS_ALT(node))
	    {
	      altNode = ALT_CODE_TO_PTR(node);
	      if (removingAlt)
	        {
		  if (node == handler)
		    *nodePtr = altNode->next;	/* del matching node */
		  else
		    nodePtr = &altNode->next;	/* skip node */
	        }
	      else
	        {
		  if (node != handler)
		    (*altNode->proc)(altNode->context, 1, 0, 0);
		  me->altHandlersUsed = altNode - me->altHandlers;
		  *nodePtr = altNode->next;	/* del any alt node */
	        }
	    }
	  else
	    {
	      if (removingAlt)
		nodePtr = &node->next;		/* skip node */
	      else
		*nodePtr = node->next;		/* nuke non-alt node */
	    }
	}
      while (node != handler);
    }
  else
    {
#ifdef KERNEL
      ;
#else /* KERNEL */
      _NXLogError ("Attempt to remove unrecognized exception handler.");
#endif /* KERNEL */
    }
}


NXHandler *_NXAddAltHandler (AltProc *proc, void *context)
{
  AltHandler *new;
  ExceptionHandlerStack *me = findme ();

  if (me->altHandlersUsed == me->altHandlersAlloced)
    {
      if (me->altHandlers == BaseAltHandlers)
	{
	  me->altHandlers = malloc (++me->altHandlersAlloced *
				    sizeof(AltHandler));
	  bcopy (BaseAltHandlers, me->altHandlers, sizeof (BaseAltHandlers));
	}
      else
	me->altHandlers = realloc (me->altHandlers, ++me->altHandlersAlloced *
						    sizeof(AltHandler));
    }
  
  new = me->altHandlers + me->altHandlersUsed++;
  new->next = me->handlerStack;
  me->handlerStack = (NXHandler *) ALT_PTR_TO_CODE (new);
  new->proc = proc;
  new->context = context;
  return me->handlerStack;
}


void _NXRemoveAltHandler (NXHandler *handler)
{
  ExceptionHandlerStack *me;
  
  for (me = &Base; me; me = me->next)
    if (me->handlerStack == handler)
      {
	AltHandler *altNode = ALT_CODE_TO_PTR (handler);
	
	me->altHandlersUsed = altNode - me->altHandlers;
	me->handlerStack = altNode->next;
	return;
      }
  
  trickyRemoveHandler (handler, TRUE);
}


void _NXAddHandler (NXHandler *handler)
{
  ExceptionHandlerStack *me = findme ();
  
  handler->next = me->handlerStack;
  me->handlerStack = handler;
  handler->code = 0;
}


void _NXRemoveHandler (NXHandler *handler)
{
  ExceptionHandlerStack *me;
  
  for (me = &Base; me; me = me->next)
    if (me->handlerStack == handler)
      {
        me->handlerStack = me->handlerStack->next;
	return;
      }
  
  trickyRemoveHandler (handler, FALSE);
}


void NXSetExceptionRaiser(NXExceptionRaiser *proc)
{
    ExceptionRaiser = proc;
}


NXExceptionRaiser *NXGetExceptionRaiser (void)
{
    return ExceptionRaiser;
}

volatile void _NXRaiseError(int code, const void *data1, const void *data2)
{
    (*ExceptionRaiser)(code, data1, data2);
    abort ();	/* we should never get here! */
}


/* forwards the error to the next handler */
volatile void NXDefaultExceptionRaiser(int code, const void *data1, const void *data2)
{
    NXHandler *destination;
    AltHandler *altDest;
	ExceptionHandlerStack *me = findme ();

    while (1) {
		destination = me->handlerStack;
		if (!destination) {
			if (_NXUncaughtExceptionHandler)
				(*_NXUncaughtExceptionHandler)(code, data1, data2);
			else {
#ifndef KERNEL
					_NXLogError("Uncaught exception #%d\n", code);
#endif /* not KERNEL */
			}
#ifdef KERNEL
				panic("Uncaught exception");
#else /* KERNEL */
				exit(-1);
#endif /* KERNEL */
		} else if (IS_ALT(destination)) {
			altDest = ALT_CODE_TO_PTR(destination);
			me->handlerStack = altDest->next;
			me->altHandlersUsed = altDest - me->altHandlers;
			(*altDest->proc)(altDest->context, code, data1, data2);
		} else {
			destination->code = code;
			destination->data1 = data1;
			destination->data2 = data2;
			me->handlerStack = destination->next;
			_longjmp(destination->jumpState, 1);
		}
    }
}


static char *ErrorBuffer = NULL;
static int ErrorBufferOffset = 0;

/* stack allocates some space from the error buffer */
void NXAllocErrorData(int size, void **data)
{
    int goalSize;
	static LOCK_T mylock = LOCK_INITIALIZER;
	
	LOCK (mylock);
    goalSize = (ErrorBufferOffset + size + 7) & ~7;
    if (goalSize > ErrorBufferSize) {
		ErrorBuffer = realloc(ErrorBuffer, (unsigned)goalSize);
		ErrorBufferSize = goalSize;
    }
    *data = ErrorBuffer + ErrorBufferOffset;
    ErrorBufferOffset = goalSize;
	UNLOCK (mylock);
}


void NXResetErrorData(void)
{
    ErrorBufferOffset = 0;		// multiple threads users must cooperate XXX
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.