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

#if   !defined(__MUTEXSEMAPHORE_HPP)
#include <MutexSemaphore.hpp>
#endif

namespace corelinux
{
   static   SemaphoreIdentifier  findAny(-1);

   //
   // Basic group constructor with access rights specified
   //

   MutexSemaphoreGroup::MutexSemaphoreGroup( Short aSemCount, Int aRightSet )
      throw(Assertion,SemaphoreException)
      :
      SemaphoreGroup( aSemCount, aRightSet ),
      theUsedMap()
   {
      ;  // do nothing
   }

   //
   // Constructor where the identifier has been formed already
   //

   MutexSemaphoreGroup::MutexSemaphoreGroup
      ( 
         Short                         aSemCount, 
         SemaphoreGroupIdentifierCref  aGID ,
         Int                           aRightSet,
         CreateDisposition             disp
      ) throw(Assertion,SemaphoreException)
      :
      SemaphoreGroup( aSemCount, aGID, aRightSet, disp ),
      theUsedMap()
   {
      ;  // do nothing
   }

   //
   // Constructor to form a group
   //

   MutexSemaphoreGroup::MutexSemaphoreGroup
      ( 
         Short                aSemCount, 
         CharCptr             aName,
         Int                  aRightSet,
         CreateDisposition    disp
      ) throw(Assertion,SemaphoreException)
      :
      SemaphoreGroup( aSemCount+1, aName, aRightSet, disp ),
      theUsedMap()
   {
      ;  // do nothing
   }

   //
   // Destructor
   //

   MutexSemaphoreGroup::~MutexSemaphoreGroup( void )
   {
      SemaphoreSharesIterator begin( theUsedMap.begin() );
      SemaphoreSharesIterator end( theUsedMap.end() );

      // For each semaphore lingering

      for( ; begin != end; ++begin )
      {
         // For each reference not returned

         for( Int x = 0; x < (*begin).second.theCount; ++x )
         {
            SemaphoreCommon::relinquishSemaphore(this,(*begin).first);
         }

         // If there was one there, delete it

         if( (*begin).second.theCount )
         {
            ::delete (*begin).second.theSem;
         }
         else
         {
            ;  // do nothing
         }
      }

      theUsedMap.clear();
   }

   //
   // Creates a default semaphore. We need to lock our pages
   // if we are shared so that we only get a semaphore that
   // is not used cross process, otherwise we use our map
   //

   AbstractSemaphorePtr MutexSemaphoreGroup::createSemaphore( void )
                           throw( SemaphoreException ) 
   {
      return (this->resolveSemaphore(findAny,-1,FAIL_IF_EXISTS,true,false));
   }

   //
   //  Creates a default semaphore locked
   //

   AbstractSemaphorePtr MutexSemaphoreGroup::createLockedSemaphore
      ( 
         bool                    Recursive, 
         bool                    Balking 
      )  throw( SemaphoreException ) 
   {
      return (this->resolveSemaphore
              (
                 findAny,
                 -1,
                 FAIL_IF_EXISTS,
                 Recursive,
                 Balking,
                 true
               ));
   }

   //
   // Creates, or opens, a specific semaphore in the
   // group
   //

   AbstractSemaphorePtr MutexSemaphoreGroup::createSemaphore
   ( 
      SemaphoreIdentifierRef  aIdentifier,
      CreateDisposition       disp,
      bool                    Recursive, 
      bool                    Balking 
   ) throw( SemaphoreException ) 
   {

      Short   aSid( aIdentifier.getScalar() );

      if( aSid < 0 || aSid > getSemaphoreCount() )
      {
         throw SemaphoreException
            (
               "Invalid Semaphore identifier",
               LOCATION,
               Exception::CONTINUABLE
            );
      }
      else
      {
         ;  // do nothing
      }

      return (this->resolveSemaphore(aIdentifier,aSid,disp,Recursive,Balking));
   }

   //
   // Creates, or opens, a specific semaphore in the
   // group and either lock on create, or get lock
   //

   AbstractSemaphorePtr MutexSemaphoreGroup::createLockedSemaphore
   ( 
      SemaphoreIdentifierRef aIdentifier,
      CreateDisposition       disp,
      bool                    Recursive, 
      bool                    Balking 
   ) throw( SemaphoreException ) 
   {
      Short   aSid( aIdentifier.getScalar() );

      if( aSid < 0 || aSid > getSemaphoreCount() )
      {
         throw SemaphoreException
            (
               "Invalid Semaphore identifier",
               LOCATION,
               Exception::CONTINUABLE
            );
      }
      else
      {
         ;  // do nothing
      }

      return (this->resolveSemaphore(aIdentifier,aSid,disp,Recursive,Balking,true));
   }

   //
   // Creates or opens a specific named semaphore in the
   // group (not implemented)
   //

   AbstractSemaphorePtr MutexSemaphoreGroup::createSemaphore
   ( 
      std::string          aName,
      CreateDisposition    disp,
      bool                 Recursive, 
      bool                 Balking 
   ) throw( SemaphoreException ) 
   {
      AbstractSemaphorePtr aSem(NULLPTR);
      throw SemaphoreException
         (
            "Semaphore does not exist",
            LOCATION
         );
      return aSem;
   }

   //
   // Destroy the semaphore
   //

   void  MutexSemaphoreGroup::destroySemaphore( AbstractSemaphorePtr aPtr )
                     throw( SemaphoreException ) 
   {
      REQUIRE( aPtr != NULLPTR );

      GUARD;

      //
      // Insure that we are in the same arena
      //

      if( aPtr->getGroupIdentifier() == getIdentifier() )
      {
         //
         // Get the index, because thats what identifiers are,
         // and check before whacking space.
         //

         Index idx( aPtr->getIdentifier().getScalar() );
         SemaphoreSharesIterator  aFItr( theUsedMap.find(idx) );

         if( aFItr != theUsedMap.end() )
         {
            SemaphoreCommon::relinquishSemaphore(this,idx);

            (*aFItr).second.theCount -= 1;
            theUsedMap[idx].theCount -= 1;
            if( (*aFItr).second.theCount == 0 )
            {
               theUsedMap.erase(aFItr);
               ::delete aPtr;
            }
            else
            {
               ;  // not yet
            }
         }
         else
         {
            ;  // double delete exception MAYBE!!!
         }

      }
      else
      {
         throw SemaphoreException
            (
               "Semaphore not part of group",
               LOCATION
            );
      }

   }

   //
   // This is where the work belongs
   //

   AbstractSemaphorePtr MutexSemaphoreGroup::resolveSemaphore
      (
         SemaphoreIdentifierRef  aIdentifier,
         Short                   aSemId,
         CreateDisposition       aDisp,
         bool                    aRecurse, 
         bool                    aBalk,
         bool                    aAutoLock
      ) throw( SemaphoreException ) 
   {

      Guard    localGuard(this->access());

      //
      // Setup for operating in CSA or local
      //

      Int                  aRec(aRecurse);
      Int                  aBlk(aBalk);
      Int                  aMaxValue(1);
      Int                  aCmnRet(0);
      MutexSemaphorePtr    aSemPtr( NULLPTR );
      SemaphoreReference   aLocalShare = {0,NULLPTR};

      //
      // Let the CSA and our group decide behavior
      //

      aCmnRet = SemaphoreCommon::obtainSemaphore
         (
            this,
            aSemId,
            aMaxValue,
            aRec,
            aBlk,
            ( aDisp == FAIL_IF_EXISTS ? 0 :
               ( aDisp == FAIL_IF_NOTEXISTS ? 2 :1 ) )
         );

      // If the CSA has resolved it

      if( aCmnRet >= 0 )
      {
         // If we don't already have it, create it, otherwise
         // reuse the local instance

         SemaphoreSharesIterator  aFItr( theUsedMap.find(aCmnRet) );

         if( aFItr == theUsedMap.end() )
         {
            aSemPtr = ::new MutexSemaphore
                        ( 
                           this,
                           aIdentifier,
                           aAutoLock,
                           aRec,
                           aBlk
                        );

            aLocalShare.theCount = 1;
            aLocalShare.theSem = AbstractSemaphorePtr(aSemPtr);
            theUsedMap[aCmnRet] = aLocalShare;
         }
         else
         {
            (*aFItr).second.theCount += 1;
            aSemPtr = MutexSemaphorePtr(SemaphorePtr((*aFItr).second.theSem));

            if( aAutoLock == true )
            {
               localGuard.release();
               aSemPtr->lockWithWait();
            }
            else
            {
               ;  // do nothing
            }
         }
      }

      // Either we are in local mode, or there was an error in
      // constraints from the CSA

      else
      {
         SemaphoreException   exists("Semaphore exists exception",LOCATION);
         SemaphoreException   notexists("Semaphore does not exist",LOCATION);

         if( aCmnRet == -1 )
         {
            // If a specific one is being looked for

            if( aSemId >= 0 )
            {
               SemaphoreSharesIterator  aFItr( theUsedMap.find(aSemId) );

               //
               // If we find it
               //

               if( aFItr != theUsedMap.end() )
               {
                  //
                  // And the caller needs a unique instance
                  //

                  if( aDisp == FAIL_IF_EXISTS )
                  {
                     throw exists;
                  }
                  else
                  {
                     (*aFItr).second.theCount += 1;
                     aSemPtr = MutexSemaphorePtr(SemaphorePtr((*aFItr).second.theSem));
                     if( aAutoLock == true )
                     {
                        localGuard.release();
                        aSemPtr->lockWithWait();
                     }
                     else
                     {
                        ;  // do nothing
                     }
                  }
               }

               //
               // If we need to create it
               //

               else
               {
                  //
                  // And the user doesn't expect it to
                  // exist
                  //

                  if( aDisp != FAIL_IF_NOTEXISTS )
                  {
                     aSemPtr = ::new MutexSemaphore
                                 ( 
                                    this,
                                    aIdentifier,
                                    aAutoLock,
                                    aRec,
                                    aBlk
                                 );

                     aLocalShare.theCount = 1;
                     aLocalShare.theSem = AbstractSemaphorePtr(aSemPtr);
                     theUsedMap[aSemId] = aLocalShare;
                  }
                  else
                  {
                     throw notexists;
                  }
               }
            }

            // Otherwise, give what we can get

            else
            {
               Index maxCnt(this->getSemaphoreCount());

               for( Index x = 0; x < maxCnt; ++x )
               {
                  if( theUsedMap.find(x) == theUsedMap.end() )
                  {
                     aSemPtr = ::new MutexSemaphore
                                 ( 
                                    this,
                                    aIdentifier,
                                    aAutoLock,
                                    aRec,
                                    aBlk
                                 );

                     aLocalShare.theCount = 1;
                     aLocalShare.theSem = AbstractSemaphorePtr(aSemPtr);
                     theUsedMap[aSemId] = aLocalShare;
                     x = maxCnt;
                  }
                  else
                  {
                     ;  // do nothing
                  }
               }

               if( aSemPtr == NULLPTR )
               {
                  throw notexists;
               }
               else
               {
                  ;  // do nothing
               }

            }
         }

         //
         // Failure to resolve in the CSA
         //

         else
         {
            if( aDisp == FAIL_IF_EXISTS )
            {
               throw exists;
            }
            else
            {
               throw notexists;
            }
         }
      }

      return AbstractSemaphorePtr(aSemPtr);
   }
}

/*
   Common rcs information do not modify
   $Author: frankc $
   $Revision: 1.10 $
   $Date: 2000/06/06 12:04:12 $
   $Locker:  $
*/


