
/*
  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(__CORELINUXGUARDPOOL_HPP)
#include <CoreLinuxGuardPool.hpp>
#endif

#if   !defined(__SYNCHRONIZED_HPP)
#include <Synchronized.hpp>
#endif

#if   !defined(__CORELINUXGUARDGROUP_HPP)
#include <CoreLinuxGuardGroup.hpp>
#endif

#if   !defined(__GUARDSEMAPHORE_HPP)
#include <GuardSemaphore.hpp>
#endif

#if   !defined(__SEMAPHORECOMMON_HPP)
#include <SemaphoreCommon.hpp>
#endif

namespace corelinux
{
   GuardPool   CoreLinuxGuardPool::theGuard( new CoreLinuxGuardPool );
   Short       CoreLinuxGuardPool::theInitialSize(0);
   Short       CoreLinuxGuardPool::theExtentSize(0);

   //
   // Constructor
   //

   CoreLinuxGuardPool::CoreLinuxGuardPool( Short numInit, Short numExtent )
      throw( Assertion )
   {
      //
      // Initialize control and the pool objects with 
      // no references yet.
      //

      if( theGuard.instance() == NULLPTR )
      {
         ENSURE( numExtent >= 0 );
         ENSURE( numInit >= 1 );

         //
         // Clear out
         //

         theGroups.clear();
         theSemaphores.clear();
         theCallers.clear();

         //
         // Create the base pool and add one for our control
         //

         createPoolGroup( numInit+1, numInit );

         //
         // Get ourselves some protection from the extra we added to the
         // base group
         //

         SemaphoreIdentifier  aSemId(numInit);
         theControlSem = theGroups[0]->createSemaphore( aSemId );

         theInitialSize = numInit;
         theExtentSize = numExtent;
      }
      else
      {
         NEVER_GET_HERE;
      }
   }

   //
   // Destructor
   //

   CoreLinuxGuardPool::~CoreLinuxGuardPool( void )
   {
      //
      // First destroy our extra
      //

      theGroups[0]->destroySemaphore(theControlSem);
      theControlSem = NULLPTR;
      theCallers.clear();

      SemaphoreCommon::exitAttachment();

      //
      // Remove all other sempahores
      //

      for(  SemaphoreMapIterator aItr=theSemaphores.begin();
          aItr != theSemaphores.end(); ++aItr )
      {
         theGroups[(*aItr).second.theGroupIndex]->destroySemaphore
            ( 
               (*aItr).first 
            );
      }

      theSemaphores.clear();

      //
      // Remove all groups
      //

      for( GroupVectorIterator aGItr = theGroups.begin();
          aGItr != theGroups.end(); ++ aGItr )
      {
         delete (*aGItr);
      }

      theGroups.clear();

   }

   //
   // Static is locked for guards
   //

   bool  CoreLinuxGuardPool::isLocked( SynchronizedPtr aPtr )
            throw(SemaphoreException)
   {
      bool  il( false );

      if( theGuard.instance() != NULLPTR )
      {
        il = theGuard.instance()->isSynchronizedLocked( aPtr );
      }
      else
      {
         throw SemaphoreException( "Pool not initialized", LOCATION );
      }

      return il;
   }

   //
   // Get the initial size
   //

   Short CoreLinuxGuardPool::getInitialPoolSize( void )
   {
      return theInitialSize;
   }

   //
   // Get the current extent size
   //

   Short CoreLinuxGuardPool::getExtentSize( void )
   {
      return theExtentSize;
   }

   //
   // Return the current guard pool size
   //

   Short CoreLinuxGuardPool::getTotalCurrentSize( void )
   {
      return getInitialPoolSize() + getExtentSize() ;
   }

   //
   // Static lock for guards
   //

   void  CoreLinuxGuardPool::lock( SynchronizedPtr aPtr ) 
            throw(SemaphoreException)
   {
      if( theGuard.instance() != NULLPTR )
      {
         theGuard.instance()->lockSynchronized( aPtr );
      }
      else
      {
         throw SemaphoreException( "Pool not initialized", LOCATION );
      }
   }

   //
   // Static release for guards
   //

   void  CoreLinuxGuardPool::release( SynchronizedPtr aPtr ) 
            throw(SemaphoreException)
   {
      if( theGuard.instance() != NULLPTR )
      {
         theGuard.instance()->releaseSynchronized( aPtr );
      }
      else
      {
         throw SemaphoreException( "Pool not initialized", LOCATION );
      }

   }

   //
   // Pool instance test for lock state
   //

   bool  CoreLinuxGuardPool::isSynchronizedLocked( SynchronizedPtr aPtr ) 
            throw(SemaphoreException)
   {
      theControlSem->lockWithWait();
      MonitorMapIterator   aCItr( theCallers.find(aPtr) );
      theControlSem->release();
      return !( aCItr == theCallers.end() );
   }

   //
   // Pool instance lock management
   //

   void  CoreLinuxGuardPool::lockSynchronized( SynchronizedPtr aPtr ) 
            throw(SemaphoreException)
   {
      theControlSem->lockWithWait();

      MonitorMapIterator   aCItr( theCallers.find(aPtr) );

      //
      // First see if we are in the list, and if we are
      // we reuse the same semaphore (increment its count)
      // and lock it.
      //

      if( aCItr != theCallers.end() )
      {
         SemaphoreMapIterator aSItr( theSemaphores.find((*aCItr).second) );
         (*aSItr).second.theQueueLength += 1;
         theControlSem->release();
         (*aSItr).first->lockWithWait();
      }

      //
      // Otherwise we don't have one mapped so we occupy one
      //

      else
      {
         AbstractSemaphorePtr aSem( NULLPTR );
         SemaphoreMapIterator begin( theSemaphores.begin() );

         while( begin != theSemaphores.end() )
         {
            if( (*begin).second.theQueueLength == 0 )
            {
               aSem = (*begin).first;
               (*begin).second.theQueueLength = 1;
               theCallers[aPtr] = aSem;
               begin = theSemaphores.end();
            }
            else
            {
               ++begin;
            }
         }

         //
         // If we have one then lock it
         //

         if( aSem != NULLPTR )
         {
            theControlSem->release();
            aSem->lockWithWait();
         }

         //
         // Degenerate case is we need to go into extents
         // or crash and burn the attempt
         //

         else
         {
            //
            // If we can go into extents then do so,
            // otherwise raise exception
            //

            if( CoreLinuxGuardPool::getExtentSize() != 0 )
            {
               createPoolGroup( CoreLinuxGuardPool::getExtentSize() );
               theControlSem->release();
               this->lockSynchronized( aPtr );
            }
            else
            {
               theControlSem->release();
               throw SemaphoreException( "Pool exhausted", LOCATION );
            }
         }
      }
   }

   //
   // Pool instance release management
   //

   void  CoreLinuxGuardPool::releaseSynchronized( SynchronizedPtr aPtr ) 
            throw(SemaphoreException)
   {
      theControlSem->lockWithWait();
      MonitorMapIterator   aCItr( theCallers.find(aPtr) );

      if( aCItr != theCallers.end() )
      {
         PoolDescriptor aDes;

         //
         // Retrieve the reference from the semaphores, reduce the
         // count of references
         //

         SemaphoreMapIterator aSItr( theSemaphores.find((*aCItr).second) );
         aDes = (*aSItr).second;
         aDes.theQueueLength = (*aSItr).second.theQueueLength -= 1;
         (*aCItr).second->release();

         //
         // Remove the caller if no more references to semaphore
         //

         if( aDes.theQueueLength == 0 )
         {
            theCallers.erase(aCItr);

            //
            //  And check to see if semaphore is in last extent.
            //

            if( aDes.theGroupIndex != 0 &&
                aDes.theGroupIndex == theGroups.size() - 1 )
            {
               destroyPoolGroup( aDes.theGroupIndex );
            }
            else
            {
               ;  // do nothing
            }
         }
         else
         {
            ;  // do nothing
         }
         theControlSem->release();
      }
      else
      {
         theControlSem->release();
         throw SemaphoreException("Caller not found",LOCATION);
      }

   }


   //
   // Create pool and initialize references
   //

   void  CoreLinuxGuardPool::createPoolGroup( Short numSems, Short initSize )
   {
      Index    poolIndex( theGroups.size() );

      initSize = ( !initSize ? numSems : initSize );

      CoreLinuxGuardGroupPtr  aGp
         ( new CoreLinuxGuardGroup(numSems) );

      theGroups.push_back( aGp );

      for( Short x=0; x < initSize; ++x )
      {
         SemaphoreIdentifier  aSemId(x);
         PoolDescriptor aDes = {0,poolIndex};
         theSemaphores[aGp->createSemaphore( aSemId )] = aDes;
      }
   }

   //
   // Destroy a group and all its semaphores. This method assumes
   // that this is the last group in the vector.
   //


   void  CoreLinuxGuardPool::destroyPoolGroup( Index aGroup )
   {
      //
      // Track those that are in the group
      //

      std::vector<AbstractSemaphorePtr>  dVector;
      Count                         hits(0);

      dVector.clear();

      for( SemaphoreMapIterator aSItr = theSemaphores.begin() ;
          aSItr != theSemaphores.end(); ++aSItr )
      {
         if( (*aSItr).second.theGroupIndex == aGroup )
         {
            ++hits;

            if( (*aSItr).second.theQueueLength == 0 )
            {
               dVector.push_back((*aSItr).first);
            }
            else
            {
               aSItr == theSemaphores.end();
               --aSItr;
            }
         }
         else
         {
            ;  // do nothing
         }
      }

      //
      // If we have a clear group
      //

      if( hits == dVector.size() )
      {
         for( Count x = 0; x < hits; ++x )
         {
            theSemaphores.erase( dVector[x] );
            theGroups[aGroup]->destroySemaphore(dVector[x]);
         }
         delete theGroups[aGroup];
         theGroups.pop_back();
      }
      else
      {
         ;  // do nothing
      }

      dVector.clear();
   }
}

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

