571 lines
17 KiB
C++
571 lines
17 KiB
C++
|
/*==========================================================================
|
||
|
*
|
||
|
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
|
||
|
*
|
||
|
* File: Initialize.cpp
|
||
|
* Content: This file contains code to both initialize and shutdown the
|
||
|
* protocol, as well as to Add and Remove service providers
|
||
|
*
|
||
|
* History:
|
||
|
* Date By Reason
|
||
|
* ==== == ======
|
||
|
* 11/06/98 ejs Created
|
||
|
* 07/01/2000 masonb Assumed Ownership
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "dnproti.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
** GLOBAL VARIABLES
|
||
|
**
|
||
|
** There are two kinds of global variables. Instance specific globals
|
||
|
** (not really global, i know) which are members of the ProtocolData structure,
|
||
|
** and true globals which are shared among all instances. The following
|
||
|
** definitions are true globals, such as FixedPools and Timers.
|
||
|
*/
|
||
|
|
||
|
LPFPOOL ChkPtPool = NULL; // Pool of CheckPoint data structure
|
||
|
LPFPOOL EPDPool = NULL; // Pool of End Point descriptors
|
||
|
LPFPOOL MSDPool = NULL; // Pool of Message Descriptors
|
||
|
LPFPOOL FMDPool = NULL; // Pool of Frame Descriptors
|
||
|
LPFPOOL RCDPool = NULL; // Pool of Receive Descriptors
|
||
|
|
||
|
LPFPOOL BufPool = NULL; // Pool of buffers to store rcvd frames
|
||
|
LPFPOOL MedBufPool = NULL;
|
||
|
LPFPOOL BigBufPool = NULL;
|
||
|
|
||
|
LONG g_lProtocolObjects = 0;
|
||
|
DNCRITICAL_SECTION g_csProtocolGlobal;
|
||
|
|
||
|
BOOL g_fTimerInited = FALSE;
|
||
|
|
||
|
/*
|
||
|
** Pools Initialization
|
||
|
**
|
||
|
** This procedure should be called once at Dll load
|
||
|
*/
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNPPoolsInit"
|
||
|
|
||
|
BOOL DNPPoolsInit()
|
||
|
{
|
||
|
DPFX(DPFPREP,DPF_CALLIN_LVL, "Enter");
|
||
|
|
||
|
if (DNInitializeCriticalSection(&g_csProtocolGlobal) == FALSE)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((ChkPtPool = FPM_Create(sizeof(CHKPT), NULL, NULL, NULL, NULL)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((EPDPool = FPM_Create(sizeof(EPD), EPD_Allocate, EPD_Get, EPD_Release, EPD_Free)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((MSDPool = FPM_Create(sizeof(MSD), MSD_Allocate, MSD_Get, MSD_Release, MSD_Free)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((FMDPool = FPM_Create(sizeof(FMD), FMD_Allocate, FMD_Get, FMD_Release, FMD_Free)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((RCDPool = FPM_Create(sizeof(RCD), RCD_Allocate, RCD_Get, RCD_Release, RCD_Free)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((BufPool = FPM_Create(sizeof(BUF), Buf_Allocate, Buf_Get, NULL, NULL)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((MedBufPool = FPM_Create(sizeof(MEDBUF), Buf_Allocate, Buf_GetMed, NULL, NULL)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if((BigBufPool = FPM_Create(sizeof(BIGBUF), Buf_Allocate, Buf_GetBig, NULL, NULL)) == NULL)
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (FAILED(TimerInit()))
|
||
|
{
|
||
|
DNPPoolsDeinit();
|
||
|
return FALSE;
|
||
|
}
|
||
|
g_fTimerInited = TRUE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Pools Deinitialization
|
||
|
**
|
||
|
** This procedure should be called by DllMain at shutdown time
|
||
|
*/
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNPPoolsDeinit"
|
||
|
|
||
|
void DNPPoolsDeinit()
|
||
|
{
|
||
|
DPFX(DPFPREP,DPF_CALLIN_LVL, "Enter");
|
||
|
|
||
|
if (g_fTimerInited)
|
||
|
{
|
||
|
TimerDeinit();
|
||
|
g_fTimerInited = FALSE;
|
||
|
}
|
||
|
|
||
|
DNDeleteCriticalSection(&g_csProtocolGlobal);
|
||
|
|
||
|
if(ChkPtPool != NULL)
|
||
|
{
|
||
|
ChkPtPool->Fini(ChkPtPool);
|
||
|
ChkPtPool = NULL;
|
||
|
}
|
||
|
if(EPDPool != NULL)
|
||
|
{
|
||
|
EPDPool->Fini(EPDPool);
|
||
|
EPDPool = NULL;
|
||
|
}
|
||
|
if(MSDPool != NULL)
|
||
|
{
|
||
|
MSDPool->Fini(MSDPool);
|
||
|
MSDPool = NULL;
|
||
|
}
|
||
|
if(FMDPool != NULL){
|
||
|
FMDPool->Fini(FMDPool);
|
||
|
FMDPool = NULL;
|
||
|
}
|
||
|
if(RCDPool != NULL)
|
||
|
{
|
||
|
RCDPool->Fini(RCDPool);
|
||
|
RCDPool = NULL;
|
||
|
}
|
||
|
if(BufPool != NULL)
|
||
|
{
|
||
|
BufPool->Fini(BufPool);
|
||
|
BufPool = NULL;
|
||
|
}
|
||
|
if(MedBufPool != NULL)
|
||
|
{
|
||
|
MedBufPool->Fini(MedBufPool);
|
||
|
MedBufPool = NULL;
|
||
|
}
|
||
|
if(BigBufPool != NULL)
|
||
|
{
|
||
|
BigBufPool->Fini(BigBufPool);
|
||
|
BigBufPool = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** Protocol Initialize
|
||
|
**
|
||
|
** This procedure should be called by DirectPlay at startup time before
|
||
|
** any other calls in the protocol are made.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNPProtocolInitialize"
|
||
|
|
||
|
HRESULT DNPProtocolInitialize(PVOID pCoreContext, PProtocolData pPData, PDN_PROTOCOL_INTERFACE_VTBL pVtbl)
|
||
|
{
|
||
|
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pCoreContext[%p], pPData[%p], pVtbl[%p]", pCoreContext, pPData, pVtbl);
|
||
|
|
||
|
// DPFX(DPFPREP,0, "Sizes: endpointdesc[%d], framedesc[%d], messagedesc[%d], protocoldata[%d], recvdesc[%d], spdesc[%d], _MyTimer[%d]", sizeof(endpointdesc), sizeof(framedesc), sizeof(messagedesc), sizeof(protocoldata), sizeof(recvdesc), sizeof(spdesc), sizeof(_MyTimer));
|
||
|
|
||
|
DNEnterCriticalSection(&g_csProtocolGlobal);
|
||
|
g_lProtocolObjects++;
|
||
|
if (g_lProtocolObjects == 1)
|
||
|
{
|
||
|
// We are the first, create everything
|
||
|
DPFX(DPFPREP,5, "Initializing timers");
|
||
|
if (FAILED(InitTimerWorkaround()))
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "Protocol timer package failed to initialize");
|
||
|
g_lProtocolObjects--;
|
||
|
DNLeaveCriticalSection(&g_csProtocolGlobal);
|
||
|
return DPNERR_GENERIC;
|
||
|
}
|
||
|
}
|
||
|
DNLeaveCriticalSection(&g_csProtocolGlobal);
|
||
|
|
||
|
pPData->ulProtocolFlags = 0;
|
||
|
pPData->Parent = pCoreContext;
|
||
|
pPData->pfVtbl = pVtbl;
|
||
|
pPData->Sign = PD_SIGN;
|
||
|
|
||
|
pPData->lSPActiveCount = 0;
|
||
|
|
||
|
srand(GETTIMESTAMP());
|
||
|
pPData->dwNextSessID = rand() | (rand() << 16); // build a 32 bit value out of two 16 bit values
|
||
|
|
||
|
pPData->tIdleThreshhold = DEFAULT_KEEPALIVE_INTERVAL; // 60 second keep-alive interval
|
||
|
pPData->dwConnectTimeout = CONNECT_DEFAULT_TIMEOUT;
|
||
|
pPData->dwConnectRetries = CONNECT_DEFAULT_RETRIES;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
pPData->ThreadsInReceive = 0;
|
||
|
pPData->BuffersInReceive = 0;
|
||
|
#endif
|
||
|
|
||
|
pPData->ulProtocolFlags |= PFLAGS_PROTOCOL_INITIALIZED;
|
||
|
|
||
|
return DPN_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Protocol Shutdown
|
||
|
**
|
||
|
** This procedure should be called at termination time, and should be the
|
||
|
** last call made to the protocol.
|
||
|
**
|
||
|
** All SPs should have been removed prior to this call which in turn means
|
||
|
** that we should not have any sends pending in a lower layer.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNPProtocolShutdown"
|
||
|
|
||
|
HRESULT DNPProtocolShutdown(PProtocolData pPData)
|
||
|
{
|
||
|
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p]", pPData);
|
||
|
|
||
|
DNEnterCriticalSection(&g_csProtocolGlobal);
|
||
|
g_lProtocolObjects--;
|
||
|
if (g_lProtocolObjects == 0)
|
||
|
{
|
||
|
// We are the last, destroy everything
|
||
|
DPFX(DPFPREP,5, "Destroying timers");
|
||
|
FiniTimerWorkaround();
|
||
|
}
|
||
|
DNLeaveCriticalSection(&g_csProtocolGlobal);
|
||
|
|
||
|
if(pPData->lSPActiveCount != 0)
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "Returning DPNERR_INVALIDCOMMAND - There are still active SPs, DNPRemoveSP wasn't called");
|
||
|
return DPNERR_INVALIDCOMMAND; // Must remove Service Providers first
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
if (pPData->BuffersInReceive != 0)
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "*** %d receive buffers were leaked", pPData->BuffersInReceive);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pPData->ulProtocolFlags = 0;
|
||
|
|
||
|
return DPN_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Add Service Provider
|
||
|
**
|
||
|
** This procedure is called by Direct Play to bind us to a service provider.
|
||
|
** We can bind up to 256 service providers at one time, although I would not ever
|
||
|
** expect to do so. This procedure will fail if Protocol Initialize has not
|
||
|
** been called.
|
||
|
**
|
||
|
**
|
||
|
** We check the size of the SP table to make sure we have a slot free. If table
|
||
|
** is full we double the table size until we reach maximum size. If table cannot grow
|
||
|
** then we fail the AddServiceProvider call.
|
||
|
*/
|
||
|
|
||
|
extern IDP8SPCallbackVtbl DNPLowerEdgeVtbl;
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNPAddServiceProvider"
|
||
|
|
||
|
HRESULT DNPAddServiceProvider(PProtocolData pPData, IDP8ServiceProvider *pISP, HANDLE *pContext)
|
||
|
{
|
||
|
PSPD pSPD=0;
|
||
|
SPINITIALIZEDATA SPInitData;
|
||
|
SPGETCAPSDATA SPCapsData;
|
||
|
HRESULT hr;
|
||
|
|
||
|
*pContext = NULL;
|
||
|
|
||
|
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], pISP[%p], pContext[%p]", pPData, pISP, pContext);
|
||
|
|
||
|
if(pPData->ulProtocolFlags & PFLAGS_PROTOCOL_INITIALIZED)
|
||
|
{
|
||
|
if ((pSPD = (PSPD)DNMalloc(sizeof(SPD))) == NULL)
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - couldn't allocate SP Descriptor");
|
||
|
return DPNERR_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// MAKE THE INITIALIZE CALL TO THE Service Provider... give him our Object
|
||
|
|
||
|
memset(pSPD, 0, sizeof(SPD)); // init to zero
|
||
|
|
||
|
pSPD->LowerEdgeVtable = &DNPLowerEdgeVtbl; // Put Vtbl into the interface Object
|
||
|
pSPD->Sign = SPD_SIGN;
|
||
|
|
||
|
SPInitData.pIDP = (IDP8SPCallback *) pSPD;
|
||
|
SPInitData.dwFlags = 0;
|
||
|
|
||
|
if (DNInitializeCriticalSection(&pSPD->SPLock) == FALSE)
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - couldn't initialize SP CS, pSPD[%p]", pSPD);
|
||
|
DNFree(pSPD);
|
||
|
return DPNERR_OUTOFMEMORY;
|
||
|
}
|
||
|
DebugSetCriticalSectionRecursionCount(&pSPD->SPLock, 0);
|
||
|
|
||
|
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Initialize, pSPD[%p]", pSPD);
|
||
|
if((hr = IDP8ServiceProvider_Initialize(pISP, &SPInitData)) != DPN_OK)
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "Returning hr=%x - SP->Initialize failed, pSPD[%p]", hr, pSPD);
|
||
|
DNDeleteCriticalSection(&pSPD->SPLock);
|
||
|
DNFree(pSPD);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
pSPD->blSendQueue.Initialize();
|
||
|
pSPD->blPendingQueue.Initialize();
|
||
|
pSPD->blEPDActiveList.Initialize();
|
||
|
#ifdef DEBUG
|
||
|
pSPD->blMessageList.Initialize();
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// MAKE THE SP GET CAPS CALL TO FIND FRAMESIZE AND LINKSPEED
|
||
|
|
||
|
SPCapsData.dwSize = sizeof(SPCapsData);
|
||
|
SPCapsData.hEndpoint = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->GetCaps, pSPD[%p]", pSPD);
|
||
|
if((hr = IDP8ServiceProvider_GetCaps(pISP, &SPCapsData)) != DPN_OK)
|
||
|
{
|
||
|
DPFX(DPFPREP,DPF_CALLOUT_LVL, "SP->GetCaps failed - hr[%x], Calling SP->Close, pSPD[%p]", hr, pSPD);
|
||
|
IDP8ServiceProvider_Close(pISP);
|
||
|
DNDeleteCriticalSection(&pSPD->SPLock);
|
||
|
|
||
|
DPFX(DPFPREP,0, "Returning hr=%x - SP->GetCaps failed, pSPD[%p]", hr, pSPD);
|
||
|
|
||
|
DNFree(pSPD);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
pSPD->uiLinkSpeed = SPCapsData.dwLocalLinkSpeed;
|
||
|
pSPD->uiFrameLength = SPCapsData.dwUserFrameSize;
|
||
|
pSPD->uiUserFrameLength = pSPD->uiFrameLength - DNP_MAX_HEADER_SIZE;
|
||
|
|
||
|
// Place new SP in table
|
||
|
|
||
|
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->AddRef, pSPD[%p]", pSPD);
|
||
|
IDP8ServiceProvider_AddRef(pISP);
|
||
|
pSPD->IISPIntf = pISP;
|
||
|
pSPD->pPData = pPData;
|
||
|
InterlockedIncrement(&pPData->lSPActiveCount);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "Returning DPNERR_UNINITIALIZED - DNPProtocolInitialize has not been called");
|
||
|
return DPNERR_UNINITIALIZED;
|
||
|
}
|
||
|
|
||
|
*pContext = pSPD;
|
||
|
|
||
|
return DPN_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Remove Service Provider
|
||
|
**
|
||
|
** It is higher layer's responsibility to make sure that there are no pending commands
|
||
|
** when this function is called, although we can do a certain amount of cleanup ourselves.
|
||
|
** For the moment will we ASSERT that everything is in fact finished up.
|
||
|
**
|
||
|
** SPTable stuff... Since we use the table slot as the SP identiier, we must not compact
|
||
|
** the SP table upon removal. Therefore, we must have a way of validating the SP index, and
|
||
|
** we may not want to re-use table slots after an SP is removed. Since we have virtually
|
||
|
** unlimited space in the table, and SPs are generally not intended to be transitory, its
|
||
|
** probably safe to invalidate the old table slot and just keep increasing the IDs.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DNPRemoveServiceProvider"
|
||
|
|
||
|
HRESULT DNPRemoveServiceProvider(PProtocolData pPData, HANDLE hSPHandle)
|
||
|
{
|
||
|
PSPD pSPD = NULL;
|
||
|
PEPD pEPD;
|
||
|
PMSD pMSD;
|
||
|
PFMD pFMD;
|
||
|
|
||
|
pSPD = (PSPD) hSPHandle;
|
||
|
|
||
|
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], hSPHandle[%x]", pPData, hSPHandle);
|
||
|
|
||
|
if(pSPD == NULL)
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "Returning DPNERR_INVALIDOBJECT - SP Handle is NULL");
|
||
|
return DPNERR_INVALIDOBJECT;
|
||
|
}
|
||
|
|
||
|
// There are several steps to shutdown:
|
||
|
// 1. All Core initiated commands must be cancelled prior to this function being called.
|
||
|
// We will assert in debug that the Core has done this.
|
||
|
// 2. All endpoints must be terminated by the Core prior to this function being called.
|
||
|
// We will assert in debug that the Core has done this.
|
||
|
// Now there are things on the SPD->SendQueue and SPD->PendingQueue that are not owned
|
||
|
// by any Command or Endpoint, and there may also be a SendThread Timer running held
|
||
|
// on SPD->SendHandle. No one else can clean these up, so these are our responsibility
|
||
|
// to clean up here. Items on the queues will be holding references to EPDs, so the
|
||
|
// EPDs will not be able to go away until we do this.
|
||
|
// 3. Cancel SPD->SendHandle Send Timer. This prevents items on the SendQueue from
|
||
|
// being submitted to the SP and moved to the PendingQueue.
|
||
|
// 4. Empty the SendQueue.
|
||
|
// 5. If we fail to cancel the SendHandle Send Timer, wait for it to run and figure out
|
||
|
// that we are going away. We do this after emptying the SendQueue for simplicity
|
||
|
// since the RunSendThread code checks for an empty SendQueue to know if it has work
|
||
|
// to do.
|
||
|
// 6. Wait for all messages to drain from the PendingQueue as the SP completes them.
|
||
|
// 7. Wait for any active EPDs to go away.
|
||
|
// 8. Call SP->Close only after all of the above so that we can ensure that we will make
|
||
|
// no calls to the SP after Close.
|
||
|
|
||
|
Lock(&pSPD->SPLock);
|
||
|
pSPD->ulSPFlags |= SPFLAGS_TERMINATING; // Nothing new gets in...
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
// Check for uncancelled commands, SPLock held
|
||
|
CBilink* pLink = pSPD->blMessageList.GetNext();
|
||
|
while (pLink != &pSPD->blMessageList)
|
||
|
{
|
||
|
pMSD = CONTAINING_RECORD(pLink, MSD, blSPLinkage);
|
||
|
ASSERT_MSD(pMSD);
|
||
|
ASSERT(pMSD->ulMsgFlags1 & MFLAGS_ONE_ON_GLOBAL_LIST);
|
||
|
DPFX(DPFPREP,0, "There are un-cancelled commands remaining on the Command List, Core didn't clean up properly - pMSD[%p], Context[%x]", pMSD, pMSD->Context);
|
||
|
ASSERT(0); // This is fatal, we can't make the guarantees we need to below under these conditions.
|
||
|
|
||
|
pLink = pLink->GetNext();
|
||
|
}
|
||
|
|
||
|
// Check for EPDs that have not been terminated, SPLock still held
|
||
|
pLink = pSPD->blEPDActiveList.GetNext();
|
||
|
while (pLink != &pSPD->blEPDActiveList)
|
||
|
{
|
||
|
pEPD = CONTAINING_RECORD(pLink, EPD, blActiveLinkage);
|
||
|
ASSERT_EPD(pEPD);
|
||
|
|
||
|
if (!(pEPD->ulEPFlags & EPFLAGS_STATE_TERMINATING))
|
||
|
{
|
||
|
DPFX(DPFPREP,0, "There are non-terminated endpoints remaining on the Endpoint List, Core didn't clean up properly - pEPD[%p], Context[%x]", pEPD, pEPD->Context);
|
||
|
ASSERT(0); // This is fatal, we can't make the guarantees we need to below under these conditions.
|
||
|
}
|
||
|
|
||
|
pLink = pLink->GetNext();
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// See if we still have a Send Event pending, SPLock still held
|
||
|
if(pSPD->SendHandle != NULL)
|
||
|
{
|
||
|
DPFX(DPFPREP,1, "Shutting down with send event still pending, cancelling, pSPD=[%p]", pSPD);
|
||
|
|
||
|
if(CancelMyTimer(pSPD->SendHandle, pSPD->SendHandleUnique) == DPN_OK)
|
||
|
{
|
||
|
pSPD->SendHandle = NULL;
|
||
|
pSPD->ulSPFlags &= ~(SPFLAGS_SEND_THREAD_SCHEDULED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DPFX(DPFPREP,1, "Failed to cancel send event", pSPD);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clean off the Send Queue, SPLock still held
|
||
|
while(!pSPD->blSendQueue.IsEmpty())
|
||
|
{
|
||
|
pFMD = CONTAINING_RECORD(pSPD->blSendQueue.GetNext(), FMD, blQLinkage);
|
||
|
ASSERT_FMD(pFMD);
|
||
|
|
||
|
ASSERT_EPD(pFMD->pEPD);
|
||
|
|
||
|
DPFX(DPFPREP,1, "Cleaning FMD off of SendQueue pSPD[%p], pFMD[%p], pEPD[%p]", pSPD, pFMD, pFMD->pEPD);
|
||
|
|
||
|
pFMD->blQLinkage.RemoveFromList();
|
||
|
|
||
|
// RELEASE_EPD will need to have the EPD lock, so we cannot hold the SPLock while calling it.
|
||
|
Unlock(&pSPD->SPLock);
|
||
|
|
||
|
Lock(&pFMD->pEPD->EPLock);
|
||
|
RELEASE_EPD(pFMD->pEPD, "UNLOCK (Releasing Leftover CMD FMD)"); // releases EPLock
|
||
|
RELEASE_FMD(pFMD, "SP Submit");
|
||
|
|
||
|
Lock(&pSPD->SPLock);
|
||
|
}
|
||
|
|
||
|
// In case we failed to cancel the SendHandle Timer above, wait for the send thread to run and figure
|
||
|
// out that we are going away. We want to be outside the SPLock while doing this.
|
||
|
while(pSPD->ulSPFlags & SPFLAGS_SEND_THREAD_SCHEDULED)
|
||
|
{
|
||
|
Unlock(&pSPD->SPLock);
|
||
|
Sleep(0); // Give up our time slice
|
||
|
Lock(&pSPD->SPLock);
|
||
|
}
|
||
|
|
||
|
// Clean off the Pending Queue, SPLock still held
|
||
|
while (!pSPD->blPendingQueue.IsEmpty())
|
||
|
{
|
||
|
Unlock(&pSPD->SPLock);
|
||
|
Sleep(0); // Give up our time slice
|
||
|
Lock(&pSPD->SPLock);
|
||
|
}
|
||
|
|
||
|
// By now we are only waiting for the SP to do any final calls to CommandComplete that are needed to take
|
||
|
// our EPD ref count down to nothing. We will wait while the SP does this.
|
||
|
while(!(pSPD->blEPDActiveList.IsEmpty()))
|
||
|
{
|
||
|
Unlock(&pSPD->SPLock);
|
||
|
Sleep(0); // Give up our time slice
|
||
|
Lock(&pSPD->SPLock);
|
||
|
}
|
||
|
|
||
|
// By this time everything pending had better be gone!
|
||
|
ASSERT(pSPD->blEPDActiveList.IsEmpty()); // Should not be any Endpoints left
|
||
|
ASSERT(pSPD->blSendQueue.IsEmpty()); // Should not be any frames on sendQ.
|
||
|
ASSERT(pSPD->blPendingQueue.IsEmpty()); // Should not be any frame in SP either
|
||
|
|
||
|
// Leave SPLock for the last time
|
||
|
Unlock(&pSPD->SPLock);
|
||
|
|
||
|
// Now that all frames are cleared out of SP, there should be no more End Points waiting around to close.
|
||
|
// We are clear to tell the SP to go away.
|
||
|
|
||
|
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Close, pSPD[%p]", pSPD);
|
||
|
IDP8ServiceProvider_Close(pSPD->IISPIntf);
|
||
|
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Release, pSPD[%p]", pSPD);
|
||
|
IDP8ServiceProvider_Release(pSPD->IISPIntf);
|
||
|
|
||
|
// Clean up the SPD object
|
||
|
DNDeleteCriticalSection(&pSPD->SPLock);
|
||
|
DNFree(pSPD);
|
||
|
|
||
|
// Remove the reference of this SP from the main Protocol object
|
||
|
ASSERT(pPData->lSPActiveCount > 0);
|
||
|
InterlockedDecrement(&pPData->lSPActiveCount);
|
||
|
|
||
|
return DPN_OK;
|
||
|
}
|
||
|
|
||
|
|