windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/qos/thread.cpp

379 lines
9 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*
- THREAD.CPP
-
* Microsoft NetMeeting
* Quality of Service DLL
* Quality of Service Notification Thread
*
* Revision History:
*
* When Who What
* -------- ------------------ ---------------------------------------
* 10.30.96 Yoram Yaacovi Created
*
* Functions:
* CQoS::StartQoSThread
* CQoS::StopQoSThread
* CQoS::QoSThread
* QoSThreadWrapper
*/
#include "precomp.h"
/***************************************************************************
Name : CQoS::StartQoSThread
Purpose : Starts a QoS notification thread
Parameters: None
Returns : HRESULT
Comment :
***************************************************************************/
HRESULT CQoS::StartQoSThread(void)
{
HRESULT hr=NOERROR;
HANDLE hThread;
DWORD idThread;
// prepare the structrue for the thread
// now spwan the thread
hThread = CreateThread (NULL,
0, // Default (same as main thread) stack size
(LPTHREAD_START_ROUTINE) QoSThreadWrapper,
(LPVOID) this, // Pass the object pointer to thread
0, // Run thread now
&idThread);
ASSERT(hThread);
if (!hThread)
{
ERRORMSG(("StartQoSThread: failed to create thread: %x\n", GetLastError()));
hr = E_FAIL;
}
m_hThread = hThread;
return hr;
}
/***************************************************************************
Name : CQoS::StopQoSThread
Purpose : Stops a QoS notification thread
Parameters: None
Returns : HRESULT
Comment : It is assumed that when the thread that calls StopQoSThread
has the QoS mutex.
***************************************************************************/
HRESULT CQoS::StopQoSThread(void)
{
HRESULT hr=NOERROR;
HANDLE evExitSignal=m_evThreadExitSignal;
DWORD dwExitCode=0;
ULONG i=0;
HANDLE hThread=NULL;
if (m_hThread)
{
// tell the thread to exit
SetEvent(evExitSignal);
hThread = m_hThread;
m_hThread = NULL;
// the thread might need the mutex to exit
RELMUTEX(g_hQoSMutex);
// wait for the thread to terminate
if (WaitForSingleObject(hThread, 1000) == WAIT_TIMEOUT)
{
// if it didn't take its own life, you take it...
DEBUGMSG(ZONE_THREAD,("StopQoSThread: QoS thread didn't properly terminate within 1 second. Terminating it\n"));
TerminateThread(hThread, 0);
}
// re-acquire the mutex (for balance)
ACQMUTEX(g_hQoSMutex);
CloseHandle(hThread);
}
return hr;
}
/***************************************************************************
Name : CQoS::NotifyQoSClient
Purpose : Notifies a QoS client on change in resource availability
Parameters:
Returns : HRESULT
Comment : prrl is a pointer to a list of resource requests. This
list has two purposes:
1. The QoS module will fill the list with the current
availability of resources
2. The client will fill the list with its resource requests
The QoS module is allocating the memory for the resource
requests list. It will allocate one resource request per
each available resource.
***************************************************************************/
HRESULT CQoS::NotifyQoSClient(void)
{
HRESULT hr=NOERROR;
LPRESOURCEREQUESTLIST prrl=NULL;
LPRESOURCEINT pr=NULL;
LPCLIENTINT pc=NULL;
ULONG cResources=m_cResources;
LPRESOURCEINT pResourceList=m_pResourceList;
ULONG i=0;
LPFNQOSNOTIFY pNotifyProc=NULL;
DWORD dwParam=NULL;
/*
* here's what happens:
*
* the QoS module creates a new resource list from the old one,
* making all resources fully available. It satisfies the new
* client resource requests from this new list.
*/
// don't bother if no clients or no resources
if (!m_pClientList || !m_pResourceList)
{
goto out;
}
// first update the request list for all clients
pc = m_pClientList;
while (pc)
{
UpdateRequestsForClient (&(pc->client.guidClientGUID));
pc = pc->fLink;
}
// we are going to wipe all requests from the resource
// lists, and set all resources back to full availability
pr = m_pResourceList;
while (pr)
{
// free the request list
FreeListOfRequests(&(pr->pRequestList));
// set the resource back to full availability
pr->nNowAvailUnits = pr->resource.nUnits;
// next resource
pr = pr->fLink;
}
/*
* Build resource request lists for each client and call it
*/
// allocate space for the resource list (which already includes
// space for one resource), plus (cResources-1) more resources
prrl = (LPRESOURCEREQUESTLIST) MEMALLOC(sizeof(RESOURCEREQUESTLIST) +
(cResources-1)*sizeof(RESOURCEREQUEST));
if (!prrl)
{
hr = E_OUTOFMEMORY;
ERRORMSG(("NotifyQoSClient: MEMALLOC failed in NotifyQoSClient\n"));
goto out;
}
RtlZeroMemory((PVOID) prrl, sizeof(RESOURCEREQUESTLIST) +
(cResources-1)*sizeof(RESOURCEREQUEST));
// call each client, in order of priority, with the available resource list
pc = m_pClientList;
while (pc)
{
LPFNQOSNOTIFY pNotifyProc=NULL;
DWORD_PTR dwParam=0;
LPGUID lpGUID=NULL;
ULONG i=0;
LPRESOURCEREQUESTINT pcrr=NULL;
ULONG nSamePriClients=1;
ULONG nLowerPriClients=0;
/*
* Building the request list
*/
pcrr = pc->pRequestList;
while (pcrr)
{
// remember the address of the notify proc for this client and its GUID
pNotifyProc = pcrr->pfnQoSNotify;
dwParam = pcrr->dwParam;
lpGUID = &(pcrr->guidClientGUID);
// add the resource to the requestlist we'll send to this client
prrl->aRequests[i].resourceID = pcrr->sResourceRequest.resourceID;
// find current availability of the resource
pr = m_pResourceList;
while (pr)
{
if (pr->resource.resourceID == pcrr->sResourceRequest.resourceID)
{
ULONG nNowAvailUnits=pr->nNowAvailUnits;
// find if there are other clients for this resource
FindClientsForResource( pr->resource.resourceID,
pc,
&nSamePriClients,
&nLowerPriClients);
// leave some of the resource for the next priority clients, if any
if (nLowerPriClients)
nNowAvailUnits = (nNowAvailUnits * (100 - m_nLeaveForNextPri)) / 100;
prrl->aRequests[i].nUnitsMin = nNowAvailUnits / nSamePriClients;
prrl->aRequests[i].nUnitsMax = nNowAvailUnits;
break;
}
// next resource
pr = pr->fLink;
}
// next request in the list we're making
i++;
// next request
pcrr = pcrr->fLink;
}
// if we have requests from this client, call its notify callback
prrl->cRequests = i;
if (pNotifyProc)
{
// call the notify callback
hr = (pNotifyProc)(prrl, dwParam);
if (SUCCEEDED(hr))
{
// the returned request list contains what the client wants
// request them on behalf of the client
// let RequestResources know that we're calling from the notify proc
m_bInNotify = TRUE;
hr = RequestResources(lpGUID, prrl, pNotifyProc, dwParam);
if (FAILED(hr))
{
ERRORMSG(("NotifyQoSClient: client returned bad resource request list\n"));
}
m_bInNotify = FALSE;
}
}
pc = pc->fLink;
}
out:
if (prrl)
MEMFREE(prrl);
return hr;
}
/***************************************************************************
Name : CQoS::QoSThread
Purpose : QoS notification thread
Parameters: None
Returns :
Comment :
***************************************************************************/
DWORD CQoS::QoSThread(void)
{
int nTimeout;
ULONG rc=0;
HANDLE evSignalExit=m_evThreadExitSignal;
HANDLE aHandles[2] = {m_evThreadExitSignal, m_evImmediateNotify};
// wake up every N seconds and notify clients
RegEntry reQoS(QOS_KEY,
HKEY_LOCAL_MACHINE,
FALSE,
KEY_READ);
nTimeout = reQoS.GetNumberIniStyle(TEXT("Timeout"), 3000);
while (1)
{
rc = WaitForMultipleObjects(2, aHandles, FALSE, nTimeout);
// if a timeout or a signal to do an immediate notify cycle...
if ((rc == WAIT_TIMEOUT) || ((rc - WAIT_OBJECT_0) == 1))
{ // ..do it
ACQMUTEX(g_hQoSMutex);
// NOTE: it is possible that while waiting on the mutex, the thread
// was stopped (no more requests). In this case, the thread will do
// a unnecessary (though harmless, since no requests) notify cycle
DEBUGMSG(ZONE_THREAD,("QoSThread: Notify thread heartbeat, why=%s\n",
(rc == WAIT_TIMEOUT ? "timeout" : "notify")));
// notify clients, unless this heartbeat should be skipped
if (m_nSkipHeartBeats == 0)
{
DEBUGMSG(ZONE_THREAD,("QoSThread: Notifying client\n"));
NotifyQoSClient();
}
// update the skip counter
(m_nSkipHeartBeats ? m_nSkipHeartBeats-- : 0);
RELMUTEX(g_hQoSMutex);
}
// anything else (WAIT_FAILED, Exit signal), bail out
else
break;
}
// this is just like ExitThread()
DEBUGMSG(ZONE_THREAD,("QoSThread: Notify thread exiting...\n"));
return 0L;
}
/***************************************************************************
Name : QoSThreadWrapper
Purpose : Wrapper for the QoS notification thread
Parameters: pQoS - pointer to the QoS object
Returns :
Comment :
***************************************************************************/
DWORD QoSThreadWrapper(CQoS *pQoS)
{
return pQoS->QoSThread();
}