
/*
  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.  
*/

extern "C"
{
   #include <errno.h>
   #include <unistd.h>
   #include <sys/wait.h>
   #include <signal.h>
   #include <sched.h>
   #include <sys/resource.h>
}

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

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

#include <iostream>

typedef  void (*termIntf)(int, siginfo_t *, void *);  

namespace   corelinux
{
   // Setup the singleton instance for the Thread

   ThreadManager     Thread::theThreadManager( new Thread );
   ThreadIdentifier  Thread::theThreadManagerId;

   // The map of thread contexts managed by Thread

   ThreadMap      Thread::theThreadMap;
   Count          Thread::theThreadCount (0);

   // Default constructor

   Thread::Thread( void ) 
      throw( Assertion )
   {
      //
      // Make sure we are the only one
      //

      if( theThreadManager.instance() == NULLPTR )
      {
         //
         // Get my identifier
         //

         theThreadManagerId = getpid();

         //
         // Setup terminate handler
         //

         struct sigaction sa;
         memset(&sa,0,sizeof(sa));
         sa.sa_sigaction = (termIntf)threadTerminated;
         sa.sa_flags = SA_SIGINFO;

         sigaction(SIGCHLD,&sa,NULLPTR);
      }
      else
      {
         NEVER_GET_HERE;
      }
   }

   // Copy constructor

   Thread::Thread( ThreadCref ) throw ( Assertion )
      :
      Synchronized()
   {
      NEVER_GET_HERE;
   }

   // Destructor
   
   Thread::~Thread( void )
   {
      ;  // do nothing yet
   }

   // Assignment operator

   ThreadRef Thread::operator=( ThreadCref ) throw ( Assertion )
   {
      NEVER_GET_HERE;
      return (*this);
   }

   // Equality operator

   bool  Thread::operator==( ThreadCref ) const throw ( Assertion ) 
   {
      NEVER_GET_HERE;
      return false;
   }

   // Return thread instance errno

   Int Thread::getKernelError( void )
   {
      return Int(*__errno_location());
   }

   // Return the thread identifier

   ThreadIdentifier  Thread::getThreadIdentifier( void )
   {
      return ThreadIdentifier( getpid() );
   }

   // Get the parent thread of this thread

   ThreadIdentifier  Thread::getParentThreadIdentifier( void )
   {
      return ThreadIdentifier( getppid() );
   }

   // Return the owner thread of Thread

   ThreadIdentifierCref Thread::getThreadManagerIdentifier( void )
   {
      return theThreadManagerId;
   }

   // Retrieve a thread context for the caller

   ThreadContextCref Thread::getThreadContext( ThreadIdentifierCref anId ) 
      throw ( InvalidThreadException )
   {
      Guard    myGuard( theThreadManager.instance()->access() );

      ThreadMapIterator aTCItr( theThreadMap.find(anId) );
      if( aTCItr == theThreadMap.end() )
      {
         throw InvalidThreadException( LOCATION );
      }
      else
      {
         ;  // do nothing
      }
      return *((*aTCItr).second);
   }

   // Get created thread count
   
   Count Thread::getCreatedThreadCount( void )
   {
      return theThreadCount;
   }

   // Get active thread count

   Count Thread::getActiveThreadCount( void )
   {
      Guard    myGuard( theThreadManager.instance()->access() );

      //
      // Setup the count and iterators outside the loop
      //

      Count             aCount(0);
      ThreadMapIterator begin( theThreadMap.begin() );
      ThreadMapIterator end( theThreadMap.end() );

      while( begin != end )
      {
         //
         // Get the state and test for some state of activeness
         //

         const ThreadState &  aState( (*begin).second->getState() );

         if( aState == THREAD_STARTING ||
             aState == THREAD_RUNNING )
         {
            ++aCount;
         }
         else
         {
            ; // do nothing
         }
         ++begin;
      }

      return aCount;
   }

   // Get the non-active thread count

   Count Thread::getCompletedThreadCount( void )
   {
      Guard    myGuard( theThreadManager.instance()->access() );

      //
      // Setup the count and iterators outside the loop
      //

      Count             aCount(0);
      ThreadMapIterator begin( theThreadMap.begin() );
      ThreadMapIterator end( theThreadMap.end() );

      while( begin != end )
      {
         //
         // Get the state and test for some state of inactiveness
         //

         if( (*begin).second->getState() > THREAD_RUNNING )
         {
            ++aCount;
         }
         else
         {
            ; // do nothing
         }
         ++begin;
      }

      return aCount;
   }

   // Get the blocked thread count

   Count Thread::getBlockedThreadCount( void )
   {
      Guard    myGuard( theThreadManager.instance()->access() );

      //
      // Setup the count and iterators outside the loop
      //

      Count             aCount(0);
      ThreadMapIterator begin( theThreadMap.begin() );
      ThreadMapIterator end( theThreadMap.end() );

      while( begin != end )
      {
         //
         // Get the state and test for some state of inactiveness
         //

         if( (*begin).second->getState() == THREAD_WAITING_TO_START )
         {
            ++aCount;
         }
         else
         {
            ; // do nothing
         }
         ++begin;
      }

      return aCount;
   }

   // Test routine for collection

   void  Thread::dump( void )
   {
      Guard    myGuard( theThreadManager.instance()->access() );

      ThreadMapIterator begin( theThreadMap.begin() );
      ThreadMapIterator end( theThreadMap.end() );

      std::cout  << std::endl;
      while( begin != end )
      {
         std::cout << "Thread [" << (*begin).first.getScalar() <<
            "] : " << (*begin).second->getState() << std::endl;
         ++begin;
      }
      std::cout << std::endl;
   }

   // Allocate context, allocate stack, invoke frame

   ThreadIdentifier  Thread::startThread( ThreadContextRef aContextRef )
   {
      ThreadContextPtr  aContextPtr( NULLPTR );
      ThreadIdentifier  aThreadId( -1 );

      // Get myself synchronous to prevent handler changes
      // when trying to start something, and hold onto it
      // as I may need the exception handled safely as well

      Guard    myGuard( theThreadManager.instance()->access() );

      try
      {
         //
         // Create a managed instance object.
         // And then start the thread frame
         //
         
         aContextPtr =  aContextRef.createContext();

	 typedef int (* CloneFunctionType)(void *);
         aThreadId = ::clone
            (
	     CloneFunctionType(ThreadContext::cloneFrameFunction),
//               aContextPtr->getFramePointer(),
               aContextPtr->getStackTop(),
               aContextPtr->getShareMask(), 
               aContextPtr
            );

         //
         // If the clone failed we need to indicate it to the
         // caller in THEIR context argument because we don't
         // have a valid key for them to retrieve the context
         // with. We also remove the allocations we have made.
         //

         if( aThreadId.getScalar() == -1 )
         {
            aContextRef.setReturnCode( Thread::getKernelError() );
            aContextRef.setThreadState( THREAD_START_FAILED );
            aContextRef.destroyContext( aContextPtr );
         }
         else
         {
            theThreadMap[aThreadId] = aContextPtr;
            aContextPtr->setThreadState ( THREAD_RUNNING );
            theThreadCount++;
         }
      }

      //
      // Handle cleanup before rethrowing the exception
      //

      catch( ExceptionRef aExcp )
      {
         aContextRef.setThreadState( THREAD_START_EXCEPTION );
         aContextRef.destroyContext(aContextPtr);
         throw;
      }
      catch( ... )
      {
         aContextRef.setThreadState( THREAD_START_EXCEPTION );
         aContextRef.destroyContext(aContextPtr);
         throw;
      }

      return aThreadId;
   }

   // Blocks caller waiting until thread ended

   Int   Thread::waitForThread( ThreadIdentifierCref anId )
      throw ( InvalidThreadException )
   {
      ThreadContextCref aContext(Thread::getThreadContext(anId));

      Int   aStatus(0);
      waitpid(anId.getScalar(),&aStatus,0);
      return aStatus;
   }

   // Destroy the thread context

   void  Thread::destroyThreadContext( ThreadIdentifierCref anId )
      throw ( InvalidThreadException, Assertion )
   {
      Guard    myGuard( theThreadManager.instance()->access() );

      //
      // First find the thread
      //
      ThreadMapIterator aTCItr( theThreadMap.find(anId) );
      if( aTCItr == theThreadMap.end() )
      {
         throw InvalidThreadException( LOCATION );
      }
      else
      {
         ;  // do nothing
      }

      // easy access

      ThreadContextPtr  aTargetContext( (*aTCItr).second );

      // validate its state

      ENSURE( aTargetContext->getState() != THREAD_STARTING );
      ENSURE( aTargetContext->getState() != THREAD_RUNNING );

      // make a copy for the factory methods

      ThreadContext     aContext(*aTargetContext);

      // destroy the instance

      aContext.destroyContext( aTargetContext );

      // remove from map

      theThreadMap.erase( aTCItr );
   }

   // Get thread priority

   Int Thread::getThreadPriority(ThreadIdentifierCref anId)
      throw ( InvalidThreadException, Assertion )
   {
      ThreadContextCref aContext( Thread::getThreadContext( anId ) );
      return Environment::getThreadPriority( aContext.getIdentifier() );
   }

   // Set thread priority

   void Thread::setThreadPriority(ThreadIdentifierCref anId, Int prio)
      throw ( InvalidThreadException, Assertion )
   {
      ThreadContextCref aContext( Thread::getThreadContext( anId ) );
      Environment::setThreadPriority(aContext.getIdentifier(), prio);
   }

   //
   // Thread termination handler to insure that the state of the thread
   // is at least accurate to completion. This is very likely to change
   // once a handle on Signal implementation and the validity of the
   // ucontext is confirmed.
   //

   void  Thread::threadTerminated( Int aNum, VoidPtr vInfo, VoidPtr vPtr )
   {
      /*
      std::cout  << DwordPtr(vPtr)[0] << std::endl;
      std::cout  << DwordPtr(vPtr)[1] << std::endl;
      std::cout  << DwordPtr(vPtr)[2] << std::endl;
      std::cout  << DwordPtr(vPtr)[3] << std::endl;
      std::cout  << DwordPtr(vPtr)[4] << std::endl;
      std::cout  << DwordPtr(vPtr)[5] << std::endl;
      std::cout  << DwordPtr(vPtr)[6] << std::endl;
      std::cout  << DwordPtr(vPtr)[7] << std::endl;
      std::cout  << DwordPtr(vPtr)[8] << std::endl;
      std::cout  << (void * ) DwordPtr(vPtr)[9] << std::endl;
      std::cout  << DwordPtr(vPtr)[10] << std::endl;
      std::cout  << (void * ) DwordPtr(vPtr)[11] << std::endl;
      std::cout  << (void * ) DwordPtr(vPtr)[12] << std::endl; 
      std::cout  << "pid = " << DwordPtr(vPtr)[13] << std::endl;
      std::cout  << DwordPtr(vPtr)[14] << std::endl;
      std::cout  << (void * ) DwordPtr(vPtr)[15] << std::endl;
      std::cout  << "pid = " << DwordPtr(vPtr)[16] << std::endl;
      std::cout  << " Terminate received " << std::endl;
      */

      int pid = waitpid( -1, NULL, WNOHANG );

      if (pid != -1) 
      {

         ThreadIdentifier anId = pid;
         ThreadContext aContext( Thread::getThreadContext( anId ) );
         aContext.setThreadState( THREAD_NORMAL_EXIT );

         // If the following line is enabled, exception will occur

         //destroyThreadContext( anId );
      }
      else
      {
         ; // do nothing
      }
   }

}

/*
   Common rcs information do not modify
   $Author: dulimart $
   $Revision: 1.20 $
   $Date: 2001/04/04 19:47:47 $
   $Locker:  $
*/

