Logo Search packages:      
Sourcecode: u++ version File versions

uEHM.cc

//                              -*- Mode: C++ -*- 
// 
// uC++ Version 5.0.1, Copyright (C) Russell Mok 1997
// 
// uEHM.cc -- 
// 
// Author           : Russell Mok
// Created On       : Sun Jun 29 00:15:09 1997
// Last Modified By : Peter A. Buhr
// Last Modified On : Tue Aug 31 22:37:28 2004
// Update Count     : 364
//
// This  library is free  software; you  can redistribute  it and/or  modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software  Foundation; either  version 2.1 of  the License, or  (at your
// option) any later version.
// 
// This library 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 Lesser General Public License
// for more details.
// 
// You should  have received a  copy of the  GNU Lesser General  Public License
// along  with this library.
// 


#define __U_KERNEL__
#include <uC++.h>
//#include <uDebug.h>

#include <cstring>                              // strlen, strncpy, strcpy

#if __GNUC__ == 3 && __GNUC_MINOR__ < 4               // TEMPORARY: bug in uncaught_exception, correction copied from gcc 3.4
#include "unwind-cxx.h"                         // TEMPORARY: include file copied from gcc 3.4 (remove)
// Copyright (C) 2001 Free Software Foundation, Inc.
namespace __cxxabiv1 {
    extern "C" void *
    __cxa_begin_catch (void *exc_obj_in) throw()
    {
      _Unwind_Exception *exceptionObject
          = reinterpret_cast <_Unwind_Exception *>(exc_obj_in);
      __cxa_eh_globals *globals = __cxa_get_globals ();
      __cxa_exception *prev = globals->caughtExceptions;
      __cxa_exception *header = __get_exception_header_from_ue (exceptionObject);

      // Foreign exceptions can't be stacked here.  If the exception stack is
      // empty, then fine.  Otherwise we really have no choice but to terminate.
      // Note that this use of "header" is a lie.  It's fine so long as we only
      // examine header->unwindHeader though.
      if (header->unwindHeader.exception_class != __gxx_exception_class)
          {
            if (prev != 0)
                std::terminate ();

            // Remember for end_catch and rethrow.
            globals->caughtExceptions = header;

            // ??? No sensible value to return; we don't know what the 
            // object is, much less where it is in relation to the header.
            return 0;
          }

      int count = header->handlerCount;
      if (count < 0)
          // This exception was rethrown from an immediately enclosing region.
          count = -count + 1;
      else
          {
            count += 1;
            globals->uncaughtExceptions -= 1;
          }
      header->handlerCount = count;

      if (header != prev)
          {
            header->nextException = prev;
            globals->caughtExceptions = header;
          }

      return header->adjustedPtr;
    }
} // namespace __cxxabiv1
#endif // __GNUC__ == 3 && __GNUC_MINOR__ < 4


//######################### std::{C++ exception routines} ########################


void uEHM::uTerminate() {
    uAbort( "Unhandled exception for task %.256s (0x%p)", uThisTask().uGetName(), &uThisTask() );
} // uEHM::uTerminate

void uEHM::uTerminateHandler() {
    try {
      (*uThisTask().uTerminateRtn)();
      uEHM::uTerminate();
    } catch( ... ) {
      uEHM::uTerminate();
    } // try
} // uEHM::uTerminateHandler

void uEHM::uUnexpected() {
    std::terminate();
} // uEHM::uTerminate

void uEHM::uUnexpectedHandler() {
    (*uThisCoroutine().uUnexpectedRtn)();
    std::terminate();
} // uEHM::uUnexpectedHandler

std::terminate_handler std::set_terminate( std::terminate_handler func ) throw() {
    uBaseTask &t = uThisTask();
    std::terminate_handler prev = t.uTerminateRtn;
    t.uTerminateRtn = func;
    return prev;
} // std::set_terminate

std::unexpected_handler std::set_unexpected( std::unexpected_handler func ) throw() {
    uBaseCoroutine &c = uThisCoroutine();       // optimization
    std::unexpected_handler prev = c.uUnexpectedRtn;
    c.uUnexpectedRtn = func;
    return prev;
} // std::set_unexpected


//######################### uEHM::uAsyncAEMsg ########################


uEHM::uAsyncAEMsg::uAsyncAEMsg( const uDualClass &ae_obj, const uFlagT f ) : flag( f ), uHidden( false ) {
    ae = ae_obj.uDuplicate();
} // uEHM::uAsyncAEMsg::uAsyncAEMsg

uEHM::uAsyncAEMsg::uAsyncAEMsg( const uThrowClass &ae_obj ) : flag( uThrowF ), uHidden( false ) {
    ae = ae_obj.uDuplicate();
} // uEHM::uAsyncAEMsg::uAsyncAEMsg

uEHM::uAsyncAEMsg::uAsyncAEMsg( const uRaiseClass &ae_obj ) : flag( uRaiseF ), uHidden( false ) {
    ae = ae_obj.uDuplicate();
} // uEHM::uAsyncAEMsg::uAsyncAEMsg

uEHM::uAsyncAEMsg::~uAsyncAEMsg() {
    delete ae;
} // uEHM::uAsyncAEMsg::~uAsyncAEMsg


//######################### uEHM::uAsyncAEMsgBuffer ########################


// msg queue for nonlocal exceptions in a coroutine

uEHM::uAsyncAEMsgBuffer::uAsyncAEMsgBuffer() {
} // uAsyncAEMsgBuffer::uAsyncAEMsgBuffer

uEHM::uAsyncAEMsgBuffer::~uAsyncAEMsgBuffer() {
    uCSpinLock dummy( lock );
    for ( uAsyncAEMsg *tmp = uDrop(); tmp; tmp = uDrop() ) {
      delete tmp;
    } // for
} // uEHM::uAsyncAEMsgBuffer::~uAsyncAEMsgBuffer

void uEHM::uAsyncAEMsgBuffer::uAddMsg( uAsyncAEMsg *msg ) {
    uCSpinLock dummy( lock );
    uAdd( msg );
} // uEHM::uAsyncAEMsgBuffer::uAddMsg

uEHM::uAsyncAEMsg *uEHM::uAsyncAEMsgBuffer::uRmMsg() {
    uCSpinLock dummy( lock );
    return uDrop();
} // uEHM::uAsyncAEMsgBuffer::uRmMsg

uEHM::uAsyncAEMsg *uEHM::uAsyncAEMsgBuffer::uRmMsg( uAsyncAEMsg *msg ) {
    // Only lock at the end or the start of the list as these are the only
    // places where interference can occur.

    if ( msg == uTail() || msg == uHead() ) {
      uCSpinLock dummy( lock );
      uRemove( msg );
    } else {
      uRemove( msg );
    } // if
    return msg;
} // uEHM::uAsyncAEMsgBuffer::uRmMsg

uEHM::uAsyncAEMsg *uEHM::uAsyncAEMsgBuffer::uNextVisible( uAsyncAEMsg *msg ) {
    // find the next msg node that is visible or null
    do {                                  // do-while because current node could be visible
      msg = uSucc( msg );
    } while ( msg && msg->uHidden );
    return msg;
} // uEHM::uAsyncAEMsgBuffer::uNextVisible


//######################### uDualClass ########################


uEHM::uDualClass::uDualClass( const char *const msg ) : msg( msg ), src( uThisCoroutine() ) {
    const char *name = src.uGetName();
    int lnth = strlen( name );
    strncpy( uName, name, uEHMMaxName );
    if ( lnth > uEHMMaxName ) {                       // name too long ?
      strcpy( &uName[uEHMMaxName], "..." );           // add 4 character ...
    } // if
}; // uEHM::uDualClass::uDualClass

uEHM::uDualClass::~uDualClass() {}

const char *const uEHM::uDualClass::message() const { return msg; }
const uBaseCoroutine &uEHM::uDualClass::source() const { return src; }
const char *uEHM::uDualClass::sourceName() const { return uName; }

const uEHM::uDualClass * const uEHM::uDualClass::setOriginalThrower( const void * p ) const {
    // Calls to this routine are generated by the translator at each throw
    // site. Because the exception object being thrown may be const, this
    // routine still has to modify the exception object. Hence, the need for
    // the const cast.
    const_cast< uEHM::uDualClass * >(this)->uStaticallyBoundObject = p;
    return this;
} // uEHM::uDualClass::setOriginalThrower

void uEHM::uDualClass::defaultTerminate() const {
} // uEHM::uDualClass::defaultTerminate

void uEHM::uDualClass::defaultResume() const {
    uStackThrow();
    // CONTROL NEVER REACHES HERE!
} // uEHM::uDualClass::defaultResume


uInitEvent(uEHM::uDualClass);


//######################### uEHM::uThrowClass ########################


uEHM::uThrowClass::uThrowClass( const char *const msg ) : uDualClass( msg ) {}
uEHM::uThrowClass::~uThrowClass() {}

#ifdef __DEBUG__
void uEHM::uThrowClass::defaultResume() const {
    uAbort( "uThrowClass object cannot be casted to a raise-able object." );
} // uEHM::uThrowClass::defaultResume
#endif // __DEBUG__


uInitEvent(uEHM::uThrowClass);


//######################### uEHM::uRaiseClass ########################


void uEHM::uRaiseClass::uStackThrow() const {
    uAbort( "Event cannot be thrown." );
} // uEHM::uRaiseClass::uStackThrow

uEHM::uRaiseClass::uRaiseClass( const char *const msg ) : uDualClass( msg ) {}
uEHM::uRaiseClass::~uRaiseClass() {}

#ifdef __DEBUG__
void uEHM::uRaiseClass::defaultTerminate() const {
    uAbort( "uRaiseClass object cannot be casted to a throw-able object." );
} // uEHM::uRaiseClass::defaultTerminate
#endif // __DEBUG__

void uEHM::uRaiseClass::defaultResume() const {
    uTerminate();
} // uEHM::uRaiseClass::defaultResume


uInitEvent(uEHM::uRaiseClass);


//######################### uEHM::uResumptionHandlers ########################


uEHM::uResumptionHandlers::uResumptionHandlers( uHandlerBase *const table[], const unsigned int size ) : size( size ), table( table ) {
    uBaseCoroutine &c = uThisCoroutine();       // optimization
    uNext = c.uHandlerStackTop;
    uConseqNext = c.uHandlerStackVisualTop;
    c.uHandlerStackTop = this;
    c.uHandlerStackVisualTop = this;
} // uEHM::uResumptionHandlers::uResumptionHandlers

uEHM::uResumptionHandlers::~uResumptionHandlers() {
    uBaseCoroutine &c = uThisCoroutine();       // optimization
    c.uHandlerStackTop = uNext;
    c.uHandlerStackVisualTop = uConseqNext;
} // uEHM::uResumptionHandlers::~uResumptionHandlers


//######################### uEHM::uDeliverAEStack ########################


uEHM::uDeliverAEStack::uDeliverAEStack( bool f, void **t, unsigned int msg ) : uDeliverFlag( f ), table_size( msg ), event_table( t ) {
    // the current node applies to all exceptions when table_size is 0
    uBaseCoroutine &c = uThisCoroutine();       // optimization
    next = c.uDAEStack;
    c.uDAEStack = this;
} // uEHM::uDeliverAEStack::uDeliverAEStack

uEHM::uDeliverAEStack::~uDeliverAEStack() {
    uThisCoroutine().uDAEStack = next;
} // uEHM::uDeliverAEStack::~uDeliverAEStack


//######################### uEHM::uRaiseWorkHorseInit ########################


// Initialization and finalization when handling a signalled event after
// finding a handler. This ensures the two different resuming handler
// hierarchies are properly maintained.  As well, it maintains the currently
// handled resumption object for reraise.

class uEHM::uRaiseWorkHorseInit {
    uResumptionHandlers *prevVisualTop;
    uDualClass *prevResumption;
  public:
    uRaiseWorkHorseInit( uResumptionHandlers *h, uDualClass & newResumption ) {
      uBaseCoroutine &c = uThisCoroutine();           // optimization
      prevResumption = c.RaisedObj;
      c.RaisedObj = &newResumption;

      uBaseCoroutine &current = c;
      prevVisualTop = current.uHandlerStackVisualTop;
      current.uHandlerStackVisualTop = h;
    } // uEHM::uRaiseWorkHorseInit::uRaiseWorkHorse

    ~uRaiseWorkHorseInit() {
      uBaseCoroutine &c = uThisCoroutine();           // optimization
      c.RaisedObj = prevResumption;
      c.uHandlerStackVisualTop = prevVisualTop;
    } // uEHM::uRaiseWorkHorseInit::~uRaiseWorkHorse
}; // uEHM::uRaiseWorkHorseInit


//######################### uEHM::uEHMAutoResourceCleanup ########################

// Used by uPoll to ensure a handled message is removed from the queue and its
// memory deallocated. These actions are done in the destructor to ensure the
// clean-up is performed even when an exception is thrown.  Unwinding the stack
// causes the uEHMAutoResourceCleanup object to be destroyed and hence the
// destructor to be called.

class uEHM::uEHMAutoResourceCleanup {
    uAsyncAEMsg *msg;
    uAsyncAEMsgBuffer &buf;
  public:
    uEHMAutoResourceCleanup( uAsyncAEMsg *msg, uAsyncAEMsgBuffer &buf ) : msg( msg ), buf( buf ) {}

    ~uEHMAutoResourceCleanup() {
      buf.uRmMsg(msg);
      delete msg;
    } // uEHM::uEHMAutoResourceCleanup::~uEHMAutoResourceCleanup
}; // uEHM::uEHMAutoResourceCleanup


//######################### uEHM ########################


#ifdef __U_DEBUG__
static void uEHMCheck( uBaseCoroutine &target, char *kind ) {
    if ( &target == NULL || &target == (uBaseCoroutine *) -1 || *((void **) &target) == NULL || *((void **) &target) == (void *) -1 ) {
      uAbort( "attempt by task %.256s (0x%p) to %s a nonlocal exception at target %.256s (0x%p), but the target is invalid or has been deleted",
            uThisTask().uGetName(), &uThisTask(), kind, target.uGetName(), &target );
    } // if
} // uEHMCheck
#endif // __U_DEBUG__

void uEHM::uEHMThrow( const uDualClass &ae_obj ) {
#ifdef __U_DEBUG_H__
    uDebugPrt( "uEHM::uEHMThrow( uDualClass::ae_obj:0x%p ) from task %.256s (0x%p)\n", &ae_obj, uThisTask().uGetName(), &uThisTask() );
#endif // __U_DEBUG_H__
    ae_obj.uStackThrow();
    // CONTROL NEVER REACHES HERE!
} // uEHM::uEHMThrow

void uEHM::uEHMThrow( const uThrowClass &ae_obj ) {
#ifdef __U_DEBUG_H__
    uDebugPrt( "uEHM::uEHMThrow( uThrowClass::ae_obj:0x%p ) from task %.256s (0x%p)\n", &ae_obj, uThisTask().uGetName(), &uThisTask() );
#endif // __U_DEBUG_H__
    ae_obj.uStackThrow();
    // CONTROL NEVER REACHES HERE!
} // uEHM::uEHMThrow

void uEHM::uEHMThrow( const uThrowClass &ae_obj, uBaseCoroutine &target ) {
#ifdef __U_DEBUG_H__
    uDebugPrt( "uEHM::uEHMThrow( uThrowClass::ae_obj:0x%p, target:0x%p ) from task %.256s (0x%p)\n", &ae_obj, &target, uThisTask().uGetName(), &uThisTask() );
#endif // __U_DEBUG_H__
#ifdef __U_DEBUG__
    uEHMCheck( target, "throw" );
#endif // __U_DEBUG__
    if ( target.uGetState() != uBaseCoroutine::uHalt ) {
      uAsyncAEMsg *temp = new uAsyncAEMsg( ae_obj );
      temp->ae->setOriginalThrower ( (void *)&ae_obj.source() );
      target.uAsyncAEBuf.uAddMsg( temp );
    } // if
} // uEHM::uEHMThrow

void uEHM::uEHMThrow( const uDualClass &ae_obj, uBaseCoroutine &target ) {
#ifdef __U_DEBUG__
    uEHMCheck( target, "throw" );
#endif // __U_DEBUG__
    if ( target.uGetState() != uBaseCoroutine::uHalt ) {
      uAsyncAEMsg *temp = new uAsyncAEMsg( ae_obj, uDualThrowF );
      temp->ae->setOriginalThrower( (void *)&ae_obj.source() );
      target.uAsyncAEBuf.uAddMsg( temp );
    } // if
} // uEHM::uEHMThrow


void uEHM::uEHMRaise( const uDualClass &ae_obj ) {
    uRaiseWorkHorse( ae_obj, true );
} // uEHM::uEHMRaise

void uEHM::uEHMRaise( const uRaiseClass &ae_obj ) {
    uRaiseWorkHorse( (uDualClass &)ae_obj, true );
} // uEHM::uEHMRaise

void uEHM::uEHMRaise( const uDualClass &ae_obj, uBaseCoroutine &target ) {
#ifdef __U_DEBUG__
    uEHMCheck( target, "raise" );
#endif // __U_DEBUG__
    if ( target.uGetState() != uBaseCoroutine::uHalt ) {
      uAsyncAEMsg *temp = new uAsyncAEMsg( ae_obj, uDualRaiseF );
      temp->ae->setOriginalThrower( (void *)&ae_obj.source() );
      target.uAsyncAEBuf.uAddMsg( temp );
    } // if
} // uEHM::uEHMRaise

void uEHM::uEHMRaise( const uRaiseClass &ae_obj, uBaseCoroutine &target ) {
#ifdef __U_DEBUG__
    uEHMCheck( target, "raise" );
#endif // __U_DEBUG__
    if ( target.uGetState() != uBaseCoroutine::uHalt ) {
      uAsyncAEMsg *temp = new uAsyncAEMsg( ae_obj );
      temp->ae->setOriginalThrower( (void *)&ae_obj.source() );
      target.uAsyncAEBuf.uAddMsg( temp );
    } // if
} // uEHM::uEHMRaise


void uEHM::uReRaise() {
    uBaseCoroutine &c = uThisCoroutine();       // optimization
    if ( c.RaisedObj == NULL ) {
      uTerminateHandler();
    } // if
    uRaiseWorkHorse( (uDualClass &) *(c.RaisedObj), true );
}; // uEHM::uReRaise


// Check for and handle pending nonlocal exceptions.

void uEHM::uPoll() {
    uAsyncAEMsgBuffer &msgbuf = uThisCoroutine().uAsyncAEBuf;
    uAsyncAEMsg *ae_msg = msgbuf.uHead();       // find first node in queue
    if ( ae_msg && ae_msg->uHidden ) {
      ae_msg = msgbuf.uNextVisible(ae_msg);           // from there, find first visible node
    } // if

    // For each visible node, check if responsible for its delivery. If yes,
    // hide it from any recursive uPoll call, handle the event, and remove it
    // from the queue through the automatic resource clean-up.

    while ( ae_msg ) {
      if ( deliverable_exception( (void *)(ae_msg->ae->uEvent_id()) ) ) {
          // Note, implicit context switches only call uYieldNoPoll(), hence
          // uPoll can only be called from uRaiseWorkHorse again, which is a
          // safe recursion.
          ae_msg->uHidden = true;                     // hide node from recursive children of uPoll
          uDualClass *ae = ae_msg->ae;
          
          // Recover memory allocated for async event message. Resuming
          // handler should not destroyed raised exception regardless of
          // whether the exception is normal or nonlocal.

          uEHMAutoResourceCleanup dummy_var( ae_msg, msgbuf ); // This ensures removal and deletion of the node at end of block.
                                                              // Necessary because of return or throw.

          if ( ae_msg->flag <= uDualThrowF ) {  // throw the event
            ae->uStackThrow();
            // CONTROL NEVER REACHES HERE!
          } // if
          uRaiseWorkHorse( *ae, false );        // handle event, potential recursion! also potential exception
          
          ae_msg = msgbuf.uNextVisible(ae_msg); // advance to next visible node

          // NOTE: AE_MSG IS DESTROYED HERE !!
      } else {
          ae_msg = msgbuf.uNextVisible(ae_msg); // advance to next visible node (without deleting)
      } // if
    } // while

    // NOTE: finalizer is destroyed here, sets the progress flag to its old
    // value, i.e., the first level turns it off
} // uEHM::uPoll


bool uEHM::uMatch_exception_type( void *derived_id, void *const parent_id ) {
    // return true if derived_id event is derived from parent_id event
#ifdef __DEBUG__
    if ( ! parent_id ) uAbort( "internal error, error in setting up guarded region." );
#endif // __DEBUG__
    for ( ; derived_id && derived_id != parent_id; derived_id = *((void **)derived_id) ) {
    } // for

    // all the ancestors of raised_id have been tested or a match found
    return derived_id;
}  // uEHM::uMatch_exception_type


bool uEHM::deliverable_exception( void *event_id ) {
    for ( uDeliverAEStack *tmp = uThisCoroutine().uDAEStack; tmp; tmp = tmp->next ) {
      if ( tmp->table_size == 0 ) {             // table_size == 0 is a short hand for all exceptions
          return tmp->uDeliverFlag;
      } // if

      for ( int i = 0; i < tmp->table_size; i += 1 ) {
          if ( uMatch_exception_type( event_id, tmp->event_table[i] ) ) {
            return tmp->uDeliverFlag;
          } // if
      } // for
    } // for
    return false;
} // uEHM::deliverable_exception


// Find and execute resumption handlers. If conseq == false, it means a
// non-consequential resumption (i.e., one newly raised outside of handling
// code) is being handled; hence, begin looking for handlers at the real top of
// the stack. If conseq == true, it means a consequential resumption is being
// handled, but in order to avoid recursive handling, should not consider
// earlier resumption handlers. In this case, the virtual top
// (uHandlerStackVisualTop) of the handler stack is used.

void uEHM::uRaiseWorkHorse( const uDualClass &ae_obj, const bool conseq ) {
#ifdef __U_DEBUG_H__
    uDebugPrt( "uEHM::uRaiseWorkHorse( ae_obj:0x%p, conseq:%d ) from task %.256s (0x%p)\n",
             &ae_obj, conseq, uThisTask().uGetName(), &uThisTask() );
#endif // __U_DEBUG_H__

    void *eventId = ae_obj.uEvent_id();
    uResumptionHandlers *tmp = (conseq) ? uThisCoroutine().uHandlerStackVisualTop : uThisCoroutine().uHandlerStackTop;

    while ( tmp ) {
#ifdef __U_DEBUG_H__
      uDebugPrt( "uEHM::uRaiseWorkHorse tmp:0x%p, eventId:0x%p\n", tmp, eventId );
#endif // __U_DEBUG_H__

      uResumptionHandlers *next = (conseq) ? tmp->uConseqNext : tmp->uNext;

      // search all resumption handlers in the same handler clause
      for ( unsigned int i = 0; i < tmp->size; i += 1 ) {
          uHandlerBase *elem = tmp->table[i];         // optimization
          const void *bound = ae_obj.getOriginalThrower();
#ifdef __U_DEBUG_H__
          uDebugPrt( "uEHM::uRaiseWorkHorse table[%d]:0x%p, originalThrower:0x%p, eventId:0x%p, bound:0x%p\n",
                   i, elem, elem->originalThrower, elem->eventId, bound );
#endif // __U_DEBUG_H__
          // if (no binding OR binding match) AND type match => handler found
          if ( ( elem->originalThrower == 0 || bound == elem->originalThrower ) &&
             uMatch_exception_type( eventId, elem->eventId ) ) {
#ifdef __U_DEBUG_H__
            uDebugPrt( "uEHM::uRaiseWorkHorse match\n" );
#endif // __U_DEBUG_H__
            uRaiseWorkHorseInit newStackVisualTop( next, (uDualClass &)(ae_obj) );
            elem->uHandler( (uDualClass &)(ae_obj) );
            return;                             // return after handling the exception
          } // if
      } // for
      tmp = next;                         // next resumption handler clause
    } // while

    // cannot find a handler, use the default handler

    ae_obj.defaultResume();                     // default handler can change the exception
} // uEHM::uRaiseWorkHorse


// Local Variables: //
// compile-command: "gmake install" //
// End: //

Generated by  Doxygen 1.6.0   Back to index