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

#if   !defined(__EVENTSEMAPHORE_HPP)
#include <EventSemaphore.hpp>
#endif

namespace corelinux
{
   /// default maximum number of listeners is "infinity"

   const    Int DEFAULT_COUNT(-1);
   static   SemaphoreIdentifier  findAny(-1);

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

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

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

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

   //
   // Constructor to form a group
   //

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

   //
   // Destructor
   //

   EventSemaphoreGroup::~EventSemaphoreGroup( 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
   //

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

   //
   // Creates a default EventSemaphore with max listener count
   //

   AbstractSemaphorePtr EventSemaphoreGroup::createSemaphore( Counter aLimit )
                           throw( SemaphoreException ) 
   {
      return this->resolveSemaphore
         (
             findAny, 
             -1, 
             FAIL_IF_EXISTS, 
             false,
             false, 
             aLimit 
         );
   }

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

   AbstractSemaphorePtr EventSemaphoreGroup::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, DEFAULT_COUNT);
   }

   //
   // Creates, or opens, a specific semaphore in the
   // group and initialize the limit of listeners
   //

   AbstractSemaphorePtr EventSemaphoreGroup::createSemaphore
      ( 
         SemaphoreIdentifierRef aIdentifier,
         Counter                aLimit,
         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, aLimit );
   }

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

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

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

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

   //
   // Destroy the semaphore
   //

   void  EventSemaphoreGroup::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 EventSemaphoreGroup::resolveSemaphore
      (
         SemaphoreIdentifierRef  aIdentifier,
         Short                   aSemId,
         CreateDisposition       aDisp,
         bool                    aRecurse, 
         bool                    aBalk,
         Counter                 aLimit
      ) throw( SemaphoreException ) 
   {

      Guard    localGuard(this->access());

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

      Int                  aRec(aRecurse);
      Int                  aBlk(aBalk);
      Int                  aMaxValue(aLimit);
      Int                  aValue(0);
      Int                  aCmnRet(0);
      EventSemaphorePtr    aSemPtr( NULLPTR );
      SemaphoreReference   aLocalShare = {0,NULLPTR};

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

      aCmnRet = SemaphoreCommon::obtainSemaphore
         (
            this, aSemId, aValue, 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 EventSemaphore
                        ( 
                           this,
                           aIdentifier,
                           aMaxValue,
                           aRec,
                           aBlk
                        );

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

      // 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 = EventSemaphorePtr(SemaphorePtr((*aFItr).second.theSem));
                  }
               }

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

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

                  if( aDisp != FAIL_IF_NOTEXISTS )
                  {
                     aSemPtr = ::new EventSemaphore
                                 ( 
                                    this,
                                    aIdentifier,
                                    aMaxValue,
                                    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 EventSemaphore
                                 ( 
                                    this,
                                    aIdentifier,
                                    aMaxValue,
                                    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: dulimart $
   $Revision: 1.3 $
   $Date: 2000/09/20 18:47:38 $
   $Locker:  $
*/



