Leaked source code of windows server 2003
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

//-----------------------------------------------------------------------------
//
//
// 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();
}