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.
 
 
 
 
 
 

767 lines
24 KiB

/*==========================================================================
*
* Copyright (C) 1998-2002 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.
*/
CFixedPool ChkPtPool; // Pool of CheckPoint data structure
CFixedPool EPDPool; // Pool of End Point descriptors
CFixedPool MSDPool; // Pool of Message Descriptors
CFixedPool FMDPool; // Pool of Frame Descriptors
CFixedPool RCDPool; // Pool of Receive Descriptors
CFixedPool BufPool; // Pool of buffers to store rcvd frames
CFixedPool MedBufPool;
CFixedPool BigBufPool;
#ifdef DBG
CBilink g_blProtocolCritSecsHeld;
#endif // DBG
#ifndef DPNBUILD_NOPROTOCOLTESTITF
PFNASSERTFUNC g_pfnAssertFunc = NULL;
PFNMEMALLOCFUNC g_pfnMemAllocFunc = NULL;
#endif // !DPNBUILD_NOPROTOCOLTESTITF
//////////////////////////////////
#define CHKPTPOOL_INITED 0x00000001
#define EPDPOOL_INITED 0x00000002
#define MSDPOOL_INITED 0x00000004
#define FMDPOOL_INITED 0x00000008
#define RCDPOOL_INITED 0x00000010
#define BUFPOOL_INITED 0x00000020
#define MEDBUFPOOL_INITED 0x00000040
#define BIGBUFPOOL_INITED 0x00000080
DWORD g_dwProtocolInitFlags = 0;
//////////////////////////////////
/*
** Pools Initialization
**
** This procedure should be called once at Dll load
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DNPPoolsInit"
BOOL DNPPoolsInit(HANDLE hModule)
{
DPFX(DPFPREP,DPF_CALLIN_LVL, "Enter");
#ifdef DBG
g_blProtocolCritSecsHeld.Initialize();
#endif // DBG
if(!ChkPtPool.Initialize(sizeof(CHKPT), NULL, NULL, NULL, NULL))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= CHKPTPOOL_INITED;
if(!EPDPool.Initialize(sizeof(EPD), EPD_Allocate, EPD_Get, EPD_Release, EPD_Free))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= EPDPOOL_INITED;
if(!MSDPool.Initialize(sizeof(MSD), MSD_Allocate, MSD_Get, MSD_Release, MSD_Free))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= MSDPOOL_INITED;
if(!FMDPool.Initialize(sizeof(FMD), FMD_Allocate, FMD_Get, FMD_Release, FMD_Free))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= FMDPOOL_INITED;
if(!RCDPool.Initialize(sizeof(RCD), RCD_Allocate, RCD_Get, RCD_Release, RCD_Free))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= RCDPOOL_INITED;
if(!BufPool.Initialize(sizeof(BUF), Buf_Allocate, Buf_Get, NULL, NULL))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= BUFPOOL_INITED;
if(!MedBufPool.Initialize(sizeof(MEDBUF), Buf_Allocate, Buf_GetMed, NULL, NULL))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= MEDBUFPOOL_INITED;
if(!BigBufPool.Initialize(sizeof(BIGBUF), Buf_Allocate, Buf_GetBig, NULL, NULL))
{
DNPPoolsDeinit();
return FALSE;
}
g_dwProtocolInitFlags |= BIGBUFPOOL_INITED;
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_dwProtocolInitFlags & CHKPTPOOL_INITED)
{
ChkPtPool.DeInitialize();
}
if(g_dwProtocolInitFlags & EPDPOOL_INITED)
{
EPDPool.DeInitialize();
}
if(g_dwProtocolInitFlags & MSDPOOL_INITED)
{
MSDPool.DeInitialize();
}
if(g_dwProtocolInitFlags & FMDPOOL_INITED)
{
FMDPool.DeInitialize();
}
if(g_dwProtocolInitFlags & RCDPOOL_INITED)
{
RCDPool.DeInitialize();
}
if(g_dwProtocolInitFlags & BUFPOOL_INITED)
{
BufPool.DeInitialize();
}
if(g_dwProtocolInitFlags & MEDBUFPOOL_INITED)
{
MedBufPool.DeInitialize();
}
if(g_dwProtocolInitFlags & BIGBUFPOOL_INITED)
{
BigBufPool.DeInitialize();
}
g_dwProtocolInitFlags = 0;
}
#undef DPF_MODNAME
#define DPF_MODNAME "DNPProtocolCreate"
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
HRESULT DNPProtocolCreate(const XDP8CREATE_PARAMS * const pDP8CreateParams, VOID** ppvProtocol)
#else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
HRESULT DNPProtocolCreate(VOID** ppvProtocol)
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
{
ASSERT(ppvProtocol);
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
ASSERT(pDP8CreateParams);
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pDP8CreateParams[%p], ppvProtocol[%p]", pDP8CreateParams, ppvProtocol);
#else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: ppvProtocol[%p]", ppvProtocol);
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
#ifndef DPNBUILD_NOPROTOCOLTESTITF
g_pfnAssertFunc = NULL;
g_pfnMemAllocFunc = NULL;
#endif // !DPNBUILD_NOPROTOCOLTESTITF
if ((*ppvProtocol = MEMALLOC(MEMID_PPD, sizeof(ProtocolData))) == NULL)
{
DPFERR("DNMalloc() failed");
return(E_OUTOFMEMORY);
}
memset(*ppvProtocol, 0, sizeof(ProtocolData));
// The sign needs to be valid by the time DNPProtocolInitialize is called
((ProtocolData*)*ppvProtocol)->Sign = PPD_SIGN;
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
DWORD dwNumToAllocate;
DWORD dwAllocated;
#ifdef _XBOX
#define MAX_FRAME_SIZE 1462 // Note we are hard coding the expected frame size.
#else // ! _XBOX
#define MAX_FRAME_SIZE 1472 // Note we are hard coding the expected frame size.
#endif // ! _XBOX
dwNumToAllocate = (pDP8CreateParams->dwMaxNumPlayers - 1);
dwAllocated = ChkPtPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u checkpoints!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
dwNumToAllocate = (pDP8CreateParams->dwMaxNumPlayers - 1);
dwAllocated = EPDPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u EPDs!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
dwNumToAllocate = pDP8CreateParams->dwMaxSendsPerPlayer
* (pDP8CreateParams->dwMaxNumPlayers - 1);
dwNumToAllocate += pDP8CreateParams->dwNumSimultaneousEnumHosts;
dwNumToAllocate += 1; // one for a listen operation
dwAllocated = MSDPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u MSDs!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
dwNumToAllocate = pDP8CreateParams->dwMaxSendsPerPlayer
* (pDP8CreateParams->dwMaxNumPlayers - 1);
// Include the possiblity of having to split a message across multiple frames.
dwNumToAllocate *= pDP8CreateParams->dwMaxMessageSize / MAX_FRAME_SIZE;
dwNumToAllocate += pDP8CreateParams->dwNumSimultaneousEnumHosts;
dwAllocated = FMDPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u FMDs!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer
* (pDP8CreateParams->dwMaxNumPlayers - 1);
dwAllocated = RCDPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u RCDs!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
if (pDP8CreateParams->dwMaxMessageSize > MAX_FRAME_SIZE)
{
dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer
* (pDP8CreateParams->dwMaxNumPlayers - 1);
dwAllocated = BufPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u small receive buffers!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
if (pDP8CreateParams->dwMaxMessageSize > MEDIUM_BUFFER_SIZE)
{
dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer
* (pDP8CreateParams->dwMaxNumPlayers - 1);
dwAllocated = MedBufPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u medium receive buffers!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
if (pDP8CreateParams->dwMaxMessageSize > LARGE_BUFFER_SIZE)
{
dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer
* (pDP8CreateParams->dwMaxNumPlayers - 1);
dwAllocated = BigBufPool.Preallocate(dwNumToAllocate, NULL);
if (dwAllocated < dwNumToAllocate)
{
DPFX(DPFPREP, 0, "Only allocated %u of %u big receive buffers!", dwAllocated, dwNumToAllocate);
DNFree(*ppvProtocol);
*ppvProtocol = NULL;
return(E_OUTOFMEMORY);
}
}
}
} // end if (messages may span multiple frames)
#endif // DPNBUILD_PREALLOCATEDMEMORYMODEL
return DPN_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "DNPProtocolDestroy"
VOID DNPProtocolDestroy(VOID* pvProtocol)
{
if (pvProtocol)
{
DNFree(pvProtocol);
}
}
/*
** 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(HANDLE hProtocolData, PVOID pCoreContext, PDN_PROTOCOL_INTERFACE_VTBL pVtbl,
IDirectPlay8ThreadPoolWork *pDPThreadPoolWork, BOOL bAssumeLANConnections)
{
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pCoreContext[%p], hProtocolData[%p], pVtbl[%p], pDPThreadPoolWork[%p]", hProtocolData, pCoreContext, pVtbl, pDPThreadPoolWork);
// 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));
ProtocolData* pPData;
pPData = (ProtocolData*)hProtocolData;
ASSERT_PPD(pPData);
IDirectPlay8ThreadPoolWork_AddRef(pDPThreadPoolWork);
pPData->pDPThreadPoolWork = pDPThreadPoolWork;
pPData->ulProtocolFlags = 0;
pPData->Parent = pCoreContext;
pPData->pfVtbl = pVtbl;
pPData->lSPActiveCount = 0;
pPData->tIdleThreshhold = DEFAULT_KEEPALIVE_INTERVAL; // 60 second keep-alive interval
pPData->dwConnectTimeout = CONNECT_DEFAULT_TIMEOUT;
pPData->dwConnectRetries = CONNECT_DEFAULT_RETRIES;
pPData->dwMaxRecvMsgSize=DEFAULT_MAX_RECV_MSG_SIZE;
pPData->dwSendRetriesToDropLink=DEFAULT_SEND_RETRIES_TO_DROP_LINK;
pPData->dwSendRetryIntervalLimit=DEFAULT_SEND_RETRY_INTERVAL_LIMIT;
pPData->dwNumHardDisconnectSends=DEFAULT_HARD_DISCONNECT_SENDS;
pPData->dwMaxHardDisconnectPeriod=DEFAULT_HARD_DISCONNECT_MAX_PERIOD;
pPData->dwInitialFrameWindowSize = bAssumeLANConnections ?
LAN_INITIAL_FRAME_WINDOW_SIZE : DEFAULT_INITIAL_FRAME_WINDOW_SIZE;
pPData->dwDropThresholdRate = DEFAULT_THROTTLE_THRESHOLD_RATE;
pPData->dwDropThreshold = (32 * DEFAULT_THROTTLE_THRESHOLD_RATE) / 100;
pPData->dwThrottleRate = DEFAULT_THROTTLE_BACK_OFF_RATE;
pPData->fThrottleRate = (100.0 - (FLOAT)DEFAULT_THROTTLE_BACK_OFF_RATE) / 100.0;
DPFX(DPFPREP, 2, "pPData->fThrottleRate [%f]", pPData->fThrottleRate);
#ifdef DBG
pPData->ThreadsInReceive = 0;
pPData->BuffersInReceive = 0;
#endif // DBG
pPData->ulProtocolFlags |= PFLAGS_PROTOCOL_INITIALIZED;
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
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(HANDLE hProtocolData)
{
ProtocolData* pPData;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p]", hProtocolData);
pPData = (ProtocolData*)hProtocolData;
ASSERT_PPD(pPData);
ASSERT(pPData->lSPActiveCount == 0);
IDirectPlay8ThreadPoolWork_Release(pPData->pDPThreadPoolWork);
pPData->pDPThreadPoolWork = NULL;
#ifdef DBG
if (pPData->BuffersInReceive != 0)
{
DPFX(DPFPREP,0, "*** %d receive buffers were leaked", pPData->BuffersInReceive);
}
#endif // DBG
pPData->ulProtocolFlags = 0;
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
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(HANDLE hProtocolData, IDP8ServiceProvider* pISP,
HANDLE* phSPContext, DWORD dwFlags)
{
ProtocolData* pPData;
PSPD pSPD;
SPINITIALIZEDATA SPInitData;
SPGETCAPSDATA SPCapsData;
HRESULT hr;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p], pISP[%p], phSPContext[%p]", hProtocolData, pISP, phSPContext);
hr = DPN_OK;
pPData = (ProtocolData*)hProtocolData;
ASSERT_PPD(pPData);
if(pPData->ulProtocolFlags & PFLAGS_PROTOCOL_INITIALIZED)
{
if ((pSPD = (PSPD)MEMALLOC(MEMID_SPD, sizeof(SPD))) == NULL)
{
DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - couldn't allocate SP Descriptor");
hr = DPNERR_OUTOFMEMORY;
goto Exit;
}
// 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 = dwFlags;
if (DNInitializeCriticalSection(&pSPD->SPLock) == FALSE)
{
DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - couldn't initialize SP CS, pSPD[%p]", pSPD);
DNFree(pSPD);
hr = DPNERR_OUTOFMEMORY;
goto Exit;
}
DebugSetCriticalSectionRecursionCount(&pSPD->SPLock, 0);
DebugSetCriticalSectionGroup(&pSPD->SPLock, &g_blProtocolCritSecsHeld);
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Initialize, pSPD[%p]", pSPD);
if((hr = IDP8ServiceProvider_Initialize(pISP, &SPInitData)) != DPN_OK)
{
if (hr == DPNERR_UNSUPPORTED)
{
DPFX(DPFPREP,1, "SP unsupported, pSPD[%p]", pSPD);
}
else
{
DPFX(DPFPREP,0, "Returning hr=%x - SP->Initialize failed, pSPD[%p]", hr, pSPD);
}
DNDeleteCriticalSection(&pSPD->SPLock);
DNFree(pSPD);
goto Exit;
}
pSPD->blSendQueue.Initialize();
pSPD->blPendingQueue.Initialize();
pSPD->blEPDActiveList.Initialize();
#ifdef DBG
pSPD->blMessageList.Initialize();
#endif // DBG
// MAKE THE SP GET CAPS CALL TO FIND FRAMESIZE AND LINKSPEED
SPCapsData.dwSize = sizeof(SPCapsData);
SPCapsData.hEndpoint = INVALID_HANDLE_VALUE;
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
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);
goto Exit;
}
pSPD->uiLinkSpeed = SPCapsData.dwLocalLinkSpeed;
pSPD->uiFrameLength = SPCapsData.dwUserFrameSize;
if (pSPD->uiFrameLength < MIN_SEND_MTU)
{
DPFX(DPFPREP,0, "SP MTU isn't large enough to support protocol pSPD[%p] Required MTU[%u] Available MTU[%u] "
"Returning DPNERR_UNSUPPORTED", pSPD, MIN_SEND_MTU, pSPD->uiFrameLength);
IDP8ServiceProvider_Close(pISP);
DNDeleteCriticalSection(&pSPD->SPLock);
DNFree(pSPD);
hr=DPNERR_UNSUPPORTED;
goto Exit;
}
pSPD->uiUserFrameLength = pSPD->uiFrameLength - MAX_SEND_DFRAME_NOCOALESCE_HEADER_SIZE;
DPFX(DPFPREP, 3, "SPD 0x%p frame length = %u, single user frame length = %u.", pSPD, pSPD->uiFrameLength, pSPD->uiUserFrameLength);
// Place new SP in table
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->AddRef, pSPD[%p]", pSPD);
IDP8ServiceProvider_AddRef(pISP);
pSPD->IISPIntf = pISP;
pSPD->pPData = pPData;
DNInterlockedIncrement(&pPData->lSPActiveCount);
}
else
{
pSPD = NULL;
DPFX(DPFPREP,0, "Returning DPNERR_UNINITIALIZED - DNPProtocolInitialize has not been called");
hr = DPNERR_UNINITIALIZED;
goto Exit;
}
*phSPContext = pSPD;
Exit:
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
return hr;
}
/*
** 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 we will ASSERT that everything is in fact finished up.
**
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DNPRemoveServiceProvider"
HRESULT DNPRemoveServiceProvider(HANDLE hProtocolData, HANDLE hSPHandle)
{
ProtocolData* pPData;
PSPD pSPD;
PFMD pFMD;
DWORD dwInterval;
#ifdef DBG
PEPD pEPD;
PMSD pMSD;
#endif // DBG
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p], hSPHandle[%x]", hProtocolData, hSPHandle);
pPData = (ProtocolData*)hProtocolData;
ASSERT_PPD(pPData);
pSPD = (PSPD) hSPHandle;
ASSERT_SPD(pSPD);
// 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 DBG
// Check for uncancelled commands, SPLock held
CBilink* pLink = pSPD->blMessageList.GetNext();
while (pLink != &pSPD->blMessageList)
{
pMSD = CONTAINING_OBJECT(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_OBJECT(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 // DBG
// Clean off the Send Queue, SPLock still held
while(!pSPD->blSendQueue.IsEmpty())
{
pFMD = CONTAINING_OBJECT(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.
dwInterval = 10;
while(pSPD->ulSPFlags & SPFLAGS_SEND_THREAD_SCHEDULED)
{
Unlock(&pSPD->SPLock);
IDirectPlay8ThreadPoolWork_SleepWhileWorking(pPData->pDPThreadPoolWork, dwInterval, 0);
dwInterval += 5;
ASSERT(dwInterval < 500);
Lock(&pSPD->SPLock);
}
// Clean off the Pending Queue, SPLock still held
dwInterval = 10;
while (!pSPD->blPendingQueue.IsEmpty())
{
Unlock(&pSPD->SPLock);
IDirectPlay8ThreadPoolWork_SleepWhileWorking(pPData->pDPThreadPoolWork, dwInterval, 0);
dwInterval += 5;
ASSERT(dwInterval < 500);
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.
dwInterval = 10;
while(!(pSPD->blEPDActiveList.IsEmpty()))
{
Unlock(&pSPD->SPLock);
IDirectPlay8ThreadPoolWork_SleepWhileWorking(pPData->pDPThreadPoolWork, dwInterval, 0);
dwInterval += 5;
ASSERT(dwInterval < 500);
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.
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
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);
DNInterlockedDecrement(&pPData->lSPActiveCount);
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
return DPN_OK;
}