You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
431 lines
13 KiB
431 lines
13 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// 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();
|
|
}
|