1694 lines
39 KiB
C++
1694 lines
39 KiB
C++
/*
|
|
- QOS.CPP
|
|
-
|
|
* Microsoft NetMeeting
|
|
* Quality of Service DLL
|
|
* IQoS interfaces
|
|
*
|
|
* Revision History:
|
|
*
|
|
* When Who What
|
|
* -------- ------------------ ---------------------------------------
|
|
* 10.23.96 Yoram Yaacovi Created
|
|
*
|
|
* Functions:
|
|
* IQoS
|
|
* CQoS::QueryInterface
|
|
* CQoS::AddRef
|
|
* CQoS::Release
|
|
* CQoS::RequestResources
|
|
* CQoS::ReleaseResources
|
|
* CQoS::SetClients
|
|
* CQoS::SetResources
|
|
* CQoS::GetResources
|
|
* CQoS::FreeBuffer
|
|
* Public:
|
|
* CQoS::CQoS
|
|
* CQoS::~CQoS
|
|
* CQoS::Initialize
|
|
* Private
|
|
* CQoS::AnyRequests
|
|
* CQoS::FindClientsForResource
|
|
* CQoS::StoreResourceRequest
|
|
* CQoS::FreeResourceRequest
|
|
* CQoS::UpdateClientInfo
|
|
* CQoS::QoSCleanup
|
|
* CQoS::FindClient
|
|
* CQoS::UpdateRequestsForClient
|
|
* External
|
|
* CreateQoS
|
|
* QoSEntryPoint
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
EXTERN_C int g_cQoSObjects=0;
|
|
EXTERN_C HANDLE g_hQoSMutex=NULL;
|
|
class CQoS *g_pQoS;
|
|
|
|
#ifdef DEBUG
|
|
HDBGZONE ghDbgZoneQoS = NULL;
|
|
|
|
static PTCHAR _rgZonesQos[] = {
|
|
TEXT("qos"),
|
|
TEXT("Init"),
|
|
TEXT("IQoS"),
|
|
TEXT("Thread"),
|
|
TEXT("Structures"),
|
|
TEXT("Parameters"),
|
|
};
|
|
#endif /* DEBUG */
|
|
|
|
/***************************************************************************
|
|
|
|
Name : QoSCleanup
|
|
|
|
Purpose : Cleans up a QoS object before releasing (free memory, etc)
|
|
|
|
Parameters: pqos - pointer to a QoS pbject
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::QoSCleanup ()
|
|
{
|
|
HRESULT hr=NOERROR;
|
|
LPRESOURCEINT pr=NULL;
|
|
LPCLIENTINT pc=NULL;
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
/*
|
|
* Free memory
|
|
*/
|
|
// traverse and free all the memory allocated by the QoS object
|
|
// resources and requests first
|
|
pr = m_pResourceList;
|
|
while (pr)
|
|
{
|
|
LPRESOURCEINT prNext=pr->fLink;
|
|
|
|
// first, delete the request list for this resource
|
|
FreeListOfRequests(&(pr->pRequestList));
|
|
|
|
MEMFREE(pr);
|
|
pr = prNext;
|
|
}
|
|
m_pResourceList = 0;
|
|
|
|
// next is clients
|
|
pc = m_pClientList;
|
|
while (pc)
|
|
{
|
|
LPCLIENTINT pcNext=pc->fLink;
|
|
|
|
// delete the request list for this client
|
|
FreeListOfRequests(&(pc->pRequestList));
|
|
|
|
// now delete the client itself
|
|
MEMFREE(pc);
|
|
pc = pcNext;
|
|
}
|
|
m_pClientList = 0;
|
|
|
|
// terminate the QoS thread and let it exit
|
|
// the thread should really be terminated when the last request
|
|
// is released, so this is just a safety measure
|
|
StopQoSThread();
|
|
|
|
// delete the events
|
|
if (m_evImmediateNotify)
|
|
CloseHandle(m_evImmediateNotify);
|
|
m_evImmediateNotify = NULL;
|
|
|
|
if (m_evThreadExitSignal)
|
|
CloseHandle(m_evThreadExitSignal);
|
|
m_evThreadExitSignal = NULL;
|
|
|
|
RELMUTEX(g_hQoSMutex);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : AnyRequests
|
|
|
|
Purpose : Finds out if there are any resource requests
|
|
|
|
Parameters: none
|
|
|
|
Returns : TRUE - there is at least one request
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
BOOL CQoS::AnyRequests(void)
|
|
{
|
|
LPRESOURCEINT pr=NULL;
|
|
BOOL bAnyRequests=FALSE;
|
|
|
|
pr = m_pResourceList;
|
|
while (pr)
|
|
{
|
|
if (pr->pRequestList)
|
|
{
|
|
bAnyRequests=TRUE;
|
|
break;
|
|
}
|
|
|
|
// next resource
|
|
pr = pr->fLink;
|
|
}
|
|
|
|
return bAnyRequests;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : FindClientsForResource
|
|
|
|
Purpose : Finds if there are clients for a specific resource
|
|
|
|
Parameters: [in] dwResourceID = the ID of the resource
|
|
[in] pc = client pointer to start searching from
|
|
[out] puSamePriClients = number of clients with the same
|
|
priority for this resource is returned here
|
|
[out] puLowerPriClients = number of clients with lower
|
|
priority for this resource is returned here
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : This function is NOT general purpose. It only counts clients
|
|
with the same priority DOWN the list.
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::FindClientsForResource( DWORD dwResourceID,
|
|
LPCLIENTINT pc,
|
|
ULONG *puSamePriClients,
|
|
ULONG *puLowerPriClients)
|
|
{
|
|
LPCLIENTINT pctemp=pc->fLink;
|
|
LPRESOURCEREQUESTINT pcrr=NULL;
|
|
|
|
*puLowerPriClients = 0;
|
|
*puSamePriClients = 1; // the first client (at 'pc')
|
|
while (pctemp)
|
|
{
|
|
LPRESOURCEINT pr=NULL;
|
|
|
|
// does this client need this specific resource ?
|
|
pcrr = pctemp->pRequestList;
|
|
while (pcrr)
|
|
{
|
|
if (pcrr->sResourceRequest.resourceID == dwResourceID)
|
|
{
|
|
// it is either a same priority client or a lower priority
|
|
// client (the list is sorted)
|
|
(pctemp->client.priority == pc->client.priority ?
|
|
(*puSamePriClients)++ :
|
|
(*puLowerPriClients)++);
|
|
break;
|
|
}
|
|
|
|
// next request for this client
|
|
pcrr = pcrr->fLink;
|
|
}
|
|
|
|
pctemp = pctemp->fLink;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : FreeListOfRequests
|
|
|
|
Purpose : Free all records of a linked list of requests, given the
|
|
address of the list pointer. Zero's the list pointer
|
|
|
|
Parameters: lppRequestList - address of the pointer to the beginning
|
|
of the list
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::FreeListOfRequests(LPRESOURCEREQUESTINT *lppRequestList)
|
|
{
|
|
LPRESOURCEREQUESTINT prr=*lppRequestList;
|
|
HRESULT hr=NOERROR;
|
|
|
|
while (prr)
|
|
{
|
|
LPRESOURCEREQUESTINT prrNext=prr->fLink;
|
|
|
|
MEMFREE(prr);
|
|
prr = prrNext;
|
|
}
|
|
|
|
*lppRequestList = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : FreeResourceRequest
|
|
|
|
Purpose : Frees resource units and respective resource requests
|
|
|
|
Parameters: pClientGUID - the GUID of the calling client (stream)
|
|
pnUnits - a pointer of where to return the number of units freed
|
|
pResourceInt - pointer to the resource being freed
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::FreeResourceRequest ( LPGUID pClientGUID,
|
|
LPRESOURCEINT pResourceInt,
|
|
int *pnUnits)
|
|
{
|
|
HRESULT hr=NOERROR;
|
|
LPRESOURCEREQUESTINT prr=NULL, *prrPrev=NULL;
|
|
|
|
// find the request from this client
|
|
prr = pResourceInt->pRequestList;
|
|
prrPrev = &(pResourceInt->pRequestList);
|
|
while (prr)
|
|
{
|
|
if (COMPARE_GUIDS(&(prr->guidClientGUID), pClientGUID))
|
|
{
|
|
// we do have a request from this client.
|
|
// reclaim the units...
|
|
*pnUnits = prr->sResourceRequest.nUnitsMin;
|
|
|
|
// ...and remove it
|
|
*prrPrev = prr->fLink;
|
|
MEMFREE(prr);
|
|
|
|
// we're done.
|
|
hr = NOERROR;
|
|
goto out;
|
|
}
|
|
|
|
prrPrev = &(prr->fLink);
|
|
prr = prr->fLink;
|
|
}
|
|
|
|
hr = QOS_E_NO_SUCH_REQUEST;
|
|
|
|
out:
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : StoreResourceRequest
|
|
|
|
Purpose : Stores a resource request with the resource
|
|
|
|
Parameters: pClientGUID - the GUID of the calling client (stream)
|
|
pResourceRequest - the request to store
|
|
pfnQoSNotify - a pointer to a notification function for the
|
|
requesting client
|
|
pResourceInt - pointer to the resource on which to store the
|
|
request
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::StoreResourceRequest (LPGUID pClientGUID,
|
|
LPRESOURCEREQUEST pResourceRequest,
|
|
LPFNQOSNOTIFY pfnQoSNotify,
|
|
DWORD_PTR dwParam,
|
|
LPRESOURCEINT pResourceInt)
|
|
{
|
|
HRESULT hr=NOERROR;
|
|
LPRESOURCEREQUESTINT prr=NULL, *prrPrev=NULL;
|
|
BOOL fRequestFound=FALSE;
|
|
|
|
/*
|
|
* Store the request
|
|
*/
|
|
|
|
// do we already have a request from this client ?
|
|
prr = pResourceInt->pRequestList;
|
|
prrPrev = &(pResourceInt->pRequestList);
|
|
while (prr)
|
|
{
|
|
if (COMPARE_GUIDS(&(prr->guidClientGUID), pClientGUID))
|
|
{
|
|
// we do have a request from this client. override it.
|
|
RtlCopyMemory( &(prr->sResourceRequest),
|
|
pResourceRequest,
|
|
sizeof(RESOURCEREQUEST));
|
|
RtlCopyMemory(&(prr->guidClientGUID), pClientGUID, sizeof(GUID));
|
|
prr->pfnQoSNotify = pfnQoSNotify;
|
|
prr->dwParam = dwParam;
|
|
|
|
// we're done.
|
|
hr = NOERROR;
|
|
fRequestFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
prrPrev = &(prr->fLink);
|
|
prr = prr->fLink;
|
|
}
|
|
|
|
if (!fRequestFound)
|
|
{
|
|
// not found. make one
|
|
prr = (LPRESOURCEREQUESTINT) MEMALLOC(sizeof(RESOURCEREQUESTINT));
|
|
ASSERT(prr);
|
|
if (!prr)
|
|
{
|
|
ERRORMSG(("StoreResourceRequest: MEMALLOC failed on RESOURCEREQUESTINT\n"));
|
|
hr = E_OUTOFMEMORY;
|
|
goto out;
|
|
}
|
|
|
|
*prrPrev = prr;
|
|
prr->fLink = NULL;
|
|
|
|
}
|
|
|
|
// whether found or made, copy the contents in
|
|
RtlCopyMemory( &(prr->sResourceRequest),
|
|
pResourceRequest,
|
|
sizeof(RESOURCEREQUEST));
|
|
RtlCopyMemory(&(prr->guidClientGUID), pClientGUID, sizeof(GUID));
|
|
prr->pfnQoSNotify = pfnQoSNotify;
|
|
prr->dwParam = dwParam;
|
|
|
|
out:
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : UpdateClientInfo
|
|
|
|
Purpose : Updates the client info when a resource request is granted
|
|
|
|
Parameters: pClientGUID - the GUID of the calling client (stream)
|
|
pfnQoSNotify - a pointer to a notification function for the
|
|
requesting client
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::UpdateClientInfo (LPGUID pClientGUID,
|
|
LPFNQOSNOTIFY pfnQoSNotify)
|
|
{
|
|
HRESULT hr=NOERROR;
|
|
LPCLIENTLIST pcl=NULL;
|
|
|
|
/*
|
|
* Update client info
|
|
*/
|
|
|
|
// we'll do this through calling the SetClients method
|
|
// allocate and fill a CLIENTLIST structure
|
|
pcl = (LPCLIENTLIST) MEMALLOC(sizeof(CLIENTLIST));
|
|
if (!pcl)
|
|
{
|
|
ERRORMSG(("UpdateClientInfo: MEMALLOC failed\n"));
|
|
hr = E_OUTOFMEMORY;
|
|
goto out;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID) pcl, sizeof(CLIENTLIST));
|
|
|
|
// fill in the resource list
|
|
pcl->cClients = 1;
|
|
RtlCopyMemory(&(pcl->aClients[0].guidClientGUID), pClientGUID, sizeof(GUID));
|
|
pcl->aClients[0].priority = QOS_LOWEST_PRIORITY;
|
|
|
|
// set the clients info on the QoS module
|
|
hr = SetClients(pcl);
|
|
|
|
out:
|
|
if (pcl)
|
|
MEMFREE(pcl);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : UpdateRequestsForClient
|
|
|
|
Purpose : Update a client's request list by finding all existing resource
|
|
requests for this client
|
|
|
|
Parameters: pClientGUID - the GUID of the calling client (stream)
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::UpdateRequestsForClient (LPGUID pClientGUID)
|
|
{
|
|
HRESULT hr=NOERROR;
|
|
LPRESOURCEINT pr=NULL;
|
|
LPCLIENTINT pc=NULL;
|
|
LPRESOURCEREQUESTINT prr=NULL, *pcrrfLink=NULL, pcrr=NULL;
|
|
|
|
/*
|
|
* get rid of the current request list for this client
|
|
*/
|
|
// find the client first
|
|
hr = FindClient(pClientGUID, &pc);
|
|
if (FAILED(hr) || !pc)
|
|
{
|
|
hr = QOS_E_NO_SUCH_CLIENT;
|
|
goto out;
|
|
}
|
|
|
|
// now delete old request list
|
|
FreeListOfRequests(&(pc->pRequestList));
|
|
|
|
/*
|
|
* create and add the new request list
|
|
*/
|
|
pr = m_pResourceList;
|
|
pcrrfLink = &(pc->pRequestList);
|
|
while (pr)
|
|
{
|
|
prr = pr->pRequestList;
|
|
while (prr)
|
|
{
|
|
if (COMPARE_GUIDS(&(prr->guidClientGUID), pClientGUID))
|
|
{
|
|
// we found a request from this client.
|
|
// allocate memory for it, and copy it in
|
|
pcrr = (LPRESOURCEREQUESTINT) MEMALLOC(sizeof(RESOURCEREQUESTINT));
|
|
ASSERT(pcrr);
|
|
if (!pcrr)
|
|
{
|
|
ERRORMSG(("UpdateRequestsForClient: MEMALLOC failed on RESOURCEREQUESTINT\n"));
|
|
hr = E_OUTOFMEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// copy the contents in
|
|
RtlCopyMemory(pcrr, prr, sizeof(RESOURCEREQUESTINT));
|
|
|
|
// need a different fLink for the client request list
|
|
*pcrrfLink = pcrr;
|
|
pcrr->fLink = NULL;
|
|
pcrrfLink = &(pcrr->fLink);
|
|
}
|
|
|
|
// next request
|
|
prr = prr->fLink;
|
|
}
|
|
|
|
// next resource
|
|
pr = pr->fLink;
|
|
}
|
|
|
|
out:
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : FindClient
|
|
|
|
Purpose : Finds and returns a client record
|
|
|
|
Parameters: pClientGUID - the GUID whose record to find
|
|
ppClient - address of where to put a pointer to the client found
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::FindClient(LPGUID pClientGUID, LPCLIENTINT *ppClient)
|
|
{
|
|
LPCLIENTINT pc=NULL;
|
|
HRESULT hr=NOERROR;
|
|
|
|
*ppClient = NULL;
|
|
pc = m_pClientList;
|
|
while (pc)
|
|
{
|
|
if (COMPARE_GUIDS(&(pc->client.guidClientGUID), pClientGUID))
|
|
{
|
|
*ppClient = pc;
|
|
goto out;
|
|
}
|
|
|
|
// next client
|
|
pc = pc->fLink;
|
|
}
|
|
|
|
hr = QOS_E_NO_SUCH_CLIENT;
|
|
|
|
out:
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
IUnknown Methods
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::QueryInterface (REFIID riid, LPVOID *lppNewObj)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::QueryInterface\n"));
|
|
|
|
if (IsBadReadPtr(&riid, (UINT) sizeof(IID)))
|
|
{
|
|
hr = ResultFromScode(E_INVALIDARG);
|
|
goto out;
|
|
}
|
|
|
|
if (IsBadWritePtr(lppNewObj, sizeof(LPVOID)))
|
|
{
|
|
hr = ResultFromScode(E_INVALIDARG);
|
|
goto out;
|
|
}
|
|
|
|
*lppNewObj = 0;
|
|
if (riid == IID_IUnknown || riid == IID_IQoS)
|
|
*lppNewObj = (IQoS *) this;
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
goto out;
|
|
}
|
|
|
|
((IUnknown *)*lppNewObj)->AddRef ();
|
|
|
|
out:
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::QueryInterface - leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
ULONG CQoS::AddRef (void)
|
|
{
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::AddRef\n"));
|
|
|
|
InterlockedIncrement((long *) &m_cRef);
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::AddRef - leave, m_cRef=%d\n", m_cRef));
|
|
|
|
return m_cRef;
|
|
}
|
|
|
|
ULONG CQoS::Release (void)
|
|
{
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::Release\n"));
|
|
|
|
// if the cRef is already 0 (shouldn't happen), assert, but let it through
|
|
ASSERT(m_cRef);
|
|
|
|
if (InterlockedDecrement((long *) &m_cRef) == 0)
|
|
{
|
|
if (m_bQoSEnabled)
|
|
QoSCleanup();
|
|
delete this;
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::Final Release\n"));
|
|
return 0;
|
|
}
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::Release - leave, m_cRef=%d\n", m_cRef));
|
|
|
|
return m_cRef;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::RequestResources
|
|
|
|
Purpose : Requests resources
|
|
|
|
Parameters: lpStreamGUID - the GUID of the calling client (stream)
|
|
lpResourceRequestList - a list of resource requests that
|
|
the caller wants to reserve
|
|
lpfnQoSNotify - a pointer to a notification function for the
|
|
requesting client
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::RequestResources (LPGUID lpClientGUID,
|
|
LPRESOURCEREQUESTLIST lpResourceRequestList,
|
|
LPFNQOSNOTIFY lpfnQoSNotify,
|
|
DWORD_PTR dwParam)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
ULONG i;
|
|
BOOL fResourceFound=FALSE, fRequestGranted=FALSE;
|
|
LPRESOURCEINT pResourceInt=NULL, *pPrevResourcefLink=NULL;
|
|
RESOURCEREQUEST *pResourceRequest=NULL;
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::RequestResources\n"));
|
|
|
|
/*
|
|
* Parameter validation
|
|
*/
|
|
|
|
// lpResourceRequestList should at least have a count DWORD
|
|
// must have a GUID and a notify callback
|
|
if (!lpResourceRequestList ||
|
|
IsBadReadPtr(lpResourceRequestList, (UINT) sizeof(DWORD)) ||
|
|
!lpClientGUID ||
|
|
!lpfnQoSNotify)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto out_nomutex;
|
|
}
|
|
|
|
DISPLAYPARAMETERS( REQUEST_RESOURCES_ID,
|
|
lpClientGUID,
|
|
lpResourceRequestList,
|
|
lpfnQoSNotify,
|
|
dwParam,
|
|
0);
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
if (!m_bQoSEnabled)
|
|
// just return
|
|
goto out;
|
|
|
|
/*
|
|
* Find and allocate the resources
|
|
*/
|
|
|
|
// for each requested resource
|
|
pResourceRequest=lpResourceRequestList->aRequests;
|
|
for (i=0; i < lpResourceRequestList->cRequests; i++)
|
|
{
|
|
pResourceInt = m_pResourceList;
|
|
fResourceFound = FALSE;
|
|
// find the resource
|
|
while (pResourceInt)
|
|
{
|
|
if (pResourceInt->resource.resourceID == pResourceRequest[i].resourceID)
|
|
{ // resource found
|
|
// see if the resource is available
|
|
// priority will be handled at the first notify callback
|
|
// CHECK: add nUnitsMax handling
|
|
if (pResourceRequest[i].nUnitsMin <= pResourceInt->nNowAvailUnits)
|
|
{
|
|
// resource is available. take the requested share.
|
|
pResourceInt->nNowAvailUnits -= pResourceRequest[i].nUnitsMin;
|
|
|
|
// store a local copy of the request
|
|
pResourceRequest[i].hResult = StoreResourceRequest(lpClientGUID,
|
|
&(pResourceRequest[i]),
|
|
lpfnQoSNotify,
|
|
dwParam,
|
|
pResourceInt);
|
|
// if we failed storing, propagate the result to the bottom line
|
|
// returned result
|
|
if (FAILED(pResourceRequest[i].hResult))
|
|
{
|
|
hr = pResourceRequest[i].hResult;
|
|
}
|
|
else
|
|
{ // at least one request was granted to this client
|
|
fRequestGranted = TRUE;
|
|
}
|
|
}
|
|
else // resource not available
|
|
{
|
|
// let the client know how much is available
|
|
pResourceRequest[i].nUnitsMin = pResourceInt->nNowAvailUnits;
|
|
pResourceRequest[i].hResult = QOS_E_RES_NOT_ENOUGH_UNITS;
|
|
hr = QOS_E_REQ_ERRORS;
|
|
}
|
|
|
|
fResourceFound = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
// not this one. try next one.
|
|
pResourceInt = pResourceInt->fLink;
|
|
|
|
} // while
|
|
|
|
if (!fResourceFound)
|
|
{
|
|
pResourceRequest[i].hResult = QOS_E_RES_NOT_AVAILABLE;
|
|
hr = QOS_E_REQ_ERRORS;
|
|
}
|
|
|
|
// next request
|
|
} // for
|
|
|
|
// if we allocated resources to this client, add it to the client list,
|
|
// provided that it is not already in the list
|
|
// special case: if the call to RequestResources was made from the QoS
|
|
// notification proc, no need to update the client info. Actually, it will
|
|
// be bad to do this, since we are traversing the client list in the
|
|
// notify proc right at this moment...
|
|
if (fRequestGranted && !m_bInNotify)
|
|
{ // add (or update) the client list with this client
|
|
HRESULT hrTemp=NOERROR;
|
|
LPCLIENTINT pc=NULL;
|
|
|
|
// if the client is not already in the client list - add it
|
|
FindClient(lpClientGUID, &pc);
|
|
if (!pc)
|
|
{
|
|
hrTemp = UpdateClientInfo (lpClientGUID, lpfnQoSNotify);
|
|
if (FAILED(hrTemp))
|
|
hr = hrTemp;
|
|
}
|
|
|
|
// also, make a note that RequestResources has been called. This will
|
|
// make the QoS thread skip one heartbeat in order not call a client
|
|
// too early
|
|
m_nSkipHeartBeats = 1;
|
|
|
|
// we have at least one request, so spawn the QoS thread, if not
|
|
// already running
|
|
if (!m_hThread)
|
|
hrTemp = StartQoSThread();
|
|
|
|
}
|
|
|
|
out:
|
|
DISPLAYQOSOBJECT();
|
|
RELMUTEX(g_hQoSMutex);
|
|
out_nomutex:
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::RequestResources - leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::ReleaseResources
|
|
|
|
Purpose : Releases resources
|
|
|
|
Parameters: lpClientGUID - the GUID of the calling client (stream)
|
|
lpResourceRequestList - a list of resource requests that
|
|
the caller wants to reserve
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : The values in the resource list are ignored. The resources
|
|
specified are freed.
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::ReleaseResources (LPGUID lpClientGUID,
|
|
LPRESOURCEREQUESTLIST lpResourceRequestList)
|
|
{
|
|
ULONG i;
|
|
int nUnits=0;
|
|
BOOL fResourceFound=FALSE;
|
|
LPRESOURCEINT pResourceInt=NULL, *pPrevResourcefLink=NULL;
|
|
RESOURCEREQUEST *pResourceRequest=NULL;
|
|
HRESULT hr = NOERROR;
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::ReleaseResources\n"));
|
|
|
|
/*
|
|
* parameter validation
|
|
*/
|
|
|
|
// lpResourceRequestList should at least have a count DWORD
|
|
if (!lpResourceRequestList ||
|
|
IsBadReadPtr(lpResourceRequestList, (UINT) sizeof(DWORD)))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto out_nomutex;
|
|
}
|
|
|
|
DISPLAYPARAMETERS( RELEASE_RESOURCES_ID,
|
|
lpClientGUID,
|
|
lpResourceRequestList,
|
|
0,
|
|
0,
|
|
0);
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
if (!m_bQoSEnabled)
|
|
// just return
|
|
goto out;
|
|
|
|
// for each requested resource
|
|
pResourceRequest=lpResourceRequestList->aRequests;
|
|
for (i=0; i < lpResourceRequestList->cRequests; i++)
|
|
{
|
|
// make sure we start with no error (caller might not cleared last hresult)
|
|
pResourceRequest[i].hResult = NOERROR;
|
|
pResourceInt = m_pResourceList;
|
|
fResourceFound = FALSE;
|
|
// find the resource
|
|
while (pResourceInt)
|
|
{
|
|
if (pResourceInt->resource.resourceID == pResourceRequest[i].resourceID)
|
|
{ // resource found
|
|
// free the local copy of the request
|
|
pResourceRequest[i].hResult = FreeResourceRequest(lpClientGUID,
|
|
pResourceInt,
|
|
&nUnits);
|
|
|
|
// if succeeded, claim the units back
|
|
if (SUCCEEDED(pResourceRequest[i].hResult) && (nUnits >= 0))
|
|
{
|
|
// add the freed units
|
|
pResourceInt->nNowAvailUnits += nUnits;
|
|
// in case something went wrong and we now have more available units
|
|
// than total ones
|
|
// NOTE: the ASSERT below is no longer proper. If SetResources was called,
|
|
// and decreased the total units for a resource while there were
|
|
// requests on this resource, the available units for this resource
|
|
// might exceed the total one if released. Since QoS will auto-repair
|
|
// this in the next notify cycle, the window for this is very small
|
|
// ASSERT(!(pResourceInt->nNowAvailUnits > pResourceInt->resource.nUnits));
|
|
if (pResourceInt->nNowAvailUnits > pResourceInt->resource.nUnits)
|
|
{ // we don't want to have more available units than total
|
|
pResourceInt->nNowAvailUnits = pResourceInt->resource.nUnits;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no such request
|
|
pResourceRequest[i].hResult = QOS_E_NO_SUCH_REQUEST;
|
|
hr = QOS_E_REQ_ERRORS;
|
|
}
|
|
|
|
fResourceFound = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
// not this one. try next one.
|
|
pResourceInt = pResourceInt->fLink;
|
|
|
|
} // while
|
|
|
|
if (!fResourceFound)
|
|
{
|
|
pResourceRequest[i].hResult = QOS_E_NO_SUCH_RESOURCE;
|
|
hr = QOS_E_REQ_ERRORS;
|
|
}
|
|
|
|
// next request
|
|
}
|
|
|
|
// if no requests left, can let the notification thread go...
|
|
if (m_hThread &&
|
|
!AnyRequests())
|
|
{
|
|
// stop the thread
|
|
StopQoSThread();
|
|
}
|
|
|
|
out:
|
|
DISPLAYQOSOBJECT();
|
|
RELMUTEX(g_hQoSMutex);
|
|
|
|
out_nomutex:
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::ReleaseResources - leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::SetResources
|
|
|
|
Purpose : Sets the available resources on the QoS module
|
|
|
|
Parameters: lpResourceList - list of resources and their availability
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::SetResources (LPRESOURCELIST lpResourceList)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
ULONG i;
|
|
BOOL fResourceFound=FALSE;
|
|
LPRESOURCEINT pResourceInt=NULL, *pPrevResourcefLink=NULL;
|
|
RESOURCE *pResource=NULL;
|
|
|
|
RegEntry reQoSResourceRoot(REGKEY_QOS_RESOURCES,
|
|
HKEY_LOCAL_MACHINE,
|
|
FALSE,
|
|
KEY_READ);
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::SetResources\n"));
|
|
|
|
/*
|
|
* parameter validation
|
|
*/
|
|
|
|
// lpResourceList should at least have a count DWORD
|
|
if (!lpResourceList || IsBadReadPtr(lpResourceList, (UINT) sizeof(DWORD)))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto out_nomutex;
|
|
}
|
|
|
|
DISPLAYPARAMETERS( SET_RESOURCES_ID,
|
|
lpResourceList,
|
|
0,
|
|
0,
|
|
0,
|
|
0);
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
if (!m_bQoSEnabled)
|
|
// just return
|
|
goto out;
|
|
|
|
/*
|
|
* Get configurable resource info
|
|
*/
|
|
|
|
pResource=lpResourceList->aResources;
|
|
for (i=0; i < lpResourceList->cResources; i++)
|
|
{
|
|
TCHAR szKey[10]; // should be way enough for a resource ID
|
|
int nUnits=INT_MAX;
|
|
int nLeaveUnused=0;
|
|
|
|
// build and open the key
|
|
wsprintf(szKey, "%d", pResource[i].resourceID);
|
|
|
|
RegEntry reQosResource(szKey, reQoSResourceRoot.GetKey(), FALSE, KEY_READ);
|
|
|
|
|
|
// MaxUnits:
|
|
// run through the list of resources and make sure none of the
|
|
// resources was set to a number of units above the allowed maximum
|
|
// if it was, trim and warn
|
|
|
|
// get maximum numbers for the resource, if any, from the registry
|
|
nUnits = reQosResource.GetNumberIniStyle(TEXT("MaxUnits"), INT_MAX);
|
|
|
|
// is the client trying to set the resource to a higher value ?
|
|
if (pResource[i].nUnits > nUnits)
|
|
{
|
|
pResource[i].nUnits = nUnits;
|
|
hr = QOS_W_MAX_UNITS_EXCEEDED;
|
|
}
|
|
|
|
// LeaveUnused:
|
|
// leave some of the resource unused, as configured
|
|
|
|
// use different default value depending on the resource
|
|
switch (pResource[i].resourceID)
|
|
{
|
|
case RESOURCE_OUTGOING_BANDWIDTH:
|
|
nLeaveUnused = 30;
|
|
break;
|
|
default:
|
|
nLeaveUnused = 10;
|
|
break;
|
|
}
|
|
|
|
nLeaveUnused = reQosResource.GetNumberIniStyle( TEXT("LeaveUnused"),
|
|
nLeaveUnused);
|
|
|
|
pResource[i].nUnits = (pResource[i].nUnits * (100 - nLeaveUnused)) / 100;
|
|
}
|
|
|
|
/*
|
|
* Add the resource to the list
|
|
*/
|
|
|
|
// run through the input resource list and store the resources
|
|
// resource availability is NOT accumulative
|
|
pResource=lpResourceList->aResources;
|
|
for (i=0; i < lpResourceList->cResources; i++)
|
|
{
|
|
pResourceInt = m_pResourceList;
|
|
pPrevResourcefLink = &m_pResourceList;
|
|
fResourceFound = FALSE;
|
|
while (pResourceInt != 0)
|
|
{
|
|
if (pResourceInt->resource.resourceID == pResource[i].resourceID)
|
|
{ // found a match
|
|
// did the total number of units change for this resource ?
|
|
if (pResourceInt->resource.nUnits != pResource[i].nUnits)
|
|
{
|
|
// update the now available units
|
|
// since we could end up with less units than what was allocated
|
|
// we are issuing a NotifyNow at the end of this call
|
|
pResourceInt->nNowAvailUnits = pResource[i].nUnits -
|
|
(pResourceInt->resource.nUnits -
|
|
pResourceInt->nNowAvailUnits);
|
|
if (pResourceInt->nNowAvailUnits < 0)
|
|
pResourceInt->nNowAvailUnits = 0;
|
|
}
|
|
|
|
// override the previous setting
|
|
RtlCopyMemory( &(pResourceInt->resource),
|
|
&(pResource[i]),
|
|
sizeof(RESOURCE));
|
|
fResourceFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
// not this one. try next one.
|
|
pPrevResourcefLink = &(pResourceInt->fLink);
|
|
pResourceInt = pResourceInt->fLink;
|
|
|
|
} // while
|
|
|
|
if (fResourceFound)
|
|
continue;
|
|
|
|
// not found. add the resource
|
|
pResourceInt = (LPRESOURCEINT) MEMALLOC(sizeof(RESOURCEINT));
|
|
ASSERT(pResourceInt);
|
|
if (!pResourceInt)
|
|
{
|
|
ERRORMSG(("IQoS::SetResources: MEMALLOC failed on RESOURCEINT\n"));
|
|
hr = E_OUTOFMEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// copy the resource in
|
|
RtlCopyMemory( &(pResourceInt->resource),
|
|
&(pResource[i]),
|
|
sizeof(RESOURCE));
|
|
pResourceInt->fLink = NULL;
|
|
pResourceInt->nNowAvailUnits = pResourceInt->resource.nUnits;
|
|
*pPrevResourcefLink = pResourceInt;
|
|
|
|
// increment the number of resources we're tracking
|
|
// this number will never go down
|
|
m_cResources++;
|
|
|
|
// next resource
|
|
|
|
} // for
|
|
|
|
// since there was a possible change in the resource availability,
|
|
// run an immediate notification cycle
|
|
if (SUCCEEDED(hr))
|
|
NotifyNow();
|
|
|
|
out:
|
|
DISPLAYQOSOBJECT();
|
|
RELMUTEX(g_hQoSMutex);
|
|
|
|
out_nomutex:
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::SetResources - leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::GetResources
|
|
|
|
Purpose : Gets the list of resources available to the QoS module
|
|
|
|
Parameters: lppResourceList - an address where QoS will place a pointer
|
|
to a buffer with the list of resources available to QoS.
|
|
The caller must use CQoS::FreeBuffer to free this buffer.
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::GetResources (LPRESOURCELIST *lppResourceList)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
ULONG i;
|
|
LPRESOURCELIST prl=NULL;
|
|
LPRESOURCEINT pResourceInt=NULL;
|
|
RESOURCE *pResource=NULL;
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::GetResources\n"));
|
|
|
|
/*
|
|
* parameter validation
|
|
*/
|
|
|
|
// lpResourceList should at least have a count DWORD
|
|
if (!lppResourceList || IsBadWritePtr(lppResourceList, (UINT) sizeof(DWORD)))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto out_nomutex;
|
|
}
|
|
|
|
// no list yet
|
|
*lppResourceList = NULL;
|
|
|
|
if (!m_bQoSEnabled)
|
|
// just return
|
|
goto out_nomutex;
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
/*
|
|
* Get resource info
|
|
*/
|
|
|
|
// allocate a buffer for the resources info
|
|
prl = (LPRESOURCELIST) MEMALLOC(sizeof(RESOURCELIST) +
|
|
((LONG_PTR)m_cResources-1)*sizeof(RESOURCE));
|
|
if (!prl)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
ERRORMSG(("GetResources: MEMALLOC failed\n"));
|
|
goto out;
|
|
}
|
|
|
|
RtlZeroMemory((PVOID) prl, sizeof(RESOURCELIST) +
|
|
((LONG_PTR)m_cResources-1)*sizeof(RESOURCE));
|
|
|
|
// now fill in the information
|
|
prl->cResources = m_cResources;
|
|
pResourceInt=m_pResourceList;
|
|
for (i=0; i < m_cResources; i++)
|
|
{
|
|
ASSERT(pResourceInt);
|
|
|
|
// see if we have a NULL resource pointer
|
|
// shouldn't happen, but we shouldn't crash if it does
|
|
if (!pResourceInt)
|
|
{
|
|
hr = QOS_E_INTERNAL_ERROR;
|
|
ERRORMSG(("GetResources: bad QoS internal resource list\n"));
|
|
goto out;
|
|
}
|
|
|
|
// copy the resource info into the buffer
|
|
RtlCopyMemory( &(prl->aResources[i]),
|
|
&(pResourceInt->resource),
|
|
sizeof(RESOURCE));
|
|
|
|
// next resource
|
|
pResourceInt = pResourceInt->fLink;
|
|
} // for
|
|
|
|
*lppResourceList = prl;
|
|
|
|
out:
|
|
DISPLAYQOSOBJECT();
|
|
RELMUTEX(g_hQoSMutex);
|
|
|
|
out_nomutex:
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::GetResources - leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::SetClients
|
|
|
|
Purpose : Tells the QoS module what are the priorities of the requesting
|
|
streams. This allows the QoS module to allocate resources
|
|
appropriately.
|
|
|
|
Parameters: lpClientList - list of clients and their respective
|
|
priorities
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : client info will override an already existing info for this
|
|
client
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::SetClients(LPCLIENTLIST lpClientList)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
ULONG i;
|
|
BOOL fClientFound=FALSE;
|
|
LPCLIENTINT pClientInt=NULL, *pPrevClientfLink=NULL, pClientNew=NULL;;
|
|
LPCLIENT pClient=NULL;
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::SetClients\n"));
|
|
|
|
/*
|
|
* parameter validation
|
|
*/
|
|
|
|
// lpClientList should at least have a count DWORD
|
|
if (!lpClientList || IsBadReadPtr(lpClientList, (UINT) sizeof(DWORD)))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto out_nomutex;
|
|
}
|
|
|
|
DISPLAYPARAMETERS( SET_CLIENTS_ID,
|
|
lpClientList,
|
|
0,
|
|
0,
|
|
0,
|
|
0);
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
if (!m_bQoSEnabled)
|
|
// just return
|
|
goto out;
|
|
|
|
// first remove existing clients that are being set again
|
|
// this will make it easier to store clients in a priority order
|
|
pClient=lpClientList->aClients;
|
|
for (i=0; i < lpClientList->cClients; i++)
|
|
{
|
|
pClientInt = m_pClientList;
|
|
pPrevClientfLink = &m_pClientList;
|
|
fClientFound = FALSE;
|
|
while (pClientInt != 0)
|
|
{
|
|
if (COMPARE_GUIDS( &(pClientInt->client.guidClientGUID),
|
|
&(pClient[i].guidClientGUID)))
|
|
{ // found a match
|
|
LPCLIENTINT pClientIntNext=pClientInt->fLink;
|
|
|
|
// special case for internal calls from RequestResources
|
|
// we want to preserve the original priority before freeing
|
|
if (pClient[i].priority == QOS_LOWEST_PRIORITY)
|
|
pClient[i].priority = pClientInt->client.priority;
|
|
|
|
// free the requests for this client
|
|
// NOTE: we're not going to recreate the request list from
|
|
// the one in the resource list. it will be created on the
|
|
// fly when needed.
|
|
FreeListOfRequests(&(pClientInt->pRequestList));
|
|
|
|
// free the client record
|
|
MEMFREE(pClientInt);
|
|
*pPrevClientfLink = pClientIntNext;
|
|
fClientFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
// not this one. try next one.
|
|
pPrevClientfLink = &(pClientInt->fLink);
|
|
pClientInt = pClientInt->fLink;
|
|
|
|
} // while
|
|
|
|
// next resource
|
|
|
|
} // for
|
|
|
|
// now store the clients in the input list in priority order
|
|
pClient=lpClientList->aClients;
|
|
for (i=0; i < lpClientList->cClients; i++)
|
|
{
|
|
pClientInt = m_pClientList;
|
|
pPrevClientfLink = &m_pClientList;
|
|
while (pClientInt != 0)
|
|
{
|
|
// as long as the priority of the new client is higher than or equal to the one
|
|
// in the list, we continue to traverse the list
|
|
if (pClient[i].priority < pClientInt->client.priority)
|
|
{ // this is the place to insert this client
|
|
break;
|
|
}
|
|
|
|
// not time to insert yet. next client
|
|
pPrevClientfLink = &(pClientInt->fLink);
|
|
pClientInt = pClientInt->fLink;
|
|
|
|
} // while
|
|
|
|
// not found. add the client
|
|
pClientNew = (LPCLIENTINT) MEMALLOC(sizeof(CLIENTINT));
|
|
ASSERT(pClientNew);
|
|
if (!pClientNew)
|
|
{
|
|
ERRORMSG(("IQoS::SetClients: MEMALLOC failed on CLIENTINT\n"));
|
|
hr = E_OUTOFMEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// copy the resource in
|
|
RtlCopyMemory( &(pClientNew->client),
|
|
&(pClient[i]),
|
|
sizeof(CLIENT));
|
|
pClientNew->fLink = pClientInt;
|
|
*pPrevClientfLink = pClientNew;
|
|
|
|
// next resource
|
|
|
|
} // for
|
|
|
|
out:
|
|
DISPLAYQOSOBJECT();
|
|
RELMUTEX(g_hQoSMutex);
|
|
|
|
out_nomutex:
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::SetClients -leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::NotifyNow
|
|
|
|
Purpose : Tells the QoS module to initiate a notification cycle as
|
|
soon as possible.
|
|
|
|
Parameters: None
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : Don't call from within a notify proc.
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::NotifyNow(void)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::NotifyNow\n"));
|
|
|
|
SetEvent(m_evImmediateNotify);
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::NotifyNow - leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::FreeBuffer
|
|
|
|
Purpose : Frees a buffer allocated by the QoS module.
|
|
|
|
Parameters: lpBuffer - a pointer to the buffer to free. This buffer must
|
|
have been allocated by QoS
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::FreeBuffer(LPVOID lpBuffer)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::FreeBuffer\n"));
|
|
|
|
if (lpBuffer)
|
|
MEMFREE(lpBuffer);
|
|
|
|
DEBUGMSG(ZONE_IQOS,("IQoS::FreeBuffer - leave, hr=0x%x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::CQoS
|
|
|
|
Purpose : The CQoS object constructor
|
|
|
|
Parameters: none
|
|
|
|
Returns : None
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
inline CQoS::CQoS (void)
|
|
{
|
|
m_cRef = 0; // will be bumped to 1 by the explicit QI in CreateQoS
|
|
m_pResourceList = NULL;
|
|
m_cResources = 0;
|
|
m_pClientList = NULL;
|
|
m_evThreadExitSignal = NULL;
|
|
m_evImmediateNotify = NULL;
|
|
m_hThread = NULL;
|
|
m_bQoSEnabled = TRUE;
|
|
m_bInNotify = FALSE;
|
|
m_nSkipHeartBeats = 0;
|
|
m_hWnd = NULL;
|
|
m_nLeaveForNextPri = 5;
|
|
// can't use ++ because RISC processors may translate to several instructions
|
|
InterlockedIncrement((long *) &g_cQoSObjects);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::~CQoS
|
|
|
|
Purpose : The CQoS object destructor
|
|
|
|
Parameters: none
|
|
|
|
Returns : None
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
inline CQoS::~CQoS (void)
|
|
{
|
|
// can't use ++ because RISC processors may translate to several instructions
|
|
InterlockedDecrement((long *) &g_cQoSObjects);
|
|
g_pQoS = (CQoS *)NULL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CQoS::Initialize
|
|
|
|
Purpose : Initializes the QoS object
|
|
|
|
Parameters: None
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT CQoS::Initialize(void)
|
|
{
|
|
HRESULT hr=NOERROR;
|
|
OSVERSIONINFO tVersionInfo;
|
|
|
|
/*
|
|
* Initialize the object
|
|
*/
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
|
|
// first see if QoS is enabled
|
|
RegEntry reQoS(QOS_KEY,
|
|
HKEY_LOCAL_MACHINE,
|
|
FALSE,
|
|
KEY_READ);
|
|
|
|
m_bQoSEnabled = reQoS.GetNumberIniStyle(TEXT("Enable"), TRUE);
|
|
|
|
if (!m_bQoSEnabled)
|
|
{
|
|
// don't create a thread, but return success
|
|
DEBUGMSG(ZONE_IQOS,("Initialize: QoS not enabled\n"));
|
|
hr = NOERROR;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* QoS notification thread
|
|
*/
|
|
|
|
// create an event that will be used to signal the thread to terminate
|
|
// CreateEvent(No security attr's, no manual reset, not signalled, no name)
|
|
m_evThreadExitSignal = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
ASSERT(m_evThreadExitSignal);
|
|
if (!(m_evThreadExitSignal))
|
|
{
|
|
ERRORMSG(("Initialize: Exit event creation failed: %x\n", GetLastError()));
|
|
hr = E_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
// create an event that will be used to signal the thread to initiate
|
|
// an immediate notify cycle
|
|
// CreateEvent(No security attr's, no manual reset, not signalled, no name)
|
|
m_evImmediateNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
ASSERT(m_evImmediateNotify);
|
|
if (!(m_evImmediateNotify))
|
|
{
|
|
ERRORMSG(("Initialize: Immediate notify event creation failed: %x\n", GetLastError()));
|
|
hr = E_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
|
|
//Set the OS flag
|
|
tVersionInfo.dwOSVersionInfoSize=sizeof (OSVERSIONINFO);
|
|
if (!(GetVersionEx (&tVersionInfo))) {
|
|
ERRORMSG(("Initialize: Couldn't get version info: %x\n", GetLastError()));
|
|
hr = E_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
if (tVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
|
|
bWin9x=TRUE;
|
|
}else {
|
|
if (tVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
|
bWin9x=FALSE;
|
|
}else {
|
|
//How on earth did we get here?
|
|
ASSERT (0);
|
|
hr=E_FAIL;
|
|
goto out;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
out:
|
|
RELMUTEX(g_hQoSMutex);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CreateQoS
|
|
|
|
Purpose : Creates the QoS object and return an IQoS interface pointer
|
|
|
|
Parameters:
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : CreateQoS will only create one instance of the QoS object.
|
|
Additional calls will return the same interface pointer
|
|
|
|
***************************************************************************/
|
|
extern "C" HRESULT WINAPI CreateQoS ( IUnknown *punkOuter,
|
|
REFIID riid,
|
|
void **ppv)
|
|
{
|
|
CQoS *pQoS;
|
|
HRESULT hr = NOERROR;
|
|
|
|
*ppv = 0;
|
|
if (punkOuter)
|
|
return CLASS_E_NOAGGREGATION;
|
|
|
|
/*
|
|
* instantiate the QoS object
|
|
*/
|
|
|
|
ACQMUTEX(g_hQoSMutex);
|
|
|
|
// only instantiate a new object if it doesn't already exist
|
|
if (g_cQoSObjects == 0)
|
|
{
|
|
if (!(pQoS = new CQoS))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// Save pointer
|
|
g_pQoS = pQoS;
|
|
|
|
// initialize the QoS object
|
|
hr = pQoS->Initialize();
|
|
|
|
}
|
|
else
|
|
{
|
|
// this is the case when the object was already instantiaed in this
|
|
// process, so we only want to return the object pointer.
|
|
pQoS = g_pQoS;
|
|
}
|
|
|
|
// must have only one QoS object at this point
|
|
ASSERT(g_cQoSObjects == 1);
|
|
|
|
RELMUTEX(g_hQoSMutex);
|
|
|
|
// get the IQoS interface for the caller
|
|
if (pQoS)
|
|
{
|
|
// QueryInterface will get us the interface pointer and will AddRef
|
|
// the object
|
|
hr = pQoS->QueryInterface (riid, ppv);
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
out:
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : QoSEntryPoint
|
|
|
|
Purpose : Called by nac.dll (where the QoS lives these days) to make
|
|
the necessary process attach and detach initializations
|
|
|
|
Parameters: same as a standard DllEntryPoint
|
|
|
|
Returns :
|
|
|
|
|
|
***************************************************************************/
|
|
extern "C" BOOL APIENTRY QoSEntryPoint( HINSTANCE hInstDLL,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved)
|
|
{
|
|
BOOL fRet=TRUE;
|
|
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
QOSDEBUGINIT();
|
|
|
|
// create a no-name mutex to control access to QoS object data
|
|
if (!g_hQoSMutex)
|
|
{
|
|
g_hQoSMutex = CreateMutex(NULL, FALSE, NULL);
|
|
ASSERT(g_hQoSMutex);
|
|
if (!g_hQoSMutex)
|
|
{
|
|
ERRORMSG(("QoSEntryPoint: CreateMutex failed, 0x%x\n", GetLastError()));
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
if (g_hQoSMutex)
|
|
CloseHandle(g_hQoSMutex);
|
|
g_hQoSMutex = NULL;
|
|
DBGDEINIT(&ghDbgZoneQoS);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return fRet;
|
|
}
|