windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/qos/qos.cpp
2020-09-26 16:20:57 +08:00

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;
}