376 lines
9.3 KiB
C++
376 lines
9.3 KiB
C++
/*
|
||
- 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;
|
||
|
||
/*
|
||
* 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();
|
||
}
|
||
|