text: //-----------------------------------------------------------------------------
  //
  //
  //  File: msgguid.cpp
  //
  //  Description: Implementation of AQMsgGuidList and CAQMsgGuidListEntry
  //      classes which provide the functionality to supersede outdated
  //      msg ID's.
  //
  //  Author: Mike Swafford (MikeSwa)
  //
  //  History:
  //      10/10/98 - MikeSwa Created 
  //
  //  Copyright (C) 1998 Microsoft Corporation
  //
  //-----------------------------------------------------------------------------
  
  #include "aqprecmp.h"
  #include "msgguid.h"
  
  CPool CAQMsgGuidListEntry::s_MsgGuidListEntryPool(MSGGUIDLIST_ENTRY_SIG);
  //
  //  A brief note about locks for thess classes.
  //
  //  The CAMsgGuidList* classes are protected by a single per-virtual server
  //  ShareLock (m_slPrivateData of course).  These locks are non-reentrant, so
  //  it is critical that we do not hold these locks while doing something that
  //  may cause a locking call back into us (like releasing a CMsgReference).
  //
  
  //---[ CAQMsgGuidListEntry::CAQMsgGuidListEntry ]------------------------------
  //
  //
  //  Description: 
  //      Constructor for CAQMsgGuidListEntry
  //  Parameters:
  //      pmsgref         Ptr to CMsgRef for this ID
  //      pguid           GUID ID of this message
  //      pliHead         Head of list to add to
  //      pmgl            List this entry belongs to
  //  Returns:
  //      -
  //  History:    
  //      10/10/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  CAQMsgGuidListEntry::CAQMsgGuidListEntry(CMsgRef *pmsgref, GUID *pguid, 
                                           PLIST_ENTRY pliHead, CAQMsgGuidList *pmgl) 
  {
      _ASSERT(pmsgref);
      _ASSERT(pguid);
      _ASSERT(pmgl);
      _ASSERT(pliHead);
  
      m_dwSignature = MSGGUIDLIST_ENTRY_SIG;
      m_pmsgref = pmsgref;
      m_pmsgref->AddRef();
      m_pmgl = pmgl;
  
      memcpy(&m_guidMsgID, pguid, sizeof(GUID));
  
      InsertHeadList(pliHead, &m_liMsgGuidList);
  }
  
  
  //---[ CAQMsgGuidListEntry::~CAQMsgGuidListEntry ]-----------------------------
  //
  //
  //  Description: 
  //      Destructor for CAQMsgGuidListEntry
  //  Parameters:
  //      -
  //  Returns:
  //      -
  //  History:
  //      10/10/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  CAQMsgGuidListEntry::~CAQMsgGuidListEntry()
  {
      //we should not still be in a list
      _ASSERT(!m_liMsgGuidList.Flink);
      _ASSERT(!m_liMsgGuidList.Blink);
  
      m_dwSignature = MSGGUIDLIST_ENTRY_SIG_INVALID;
  
      //It is safe to release the message ref here, since there is no way it 
      //can call back into use (unless there is a ref-counting bug).
      if (m_pmsgref)
          m_pmsgref->Release();
  
      m_pmgl = NULL;
  }
  
  //---[ CAQMsgGuidListEntry::pmgleGetEntry ]------------------------------------
  //
  //
  //  Description: 
  //      Static function to get entry from LIST_ENTRY
  //
  //      NOTE: inline function for use by CAQMsgGuidList only
  //  Parameters:
  //      pli         LIST ENTRY
  //  Returns:
  //      pointer to associated CAQMsgGuidListEntry
  //  History:
  //      10/10/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  CAQMsgGuidListEntry *CAQMsgGuidListEntry::pmgleGetEntry(PLIST_ENTRY pli)
  {
      _ASSERT(pli);
      CAQMsgGuidListEntry *pmgle = CONTAINING_RECORD(pli, 
                  CAQMsgGuidListEntry, m_liMsgGuidList);
      ASSERT(pmgle->m_dwSignature == MSGGUIDLIST_ENTRY_SIG);
      return pmgle;
  }
      
  //---[ CAQMsgGuidListEntry::fCompareGuid ]-------------------------------------
  //
  //
  //  Description: 
  //      Function used by CAQMsgGuidList to determine if this has a the GUID
  //      matching the superseded ID.
  //
  //      NOTE: inline function for use by CAQMsgGuidList only
  //  Parameters:
  //      pguid       GUID to check against
  //  Returns:
  //      TRUE if they match
  //      FALSE otherwise
  //  History:
  //      10/10/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  BOOL CAQMsgGuidListEntry::fCompareGuid(GUID *pguid)
  {
      _ASSERT(pguid);
      return (0 == memcmp(pguid, &m_guidMsgID, sizeof(GUID)));
  }
  
  //---[ CAQMsgGuidListEntry::RemoveFromList ]-----------------------------------
  //
  //
  //  Description: 
  //      Used by CMsgRef to remove an entry from the list once delivery is 
  //      complete.
  //  Parameters:
  //      -
  //  Returns:
  //      -
  //  History:
  //      10/10/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  void CAQMsgGuidListEntry::RemoveFromList()
  {
      _ASSERT(m_pmgl);
      m_pmgl->RemoveFromList(&m_liMsgGuidList);
  }
  
  //---[ CAQMsgGuidListEntry::pmsgrefGetAndClearMsgRef ]-------------------------
  //
  //
  //  Description: 
  //      First phase shutdown/deletion of object. Will set to NULL and return 
  //      orginal msgref pointer.  When caller releases lock, they should release
  //      the returned msgref.
  // 
  //      NOTE: Releasing the msgref while holding onto m_slPrivateData can 
  //      lead to a deadlock.  m_slPrivateData should be held while this 
  //  Parameters:
  //      -
  //  Returns:
  //      -
  //  History:
  //      10/10/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  CMsgRef *CAQMsgGuidListEntry::pmsgrefGetAndClearMsgRef()
  {
      CMsgRef *pmsgref = m_pmsgref;
      m_pmsgref = NULL;
      return pmsgref;
  }
  
  //---[ CAQMsgGuidListEntry::SupersedeMsg ]-------------------------------------
  //
  //
  //  Description: 
  //      Function to supersede msg associated with this object.  Will flag the
  //      associated CMsgRef as "non-deliverable"
  //
  //      NOTE: Should have MsgGuidList Write lock when calling
  //  Parameters:
  //      -
  //  Returns:
  //      -
  //  History:
  //      10/10/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  void CAQMsgGuidListEntry::SupersedeMsg()
  {
      m_pmsgref->SupersedeMsg();
      m_pmsgref->Release();
      m_pmsgref = NULL;
  }
  
  //---[ CAQMsgGuidList::CAQMsgGuidList ]-----------------------------------------
  //
  //
  //  Description: 
  //      Constructor for CAQMsgGuidList.
  //  Parameters:
  //      pcSupersededMsgs        Ptr to DWORD to InterlockedIncrement for
  //                              a count of superseded messages.
  //                              (can be NULL if no counters are wanted)
  //  Returns:
  //      -
  //  History:    
  //      10/11/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  CAQMsgGuidList::CAQMsgGuidList(DWORD *pcSupersededMsgs)
  {
      m_dwSignature = MSGGUIDLIST_SIG;
      m_pcSupersededMsgs = pcSupersededMsgs;
      InitializeListHead(&m_liMsgGuidListHead);
  }
  
  //---[ CAQMsgGuidList::~CAQMsgGuidList ]---------------------------------------
  //
  //
  //  Description: 
  //      Desctructor for CAQMsgGuidList
  //  Parameters:
  //      -
  //  Returns:
  //      -
  //  History:
  //      10/11/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  CAQMsgGuidList::~CAQMsgGuidList()
  {
      Deinitialize(NULL);
      _ASSERT(IsListEmpty(&m_liMsgGuidListHead));
  }
  
  //---[ CAQMsgGuidList::pmgleAddMsgGuid ]---------------------------------------
  //
  //
  //  Description: 
  //      Adds a message ID/Msg to the list of msg GUIDs.  Will also search for 
  //      the superseded msg GUID ID from the tail of the list.
  //
  //      This is meant as a server-side optimization.  There is *no* attempt
  //      to recover from out of memory situations.
  //  Parameters:
  //      pmsgref             MsgRef associated with this ID
  //      pguidID             GUID ID of this message
  //      pguidSuperseded     GUID ID of message superseded by this message
  //      
  //  Returns:
  //      Pointer to list entry for this msg (caller *must* Release)
  //      NULL if no entry allocated
  //  History:
  //      10/11/98 - MikeSwa Created 
  //      05/08/99 - MikeSwa Fixed AV 
  //
  //-----------------------------------------------------------------------------
  CAQMsgGuidListEntry *CAQMsgGuidList::pmgleAddMsgGuid(CMsgRef *pmsgref, 
                                                       GUID *pguidID, 
                                                       GUID *pguidSuperseded)
  {
      _ASSERT(pmsgref);
      _ASSERT(pguidID);
      CAQMsgGuidListEntry *pmgle = NULL;
      PLIST_ENTRY pliCurrent = NULL;
      CMsgRef *pmsgrefSuperseded = NULL;
  
      //First search list for matching GUID
      m_slPrivateData.ShareLock();
      pliCurrent = m_liMsgGuidListHead.Blink;
      while (pliCurrent && (pliCurrent != &m_liMsgGuidListHead))
      {
          pmgle = CAQMsgGuidListEntry::pmgleGetEntry(pliCurrent);
          if (pguidSuperseded && pmgle->fCompareGuid(pguidSuperseded))
          {
              //we found a match... addref it and stop looking
              pmgle->AddRef();
              break;
          }
  
          //NOTE: We may want to consider adding functionality that 
          //would allow us to supersede messages that are added to the 
          //system later... if some layer of abstaction (like the pickup dir)
          //causes out of order submission, this would allow us to handle
          //that case.  It could require:
          //  - Additional check of current ID against all superseded ID's (2x cost)
          //  - Additional storage of original superseded ID's.
  
          pmgle = NULL;
          pliCurrent = pliCurrent->Blink;
      }
      m_slPrivateData.ShareUnlock();
  
      m_slPrivateData.ExclusiveLock();
      if (pmgle)
      {
          //make sure someone else hasn't removed it from the list
          if (pliCurrent->Blink && pliCurrent->Flink)
          {
              //If we found a match supersede
              if (m_pcSupersededMsgs)
                  InterlockedIncrement((PLONG) m_pcSupersededMsgs);
              pmgle->SupersedeMsg();
              RemoveEntryList(pliCurrent);
              pliCurrent->Flink = NULL;
              pliCurrent->Blink = NULL;
  
              pmsgrefSuperseded = pmgle->pmsgrefGetAndClearMsgRef();
              //Release once for entry in list, and once for AddRef above
              _VERIFY(pmgle->Release());
              pmgle->Release();
          }
      }
  
      pmgle = new CAQMsgGuidListEntry(pmsgref, pguidID, &m_liMsgGuidListHead, this);
      if (pmgle)
          pmgle->AddRef();
  
      m_slPrivateData.ExclusiveUnlock();
  
      if (pmsgrefSuperseded)
          pmsgrefSuperseded->Release();
  
      return pmgle;
  }
  
  //---[ CAQMsgGuidList::Deinitialize ]------------------------------------------
  //
  //
  //  Description: 
  //      Walks list and released all msg id objects.  Calls server stop hint 
  //      function if provided.
  //  Parameters:
  //      painst      Ptr to virtual server object to call stop hint function
  //  Returns:
  //      -
  //  History:
  //      10/11/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  void CAQMsgGuidList::Deinitialize(CAQSvrInst *paqinst)
  {
      PLIST_ENTRY pliCurrent = NULL;
      CAQMsgGuidListEntry *pmgle = NULL;
      CMsgRef *pmsgref = NULL;
  
      m_slPrivateData.ExclusiveLock();
  
      //Walk entire list and release all objects
      while (!IsListEmpty(&m_liMsgGuidListHead))
      {
          pliCurrent = m_liMsgGuidListHead.Flink;
          _ASSERT(pliCurrent);
          pmgle = CAQMsgGuidListEntry::pmgleGetEntry(pliCurrent);
          _ASSERT(pmgle);
          RemoveEntryList(pliCurrent);
          pliCurrent->Flink = NULL;
          pliCurrent->Blink = NULL;
      
          //we must unlock to Deinitalize and release won't deadlock
          m_slPrivateData.ExclusiveUnlock();
          //Send shutdown hint
          if (paqinst)
              paqinst->ServerStopHintFunction();
  
          pmsgref = pmgle->pmsgrefGetAndClearMsgRef();
          if (pmsgref)
              pmsgref->Release();
  
          pmgle->Release();
  
          //Lock so we can check if list is empty
          m_slPrivateData.ExclusiveLock();
      }
      m_slPrivateData.ExclusiveUnlock();
  }
  
  //---[ CAQMsgGuidList::RemoveFromList ]----------------------------------------
  //
  //
  //  Description: 
  //      Used by a CAQMsgGuidListEntry to remove itself from the list in a 
  //      thread-safe manner.  The CAQMsgGuidListEntry is called by the CMsgRef
  //      when it is completely handled.
  //  Parameters:
  //      pli         PLIST_ENTRY to remove from list
  //  Returns:
  //      -
  //  History:
  //      10/11/98 - MikeSwa Created 
  //
  //-----------------------------------------------------------------------------
  void CAQMsgGuidList::RemoveFromList(PLIST_ENTRY pli)
  {
      _ASSERT(pli);
      CAQMsgGuidListEntry *pmgle = CAQMsgGuidListEntry::pmgleGetEntry(pli);
      CMsgRef *pmsgref = NULL;
      m_slPrivateData.ExclusiveLock();
  
      if (pli->Flink && pli->Blink)
      {
          //Only remove from list once
          RemoveEntryList(pli);
          pli->Flink = NULL;
          pli->Blink = NULL;
          pmsgref = pmgle->pmsgrefGetAndClearMsgRef();
          //Caller must still have reference
          _VERIFY(pmgle->Release());
      }
      m_slPrivateData.ExclusiveUnlock();
  
      //Do not release while holding lock
      if (pmsgref)
          pmsgref->Release();
  }
  