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.
 
 
 
 
 
 

827 lines
20 KiB

/*==========================================================================
*
* 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");
}