/*
  CoreLinux++ 
  Copyright (C) 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.  
*/   

/** \example examp14.cpp
   This example is to show use of the SemaphoreGroup and Semaphore. 
   
   For this test we use the GatewaySemaphoreGroup. We create two semaphores,
   each with a GatewayCount of three (3).
   
   while the Parent thread (main) releases the resources on the first semaphore,
   it consumes resources on the second.
   
   while the Child thread (threadCountTest) consumes resources on the first,
   it releases them on the second.
   
*/                   

//
// Clone support
//

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

#include <Common.hpp>
#include <GatewaySemaphoreGroup.hpp>
#include <GatewaySemaphore.hpp>

using namespace corelinux;
using namespace std;

#include <iostream>
#include <exception>

//
// In module function prototypes
//

int   main( void );


//
// General Functions 
//

void  handleAssertion( AssertionCref );
void  handleException( ExceptionCref );
void  handleStatus( SemaphoreOperationStatus );

//
// Thread entry point declarations
//

Int threadCountTest( void *aV );

//
// Global data
//

const int                  STACKSIZE(4096);     // Thread stack size
      ThreadIdentifier     badThread(-1);       // Use for testing
      Dword                gvalue(0);           // Resource being protected
      SemaphoreIdentifier  aSemId0(0);
      SemaphoreIdentifier  aSemId1(1);

//
// Main entry point
//

int main( void )
{

   cout << endl;

   GatewaySemaphoreGroupPtr  agGroup( NULLPTR );

   //
   // Practice graceful exception management
   //

   try
   {
      //
      // Setup our SemaphoreGroup and control Semaphores
      //

      agGroup = new GatewaySemaphoreGroup
         ( 
            2, OWNER_ALL | GROUP_ALL | PUBLIC_ALL 
         );


      //
      // In our first example, we create a non-recursive GatewaySemaphore to use as
      // a counter. By consuming all the resources before starting the thread we can
      // eek out control
      //

      GatewaySemaphorePtr aSem0
         ( 
            (GatewaySemaphorePtr)agGroup->createCountSemaphore
               (
                  aSemId0,
                  3,
                  CREATE_OR_REUSE
               ) 
         );

      aSem0->lockWithWait();
      aSem0->lockWithWait();
      aSem0->lockWithWait();

      //
      // Setup thread stack and point to bottom
      //

      VoidPtr  *newstack((VoidPtr*) new Byte[STACKSIZE]);

      newstack = (void **)(STACKSIZE + (char *) newstack);
      
      //
      // Spawn thread
      // threadCountTest - entry point int (fn*func)(void*arg)
      // newstack - bos
      // flags - Share everything
      // agGroup - SemaphoreGroupPtr (arg)
      //

      ThreadIdentifier  aChildId =
         clone
            (
               threadCountTest,
               newstack,
               CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD, 
               agGroup
            );

      //
      // Check for errors
      //

      if( aChildId != badThread )
      {
         //
         // We could have used a MutexSemaphore as well to get 
         // error free indication the thread has started.
         //

         sleep(1);

         //
         // Get the thread controlled resource counter
         //

         GatewaySemaphorePtr  aSem1
            ( 
               (GatewaySemaphorePtr)agGroup->createCountSemaphore
                  (
                     aSemId1,
                     3,
                     CREATE_OR_REUSE
                  ) 
            );

         //
         // Signal handling would be a better mechanism, but for now
         // loop a few
         //

         do
         {
            cout  << "Parent releasing count" << endl;
            aSem0->release();
            aSem1->lockWithWait();
         } while( aSem0->getOwnerRecursionQueueLength() != -1 );


         Int   aStatus(0);
         waitpid(aChildId.getScalar(),&aStatus,0);

         cout << "Thread had " << 
            (WIFEXITED(aStatus) ? "Normal exit" : "Error exit" ) << 
               endl;

         agGroup->destroySemaphore(aSem0);
         agGroup->destroySemaphore(aSem1);

      }
      else
      {
         cerr << "Error creating thread : " << Thread::getKernelError() << endl;
      }
   
      //
      // Housekeeping
      //

      delete agGroup;
      agGroup = NULLPTR;
   }

   catch( SemaphoreExceptionRef aRef )
   {
      handleException(aRef);
      cerr << "Error number " << aRef.getErrNum() << endl;
   }
   catch( AssertionRef aAssert )
   {
      handleAssertion(aAssert);
   }
   catch( ExceptionRef aException )
   {
      handleException(aException);
   }
   catch( std::exception & e )
   {
      cerr  << e.what() << endl;
   }
   catch( ... )
   {
      cerr  << "Unknown exception." << endl;
   }

   //
   // If we have not gone through the housekeeping
   //

   if( agGroup != NULLPTR )
   {
      delete agGroup;
   }

   return 0;               
}

//
// Child thread implementation that exchanges counters
//

Int threadCountTest( void *aV )
{

   try
   {
      GatewaySemaphoreGroupPtr   aSG( (GatewaySemaphoreGroupPtr)aV );

      GatewaySemaphorePtr        aSem1( 
         (GatewaySemaphorePtr)aSG->createCountSemaphore(aSemId1,3,CREATE_OR_REUSE) );

      GatewaySemaphorePtr        aSem0( 
         (GatewaySemaphorePtr)aSG->createCountSemaphore(aSemId0,3,CREATE_OR_REUSE) );

      aSem1->lockWithWait();
      aSem1->lockWithWait();
      aSem1->lockWithWait();

      do
      {
         aSem0->lockWithWait();
         cout  << "Child gets count" << endl;
         aSem1->release();
      } while( aSem0->getOwnerRecursionQueueLength() != 3 );

   }

   catch(SemaphoreExceptionRef aSemExcp )
   {
      handleException( aSemExcp );
   }
   catch( ... )
   {
      cerr << "Thread bailing out with exception" << endl;
   }

   return 0;
}


//
// Peform defaults (just show it)
//

void  handleStatus( SemaphoreOperationStatus aStatus )
{
   switch( aStatus )
   {
      case  SUCCESS:
         cerr << "Success" << endl;
         break;
      case  BALKED:
         cerr << "Request balked" << endl;
         break;
      case  TIMEDOUT:
         cerr << "Request timed out" << endl;
         break;
      case UNAVAILABLE:
         cerr << "Request not satisfied" << endl;
         break;
      case  KERNELERROR:
         cerr << "Kernel reported error : " << Thread::getKernelError() << endl;
         break;
      default:
         NEVER_GET_HERE;
         break;
   }

   return;
}

void  handleAssertion( AssertionCref aAssert )
{
   cerr << aAssert.getFile() << ":" << aAssert.getLine() << ":" << 
      "Assertion: ";

   if( aAssert.getType() == Assertion::NEVERGETHERE )
   {
      cerr << "NEVER_GET_HERE";
   }
   else
   {
      if( aAssert.getType() == Assertion::REQUIRE )
      {
         cerr  << "REQUIRE";
      }
      else if( aAssert.getType() == Assertion::ENSURE )
      {
         cerr  << "ENSURE";
      }
      else if( aAssert.getType() == Assertion::CHECK )
      {
         cerr  << "CHECK";
      }
      else 
      {
         cerr  << "ASSERT";
      }
      cerr << "( " << aAssert.getWhy() << " )";
   }

   cerr << endl;
}

void  handleException( ExceptionCref aExcp )
{
   cerr << aExcp.getFile() << ":" << aExcp.getLine() << ":" <<
      "Exception: " << aExcp.getWhy() << endl;
}

/*
   Common rcs information do not modify
   $Author: frankc $
   $Revision: 1.4 $
   $Date: 2000/09/07 12:56:24 $
   $Locker:  $
*/


