|
|
/*==========================================================================
* * Copyright (C) 2000-2002 Microsoft Corporation. All Rights Reserved. * * File: Cancel.cpp * Content: DirectNet Cancel Operations *@@BEGIN_MSINTERNAL * History: * Date By Reason * ==== == ====== * 04/07/00 mjn Created * 04/08/00 mjn Added DNCancelEnum(), DNCancelSend() * 04/11/00 mjn DNCancelEnum() uses CAsyncOp * 04/17/00 mjn DNCancelSend() uses CAsyncOp * 04/25/00 mjn Added DNCancelConnect() * 07/05/00 mjn Added code to handle invalid async ops * 07/08/00 mjn Fixed CAsyncOp to contain m_bilinkParent * 08/05/00 mjn Added DNCancelChildren(),DNCancelActiveCommands(),DNCanCancelCommand() * mjn Removed DNCancelEnum(),DNCancelListen(),DNCancelSend(),DNCancelConnect() * 08/07/00 mjn Added DNCancelRequestCommands() * 08/22/00 mjn Remove cancelled receive buffers from the active list in DNDoCancelCommand() * 09/02/00 mjn Cancel active commands in reverse order (to prevent out of order messages at protocol level) * 01/10/01 mjn Allow DNCancelActiveCommands() to set result code of cancelled commands * 02/08/01 mjn Use SyncEvents on AsyncOps to prevent protocol completions from returning before cancels return * mjn Added DNWaitForCancel() * 04/13/01 mjn DNCancelRequestCommands() uses request bilink * 05/23/01 mjn Only cancel commands that are allowed to be cancelled in DNDoCancelCommand() * 06/03/01 mjn Ignore uncancelable children in DNCancelChildren() *@@END_MSINTERNAL * ***************************************************************************/
#include "dncorei.h"
// DNCanCancelCommand
//
// This will determine if an operation is cancelable based on the selection flags
#undef DPF_MODNAME
#define DPF_MODNAME "DNCanCancelCommand"
BOOL DNCanCancelCommand(CAsyncOp *const pAsyncOp, const DWORD dwFlags, CConnection *const pConnection) { BOOL fReturnVal;
DPFX(DPFPREP, 8,"Parameters: pAsyncOp [0x%p], dwFlags [0x%lx], pConnection [0x%p]",pAsyncOp,dwFlags,pConnection);
DNASSERT(pAsyncOp != NULL);
fReturnVal = FALSE; switch(pAsyncOp->GetOpType()) { case ASYNC_OP_CONNECT: { if (dwFlags & DN_CANCEL_FLAG_CONNECT) { fReturnVal = TRUE; } break; } case ASYNC_OP_DISCONNECT: { if (dwFlags & DN_CANCEL_FLAG_DISCONNECT) { fReturnVal = TRUE; } break; } case ASYNC_OP_ENUM_QUERY: { if (dwFlags & DN_CANCEL_FLAG_ENUM_QUERY) { fReturnVal = TRUE; } break; } case ASYNC_OP_ENUM_RESPONSE: { if (dwFlags & DN_CANCEL_FLAG_ENUM_RESPONSE) { fReturnVal = TRUE; } break; } case ASYNC_OP_LISTEN: { if (dwFlags & DN_CANCEL_FLAG_LISTEN) { fReturnVal = TRUE; } break; } case ASYNC_OP_SEND: { if (pAsyncOp->IsInternal()) { if (dwFlags & DN_CANCEL_FLAG_INTERNAL_SEND) { fReturnVal = TRUE; } } else { if (dwFlags & DN_CANCEL_FLAG_USER_SEND) { if ((pConnection == NULL) || (pAsyncOp->GetConnection() == pConnection)) { if (pAsyncOp->GetOpFlags() & DN_SENDFLAGS_HIGH_PRIORITY) { if (! (dwFlags & DN_CANCEL_FLAG_USER_SEND_NOTHIGHPRI)) { fReturnVal = TRUE; } } else if (pAsyncOp->GetOpFlags() & DN_SENDFLAGS_HIGH_PRIORITY) { if (! (dwFlags & DN_CANCEL_FLAG_USER_SEND_NOTLOWPRI)) { fReturnVal = TRUE; } } else { if (! (dwFlags & DN_CANCEL_FLAG_USER_SEND_NOTNORMALPRI)) { fReturnVal = TRUE; } } } } } break; } case ASYNC_OP_RECEIVE_BUFFER: { if (dwFlags & DN_CANCEL_FLAG_RECEIVE_BUFFER) { fReturnVal = TRUE; } break; } case ASYNC_OP_REQUEST: { break; } #ifndef DPNBUILD_NOMULTICAST
case ASYNC_OP_LISTEN_MULTICAST: case ASYNC_OP_CONNECT_MULTICAST_SEND: { if (dwFlags & DN_CANCEL_FLAG_JOIN) { fReturnVal = TRUE; } break; } case ASYNC_OP_CONNECT_MULTICAST_RECEIVE: { break; } #endif // ! DPNBUILD_NOMULTICAST
default: { break; } } DPFX(DPFPREP, 8,"Returning: [%ld]",fReturnVal); return(fReturnVal); }
// DNDoCancelCommand
//
// This will attempt to cancel a given operation based on its OpType
#undef DPF_MODNAME
#define DPF_MODNAME "DNDoCancelCommand"
HRESULT DNDoCancelCommand(DIRECTNETOBJECT *const pdnObject, CAsyncOp *const pAsyncOp) { HRESULT hResultCode;
DPFX(DPFPREP, 8,"Parameters: pAsyncOp [0x%p]",pAsyncOp);
hResultCode = DPNERR_CANNOTCANCEL;
switch(pAsyncOp->GetOpType()) { case ASYNC_OP_CONNECT: case ASYNC_OP_ENUM_QUERY: case ASYNC_OP_ENUM_RESPONSE: case ASYNC_OP_LISTEN: case ASYNC_OP_SEND: #ifndef DPNBUILD_NOMULTICAST
case ASYNC_OP_LISTEN_MULTICAST: case ASYNC_OP_CONNECT_MULTICAST_SEND: case ASYNC_OP_CONNECT_MULTICAST_RECEIVE: #endif // ! DPNBUILD_NOMULTICAST
{ HANDLE hProtocol; BOOL fCanCancel;
DNASSERT(pdnObject->pdnProtocolData != NULL );
//
// If this operation has been marked as not cancellable,
// we will return an error
//
pAsyncOp->Lock(); hProtocol = pAsyncOp->GetProtocolHandle(); fCanCancel = !pAsyncOp->IsCannotCancel(); pAsyncOp->Unlock();
if (fCanCancel && (hProtocol != NULL)) { DPFX(DPFPREP, 9,"Attempting to cancel AsyncOp [0x%p]",pAsyncOp); hResultCode = DNPCancelCommand(pdnObject->pdnProtocolData,hProtocol); DPFX(DPFPREP, 9,"Result of cancelling AsyncOp [0x%p] was [0x%lx]",pAsyncOp,hResultCode); } else { DPFX(DPFPREP,9,"Not allowed to cancel this operation"); hResultCode = DPNERR_CANNOTCANCEL; } break; } case ASYNC_OP_RECEIVE_BUFFER: { hResultCode = pdnObject->HandleTable.Destroy( pAsyncOp->GetHandle(), NULL ); if (hResultCode == DPN_OK) { //
// Remove from active list
//
DNEnterCriticalSection(&pdnObject->csActiveList); pAsyncOp->m_bilinkActiveList.RemoveFromList(); DNLeaveCriticalSection(&pdnObject->csActiveList);
// Remove HandleTable reference
pAsyncOp->Release(); } else { hResultCode = DPNERR_CANNOTCANCEL; } break; } // case ASYNC_OP_DISCONNECT:
case ASYNC_OP_REQUEST: default: { DNASSERT(FALSE); break; } }
DPFX(DPFPREP, 8,"Returning: [0x%lx]",hResultCode); return(hResultCode); }
// DNCancelChildren
//
// This will mark an operation as CANCELLED to prevent new children from attaching,
// build a cancel list of any children, and recursively call itself to cancel those children.
// At the bottom level, if there is a Protocol handle, we will actually call DNPCancelCommand()
#undef DPF_MODNAME
#define DPF_MODNAME "DNCancelChildren"
HRESULT DNCancelChildren(DIRECTNETOBJECT *const pdnObject, CAsyncOp *const pParent) { HRESULT hResultCode; CBilink *pBilink; CAsyncOp *pAsyncOp; CAsyncOp *CancelList[16]; CSyncEvent *pSyncEvent; DWORD dwRemainingCount; DWORD dwCurrentCount; #ifdef DBG
DWORD dwInitialCount; #endif // DBG
DPFX(DPFPREP, 6,"Parameters: pParent [0x%p]",pParent);
DNASSERT(pdnObject != NULL); DNASSERT(pParent != NULL);
pAsyncOp = NULL; memset(CancelList, 0, sizeof(CancelList)); pSyncEvent = NULL;
//
// Mark the parent as cancelled so that no new children can attach
//
pParent->Lock(); if (pParent->IsCancelled() || pParent->IsComplete() || pParent->IsCannotCancel()) { pParent->Unlock(); DPFX(DPFPREP, 7,"Ignoring pParent [0x%p]",pParent); hResultCode = DPN_OK; goto Exit; } pParent->SetCancelled();
//
// Determine size of cancel list
//
dwRemainingCount = 0; pBilink = pParent->m_bilinkParent.GetNext(); while (pBilink != &pParent->m_bilinkParent) { pAsyncOp = CONTAINING_OBJECT(pBilink,CAsyncOp,m_bilinkChildren); pAsyncOp->Lock(); if (!pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete()) { dwRemainingCount++; } pAsyncOp->Unlock(); pBilink = pBilink->GetNext(); } DPFX(DPFPREP, 7,"Number of cancellable children [%ld]",dwRemainingCount);
//
// Attach a sync event if this is a protocol operation
// This event may be cleared by the completion
//
if (pParent->GetProtocolHandle() != NULL) { if ((hResultCode = SyncEventNew(pdnObject,&pSyncEvent)) != DPN_OK) { DPFERR("Could not get new sync event"); DisplayDNError(0,hResultCode); } else { pSyncEvent->Reset(); pParent->SetCancelEvent( pSyncEvent ); pParent->SetCancelThreadID( GetCurrentThreadId() ); DPFX(DPFPREP,7,"Setting sync event [0x%p]",pSyncEvent); } }
pParent->Unlock();
#ifdef DBG
dwInitialCount = dwRemainingCount; #endif // DBG
//
// Preset the return
//
hResultCode = DPN_OK;
//
// Fill cancel list
//
while (dwRemainingCount > 0) { dwRemainingCount = 0; dwCurrentCount = 0;
pParent->Lock();
pBilink = pParent->m_bilinkParent.GetNext(); while (pBilink != &pParent->m_bilinkParent) { pAsyncOp = CONTAINING_OBJECT(pBilink,CAsyncOp,m_bilinkChildren); pAsyncOp->Lock(); if (!pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete()) { if (dwCurrentCount < (sizeof(CancelList) / sizeof(CAsyncOp*))) { pAsyncOp->AddRef(); CancelList[dwCurrentCount] = pAsyncOp; dwCurrentCount++; #ifdef DBG
DNASSERT(dwCurrentCount <= dwInitialCount); #endif // DBG
} else { dwRemainingCount++;
//
// The list should never grow. In fact it should
// always be smaller because the current cancel list
// should have taken some.
//
#ifdef DBG
DNASSERT(dwRemainingCount < dwInitialCount); #endif // DBG
} } pAsyncOp->Unlock(); pBilink = pBilink->GetNext(); }
//
// Drop the lock while we attempt to cancel.
//
pParent->Unlock();
DPFX(DPFPREP, 7,"Actual number of cancellable children [%ld], remaining [%ld]",dwCurrentCount,dwRemainingCount);
//
// Call ourselves with each of the children (if there are any)
// and clean up (release AsyncOp children)
//
if (dwCurrentCount > 0) { DWORD dw; HRESULT hr;
for (dw = 0 ; dw < dwCurrentCount ; dw++ ) { hr = DNCancelChildren(pdnObject,CancelList[dw]); if ((hr != DPN_OK) && (hResultCode == DPN_OK)) { hResultCode = hr; } CancelList[dw]->Release(); CancelList[dw] = NULL; } } else { DNASSERT(dwRemainingCount == 0); } }
//
// Cancel this operation (if we can)
// This will only work for CONNECTs,DISCONNECTs,ENUM_QUERYs,ENUM_RESPONSEs,LISTENs,SENDs with a protocol handle
//
if (pParent->GetProtocolHandle() != NULL) { HRESULT hr;
hr = DNDoCancelCommand(pdnObject,pParent); if ((hr != DPN_OK) && (hResultCode == DPN_OK)) { hResultCode = hr; } }
//
// Set the cancel event and clear it from the async op if it's still there
//
if (pSyncEvent) { pSyncEvent->Set();
pParent->Lock(); pSyncEvent = pParent->GetCancelEvent(); pParent->SetCancelEvent( NULL ); pParent->Unlock();
if (pSyncEvent) { DPFX(DPFPREP,7,"Returning sync event [0x%p]",pSyncEvent); pSyncEvent->ReturnSelfToPool(); pSyncEvent = NULL; } }
Exit: DNASSERT( pSyncEvent == NULL );
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode); return(hResultCode); }
// DNCancelActiveCommands
//
// This will attempt to cancel ALL operations in the active list.
#undef DPF_MODNAME
#define DPF_MODNAME "DNCancelActiveCommands"
HRESULT DNCancelActiveCommands(DIRECTNETOBJECT *const pdnObject, const DWORD dwFlags, CConnection *const pConnection, const BOOL fSetResult, const HRESULT hrCancel) { HRESULT hResultCode; CAsyncOp *pAsyncOp; CAsyncOp *CancelList[64]; CBilink *pBilink; DWORD dwRemainingCount; DWORD dwCurrentCount;
DPFX(DPFPREP, 6,"Parameters: dwFlags [0x%lx], pConnection [0x%p], fSetResult [%ld], hrCancel [0x%lx]",dwFlags,pConnection,fSetResult,hrCancel);
DNASSERT(pdnObject != NULL); DNASSERT((pConnection == NULL) || (dwFlags & DN_CANCEL_FLAG_USER_SEND));
memset(CancelList, 0, sizeof(CancelList));
//
// Preset the return
//
hResultCode = DPN_OK;
//
// Create cancel list
//
do { dwRemainingCount = 0; dwCurrentCount = 0;
//
// Prevent changes
//
DNEnterCriticalSection(&pdnObject->csActiveList);
pBilink = pdnObject->m_bilinkActiveList.GetPrev(); while (pBilink != &pdnObject->m_bilinkActiveList) { pAsyncOp = CONTAINING_OBJECT(pBilink,CAsyncOp,m_bilinkActiveList); if (DNCanCancelCommand(pAsyncOp,dwFlags,pConnection)) { pAsyncOp->Lock(); if (!pAsyncOp->IsCancelled() && !pAsyncOp->IsComplete()) { if (dwCurrentCount < (sizeof(CancelList) / sizeof(CNameTableEntry*))) { pAsyncOp->AddRef(); CancelList[dwCurrentCount] = pAsyncOp; dwCurrentCount++; } else { dwRemainingCount++; } } pAsyncOp->Unlock(); } pBilink = pBilink->GetPrev(); }
//
// Allow changes, though the list should not grow any more here
//
DNLeaveCriticalSection(&pdnObject->csActiveList);
DPFX(DPFPREP, 7,"Number of cancellable ops [%ld], remaining [%ld]",dwCurrentCount,dwRemainingCount);
//
// Cancel each operation in the cancel list operation (if we can)
// This will only work for CONNECTs,DISCONNECTs,ENUM_QUERYs,ENUM_RESPONSEs,LISTENs,SENDs with a protocol handle
//
if (dwCurrentCount > 0) { DWORD dw; HRESULT hr; CSyncEvent *pSyncEvent;
pSyncEvent = NULL;
for (dw = 0 ; dw < dwCurrentCount ; dw++ ) { //
// Ensure operation has not already been cancelled
// If this is a protocol operation, we will add a sync event to prevent any completions from returning
// until we're done
//
DNASSERT( CancelList[dw] != NULL ); CancelList[dw]->Lock(); if (CancelList[dw]->IsCancelled() || CancelList[dw]->IsComplete()) { CancelList[dw]->Unlock(); CancelList[dw]->Release(); CancelList[dw] = NULL; continue; } if (CancelList[dw]->GetProtocolHandle() != NULL) { if ((hr = SyncEventNew(pdnObject,&pSyncEvent)) != DPN_OK) { DPFERR("Could not get sync event"); DisplayDNError(0,hr); } else { pSyncEvent->Reset(); CancelList[dw]->SetCancelEvent( pSyncEvent ); CancelList[dw]->SetCancelThreadID( GetCurrentThreadId() ); DPFX(DPFPREP,7,"Setting sync event [0x%p]",pSyncEvent); } } CancelList[dw]->SetCancelled(); CancelList[dw]->Unlock();
//
// Perform the actual cancel
//
hr = DNDoCancelCommand(pdnObject,CancelList[dw]); if ((hr != DPN_OK) && (hResultCode == DPN_OK)) { hResultCode = hr; }
//
// If this operation was cancelled and we need to set the result, we will
//
if ((hr == DPN_OK) && fSetResult) { CancelList[dw]->Lock(); CancelList[dw]->SetResult( hrCancel ); CancelList[dw]->Unlock(); }
//
// Set the cancel event and clear it from the async op if it's still there
//
if (pSyncEvent) { pSyncEvent->Set();
CancelList[dw]->Lock(); pSyncEvent = CancelList[dw]->GetCancelEvent(); CancelList[dw]->SetCancelEvent( NULL ); CancelList[dw]->Unlock();
if (pSyncEvent) { DPFX(DPFPREP,7,"Returning sync event [0x%p]",pSyncEvent); pSyncEvent->ReturnSelfToPool(); pSyncEvent = NULL; } }
CancelList[dw]->Release(); CancelList[dw] = NULL; }
DNASSERT(pSyncEvent == NULL); } } while (dwRemainingCount > 0);
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode); return(hResultCode); }
// DNCancelRequestCommands
//
// This will attempt to cancel REQUEST operations in the HandleTable.
// Requests have handles which are matched up against responses. Since these
// typically have SEND children (which may have completed and thus vanished),
// there is no guarantee these are not orphaned off in the HandleTable.
// We will look through the HandleTable for them and cancel them.
#undef DPF_MODNAME
#define DPF_MODNAME "DNCancelRequestCommands"
HRESULT DNCancelRequestCommands(DIRECTNETOBJECT *const pdnObject) { HRESULT hResultCode; CAsyncOp **RequestList; DWORD dwCount; DWORD dwActual; CBilink *pBilink;
DPFX(DPFPREP, 6,"Parameters: (none)");
DNASSERT(pdnObject != NULL);
RequestList = NULL;
dwCount = 0; dwActual = 0;
//
// Determine outstanding request list size and build it
//
DNEnterCriticalSection(&pdnObject->csActiveList); pBilink = pdnObject->m_bilinkRequestList.GetNext(); while (pBilink != &pdnObject->m_bilinkRequestList) { dwCount++; pBilink = pBilink->GetNext(); } if (dwCount > 0) { CAsyncOp *pAsyncOp;
if ((RequestList = static_cast<CAsyncOp**>(MemoryBlockAlloc(pdnObject,dwCount * sizeof(CAsyncOp*)))) == NULL) { DNLeaveCriticalSection(&pdnObject->csActiveList); DPFERR("Could not allocate request list"); hResultCode = DPNERR_OUTOFMEMORY; goto Failure; } pBilink = pdnObject->m_bilinkRequestList.GetNext(); while (pBilink != &pdnObject->m_bilinkRequestList) { pAsyncOp = CONTAINING_OBJECT(pBilink,CAsyncOp,m_bilinkActiveList); DNASSERT(dwActual < dwCount); DNASSERT(pAsyncOp->GetOpType() == ASYNC_OP_REQUEST); pAsyncOp->AddRef(); RequestList[dwActual] = pAsyncOp; pAsyncOp = NULL; dwActual++; pBilink = pBilink->GetNext(); } } DNLeaveCriticalSection(&pdnObject->csActiveList);
//
// Remove requests from request list and handle table
//
for (dwActual = 0 ; dwActual < dwCount ; dwActual++) { DNEnterCriticalSection(&pdnObject->csActiveList); RequestList[dwActual]->m_bilinkActiveList.RemoveFromList(); DNLeaveCriticalSection(&pdnObject->csActiveList);
RequestList[dwActual]->Lock(); RequestList[dwActual]->SetResult( DPNERR_USERCANCEL ); RequestList[dwActual]->Unlock(); if (SUCCEEDED(pdnObject->HandleTable.Destroy(RequestList[dwActual]->GetHandle(), NULL))) { // Release the HandleTable reference
RequestList[dwActual]->Release(); } RequestList[dwActual]->Release(); RequestList[dwActual] = NULL; }
//
// Clean up
//
if (RequestList) { MemoryBlockFree(pdnObject,RequestList); RequestList = NULL; } hResultCode = DPN_OK;
Exit: DNASSERT(RequestList == NULL);
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode); return(hResultCode);
Failure: if (RequestList) { MemoryBlockFree(pdnObject,RequestList); RequestList = NULL; } goto Exit; }
// DNWaitForCancel
//
// This will strip a cancel event off an async op if it exists, wait on it, and then return it to the pool
#undef DPF_MODNAME
#define DPF_MODNAME "DNWaitForCancel"
void DNWaitForCancel(CAsyncOp *const pAsyncOp) { DPFX(DPFPREP, 6,"Parameters: pAsyncOp [0x%p]",pAsyncOp);
CSyncEvent *pSyncEvent;
DNASSERT(pAsyncOp != NULL);
pSyncEvent = NULL;
//
// Get (and clear) sync event from async op
//
pAsyncOp->Lock(); pSyncEvent = pAsyncOp->GetCancelEvent(); if (pSyncEvent) { // Only pull the SyncEvent out if we are going to wait on it
if (pAsyncOp->GetCancelThreadID() == GetCurrentThreadId()) { // The other side of this will clean it up
DPFX(DPFPREP,7,"Cancel called on current thread - ignoring wait and continuing"); pSyncEvent = NULL; } else { // We are pulling it out, so we will clean it up
pAsyncOp->SetCancelEvent( NULL ); } } pAsyncOp->Unlock();
//
// If there was a sync event,
// - wait on it
// - return it to the pool
//
if (pSyncEvent) { DPFX(DPFPREP,7,"Waiting on sync event [0x%p]",pSyncEvent); pSyncEvent->WaitForEvent();
DPFX(DPFPREP,7,"Returning sync event [0x%p]",pSyncEvent); pSyncEvent->ReturnSelfToPool(); pSyncEvent = NULL; }
DNASSERT(pSyncEvent == NULL);
DPFX(DPFPREP, 6,"Returning"); }
|