
/*
  CoreLinux++ 
  Copyright (C) 1999,2000 CoreLinux Consortium
  
   The CoreLinux++ Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The CoreLinux++ Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  
*/

#if   !defined(__COMMON_HPP)
#include <Common.hpp>
#endif

#if   !defined(__THREADCONTEXT_HPP)
#include <ThreadContext.hpp>
#endif

extern "C"
{
   #include <signal.h>
   #include <sched.h>
}

namespace corelinux
{
   // Default stack size to use

   const Size  DEFAULT_STACKSIZE(8192);

   // Set the default thread frame entry point

   ThreadFrameFunctionPtr     ThreadContext::theDefaultFrameFunction
      ( 
         defaultFrameFunction 
      );

   // Set the default context allocation handler

   ThreadContextCreatePtr  ThreadContext::theDefaultContextCreator
      ( 
         defaultContextCreate
      );

   // Set the default context deallocation handler

   ThreadContextDestroyPtr ThreadContext::theDefaultContextDestroyer
      (
         defaultContextDestroy
      );

   // Set the default stack allocation handler

   ThreadStackCreatePtr    ThreadContext::theDefaultStackCreator
      ( 
         defaultStackCreate
      );

   // Set the default stack deallocation handler

   ThreadStackDestroyPtr   ThreadContext::theDefaultStackDestroyer
      (
         defaultStackDestroy
      );

   //
   // Default unused constructor
   //

   ThreadContext::ThreadContext( void )
      throw ( Assertion )
      :
      Synchronized(),
      theFrameFunction( defaultFrameFunction ), 
      theContextCreator( defaultContextCreate ),
      theContextDestroyer( defaultContextDestroy ),
      theStackCreator( defaultStackCreate ),
      theStackDestroyer( defaultStackDestroy ),
      theStack( NULLPTR ),
      theStackSize( 0 ),
      theShareMask( 0 ),
      theThreadIdentifier( -1 ),
      theCallersFunction( NULLPTR ),
      theThreadState( THREAD_EXCEPTION ),
      theReturnCode( 0 )
   {
      NEVER_GET_HERE;
   }

   //
   // Minimal Constructor
   //

   ThreadContext::ThreadContext( CallerFunctionPtr aFuncPtr )
      throw ( Assertion )
      :
      Synchronized(),
      theFrameFunction( defaultFrameFunction ), 
      theContextCreator( defaultContextCreate ),
      theContextDestroyer( defaultContextDestroy ),
      theStackCreator( defaultStackCreate ),
      theStackDestroyer( defaultStackDestroy ),
      theStack( NULLPTR ),
      theStackSize( DEFAULT_STACKSIZE ),
      theShareMask
         ( 
            CLONE_VM | 
            CLONE_FS | 
            CLONE_FILES | 
            CLONE_SIGHAND | 
            SIGCHLD 
         ),
      theThreadIdentifier( -1 ),
      theCallersFunction( NULLPTR ),
      theThreadState( THREAD_EXCEPTION ),
      theReturnCode( 0 )
   {
      REQUIRE( aFuncPtr != NULLPTR );

      theCallersFunction = aFuncPtr;
      theThreadState = THREAD_WAITING_TO_START;
   }

   //
   // With user defined stack size
   //

   ThreadContext::ThreadContext
      ( 
         CallerFunctionPtr aFuncPtr, 
         Size aStackSize 
      )
      throw ( Assertion )
      :
      Synchronized(),
      theFrameFunction( defaultFrameFunction ), 
      theContextCreator( defaultContextCreate ),
      theContextDestroyer( defaultContextDestroy ),
      theStackCreator( defaultStackCreate ),
      theStackDestroyer( defaultStackDestroy ),
      theStack( NULLPTR ),
      theStackSize( DEFAULT_STACKSIZE ),
      theShareMask
         ( 
            CLONE_VM | 
            CLONE_FS | 
            CLONE_FILES | 
            CLONE_SIGHAND | 
            SIGCHLD 
         ),
      theThreadIdentifier( -1 ),
      theCallersFunction( NULLPTR ),
      theThreadState( THREAD_EXCEPTION ),
      theReturnCode( 0 )
   {
      REQUIRE( aFuncPtr != NULLPTR );
      REQUIRE( aStackSize > 0 );
   
      theCallersFunction = aFuncPtr;
      theThreadState = THREAD_WAITING_TO_START;
      theStackSize = aStackSize;
   }


   //
   // Copy constructor
   //

   ThreadContext::ThreadContext( ThreadContextCref aContext )
      throw ( Assertion )
      :
      Synchronized(),
      theFrameFunction( aContext.theFrameFunction ), 
      theContextCreator( aContext.theContextCreator ),
      theContextDestroyer( aContext.theContextDestroyer ),
      theStackCreator( aContext.theStackCreator ),
      theStackDestroyer( aContext.theStackDestroyer ),
      theStack( NULLPTR ),
      theStackSize( DEFAULT_STACKSIZE ),
      theShareMask
         ( 
            CLONE_VM | 
            CLONE_FS | 
            CLONE_FILES | 
            CLONE_SIGHAND | 
            SIGCHLD 
         ),
      theThreadIdentifier( -1 ),
      theCallersFunction( NULLPTR ),
      theThreadState( THREAD_EXCEPTION ),
      theReturnCode( 0 )
   {
      REQUIRE( aContext.getState() == THREAD_WAITING_TO_START );

      theThreadState = THREAD_WAITING_TO_START;
      theStackSize = aContext.theStackSize;
      theShareMask = aContext.theShareMask;
      theCallersFunction = aContext.theCallersFunction;
   }

   //
   // Destructor
   //

   ThreadContext::~ThreadContext( void )
   {
      theThreadState = THREAD_EXCEPTION,
      theStackSize = 0;
      theShareMask = 0;
      theThreadIdentifier = (-1);
      theReturnCode = -1;

      theStack =  NULLPTR;
      theCallersFunction =  NULLPTR;
      theFrameFunction =  NULLPTR;
      theContextCreator = NULLPTR;
      theContextDestroyer = NULLPTR;
      theStackCreator = NULLPTR;
      theStackDestroyer = NULLPTR;

   }

   //
   // Assignment operator
   //

   ThreadContextRef ThreadContext::operator=( ThreadContextCref aContext )
      throw ( Assertion )
   {
      REQUIRE( aContext.getState() == THREAD_WAITING_TO_START );
      REQUIRE( theThreadState == THREAD_WAITING_TO_START );

      if( this != &aContext )
      {
         theStackSize = aContext.theStackSize;
         theShareMask = aContext.theShareMask;
         theCallersFunction = aContext.theCallersFunction;
      }
      else
      {
         ;  // do nothing
      }

      return (*this);
   }


   // Assign identifier

   ThreadContextRef ThreadContext::operator=( ThreadIdentifier aIdentifier )
   {
      theThreadIdentifier = aIdentifier;
      return ( *this );
   }

   // Equality on instance

   bool  ThreadContext::operator==( ThreadContextCref aContext ) const
   {
      return ( this == &aContext );
   }

   // Equality on identifier

   bool  ThreadContext::operator==( ThreadIdentifierCref aIdentifier ) const
   {
      return ( theThreadIdentifier == aIdentifier );
   }

   // Equality on function pointer

   bool  ThreadContext::operator==( CallerFunctionPtr aFunction ) const
   {
      return ( theCallersFunction == aFunction );
   }

   // Coerce identifier from instance

   ThreadContext::operator ThreadIdentifierCref( void ) const
   {
      return theThreadIdentifier;
   }

   // Get the thread state

   const ThreadState &  ThreadContext::getState( void ) const
   {
      return theThreadState;
   }

   // Get the threads return code

   Int      ThreadContext::getReturnCode( void ) const
   {
      return theReturnCode;
   }

   // Explicit call for identifier

   ThreadIdentifierCref ThreadContext::getIdentifier( void ) const
   {
      return theThreadIdentifier;
   }

   // Get the start function for the caller

   CallerFunctionPtr    ThreadContext::getCallerFunction( void )
   {
      return theCallersFunction;
   }

   // Return the size of the stack 

   Size     ThreadContext::getStackSize( void ) const
   {
      return theStackSize;
   }

   // Get the contexts share mask

   Int      ThreadContext::getShareMask( void ) const
   {
      return theShareMask;
   }

   // Get the stack pointer

   BytePtr  ThreadContext::getStack( void )
   {
      return theStack;
   }

   // Get the top of the stack for execution

   BytePtr  *ThreadContext::getStackTop( void )
   {
      BytePtr  *st( (BytePtr *) (theStackSize + (BytePtr) theStack) );
      return st;
   }

   ThreadFrameFunctionPtr  ThreadContext::getFramePointer( void )
   {
      return theFrameFunction;
   }

   // Work with the resource share mask

   void  ThreadContext::setShareMask( Int aMask )
   {
      theShareMask = aMask;
   }

   // Set the thread state

   void  ThreadContext::setThreadState( ThreadState aState )
   {
      theThreadState = aState;
   }

   // Set the thread return code

   void  ThreadContext::setReturnCode( Int aReturnCode )
   {
      theReturnCode = aReturnCode;
   }

   // Sets the thread frame handler

   void  ThreadContext::setFrameFunction( ThreadFrameFunctionPtr aFrame )
   {
      GUARD;

      //
      // If either are null, we are in effect asking to reset to
      // the defaults.
      //

      if( aFrame == NULLPTR )
      {
         theFrameFunction = defaultFrameFunction;
      }

      //
      // Otherwise set the new handler.
      //

      else
      {
         theFrameFunction = aFrame;
      }
   }

   // Set the context handlers

   void  ThreadContext::setContextFunctions
      ( 
         ThreadContextCreatePtr aCreate, 
         ThreadContextDestroyPtr aDestroy 
      )
   {
      GUARD;

      // If any are null, we revert to defaults

      if( aCreate == NULLPTR || aDestroy == NULLPTR )
      {
         theContextCreator = defaultContextCreate;
         theContextDestroyer = defaultContextDestroy;
      }

      // Otherwise we set the functions specified

      else
      {
         theContextCreator = aCreate;
         theContextDestroyer = aDestroy;
      }
   }

   // Set the stack handlers

   void  ThreadContext::setStackFunctions
      ( 
         ThreadStackCreatePtr aCreate, 
         ThreadStackDestroyPtr aDestroy 
      )
   {
      GUARD;

      // If any are null, we revert to defaults

      if( aCreate == NULLPTR || aDestroy == NULLPTR )
      {
         theStackCreator = defaultStackCreate;
         theStackDestroyer = defaultStackDestroy;
      }

      // Otherwise we set the functions specified

      else
      {
         theStackCreator = aCreate;
         theStackDestroyer = aDestroy;
      }
   }

   // Factory create method

   ThreadContextPtr  ThreadContext::createContext( void )
      throw ( ThreadException )
   {
      ThreadContextPtr  aContextPtr( NULLPTR );
      
      aContextPtr = (*theContextCreator)(*this);

      if( aContextPtr != NULLPTR )
      {
         try
         {
            aContextPtr->theStack = (*theStackCreator)(aContextPtr);
            if( aContextPtr->theStack == NULLPTR )
            {
               destroyContext( aContextPtr );
               aContextPtr = NULLPTR;
               throw ThreadException("Unable to allocate stack",LOCATION);
            }
            else
            {
               ;  // do nothing
            }
         }
         catch(...)
         {
            destroyContext( aContextPtr );
            aContextPtr = NULLPTR;
            throw;
         }
      }
      else
      {
         throw ThreadException("Unable to allocate context",LOCATION);
      }

      return aContextPtr;
   }

   // Factory destroy method

   void  ThreadContext::destroyContext( ThreadContextPtr aContext )
      throw ( Assertion )
   {
      REQUIRE( aContext != NULLPTR );
      if( aContext->theStack != NULLPTR )
      {
         (*theStackDestroyer)(aContext->theStack);
         aContext->theStack = NULLPTR;
      }
      else
      {
         ;  // do nothing
      }

      (*theContextDestroyer)(aContext);
   }

   // The thread frame immutable entry point

   Int   ThreadContext::cloneFrameFunction( ThreadContextPtr aPtr )
   {
      try
      {
         aPtr->setThreadState( THREAD_RUNNING );

         (*aPtr) = Thread::getThreadIdentifier();

         aPtr->setReturnCode(aPtr->getFramePointer()(aPtr));

         aPtr->setThreadState( THREAD_NORMAL_EXIT );
      }
      catch( ExceptionRef aRef )
      {
         aPtr->setThreadState( THREAD_EXCEPTION );
      }
      catch( ... )
      {
         aPtr->setThreadState( THREAD_EXCEPTION );
      }

      return aPtr->getReturnCode();
   }

   // The thread frame substitution point

   Int      ThreadContext::defaultFrameFunction( ThreadContextPtr aPtr )
   {
      return (aPtr->getCallerFunction())(aPtr);
   }

   // The allocation of the context to operate on

   ThreadContextPtr ThreadContext::defaultContextCreate
      ( 
         ThreadContextRef aInitContext 
      )
   {
      ThreadContextPtr  aContextPtr( new ThreadContext(aInitContext) );

      if( aContextPtr == NULLPTR )
      {
         throw Exception("Unable to allocate context", LOCATION );
      }
      else
      {
         ;  // do nothing
      }

      return aContextPtr;
   }

   // The deallocation of the context operated on.

   void  ThreadContext::defaultContextDestroy( ThreadContextPtr aContext )
   {
      if( aContext != NULLPTR )
      {
         delete aContext;
      }
      else
      {
         ;  // do nothing
      }
   }

   // The allocation for the thread stack

   BytePtr  ThreadContext::defaultStackCreate( ThreadContextPtr aContext )
   {
      BytePtr  aStackPtr( new Byte[aContext->getStackSize()] );

      if( aStackPtr == NULLPTR )
      {
         throw Exception("Unable to allocate stack", LOCATION );
      }
      else
      {
         ;  // do nothing
      }

      return aStackPtr;
   }

   // The deallocation for the thread stack

   void  ThreadContext::defaultStackDestroy( BytePtr aStack )
   {
      if( aStack != NULLPTR )
      {
         delete [] aStack;
      }
      else
      {
         ;  // do nothing
      }
   }

}


/*
   Common rcs information do not modify
   $Author: frankc $
   $Revision: 1.4 $
   $Date: 2000/07/28 01:39:47 $
   $Locker:  $
*/

