You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
376 lines
9.3 KiB
376 lines
9.3 KiB
/*
|
|
- 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();
|
|
}
|
|
|