Source code of Windows XP (NT5)
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.

378 lines
9.0 KiB

  1. /*
  2. - THREAD.CPP
  3. -
  4. * Microsoft NetMeeting
  5. * Quality of Service DLL
  6. * Quality of Service Notification Thread
  7. *
  8. * Revision History:
  9. *
  10. * When Who What
  11. * -------- ------------------ ---------------------------------------
  12. * 10.30.96 Yoram Yaacovi Created
  13. *
  14. * Functions:
  15. * CQoS::StartQoSThread
  16. * CQoS::StopQoSThread
  17. * CQoS::QoSThread
  18. * QoSThreadWrapper
  19. */
  20. #include "precomp.h"
  21. /***************************************************************************
  22. Name : CQoS::StartQoSThread
  23. Purpose : Starts a QoS notification thread
  24. Parameters: None
  25. Returns : HRESULT
  26. Comment :
  27. ***************************************************************************/
  28. HRESULT CQoS::StartQoSThread(void)
  29. {
  30. HRESULT hr=NOERROR;
  31. HANDLE hThread;
  32. DWORD idThread;
  33. // prepare the structrue for the thread
  34. // now spwan the thread
  35. hThread = CreateThread (NULL,
  36. 0, // Default (same as main thread) stack size
  37. (LPTHREAD_START_ROUTINE) QoSThreadWrapper,
  38. (LPVOID) this, // Pass the object pointer to thread
  39. 0, // Run thread now
  40. &idThread);
  41. ASSERT(hThread);
  42. if (!hThread)
  43. {
  44. ERRORMSG(("StartQoSThread: failed to create thread: %x\n", GetLastError()));
  45. hr = E_FAIL;
  46. }
  47. m_hThread = hThread;
  48. return hr;
  49. }
  50. /***************************************************************************
  51. Name : CQoS::StopQoSThread
  52. Purpose : Stops a QoS notification thread
  53. Parameters: None
  54. Returns : HRESULT
  55. Comment : It is assumed that when the thread that calls StopQoSThread
  56. has the QoS mutex.
  57. ***************************************************************************/
  58. HRESULT CQoS::StopQoSThread(void)
  59. {
  60. HRESULT hr=NOERROR;
  61. HANDLE evExitSignal=m_evThreadExitSignal;
  62. DWORD dwExitCode=0;
  63. ULONG i=0;
  64. HANDLE hThread=NULL;
  65. if (m_hThread)
  66. {
  67. // tell the thread to exit
  68. SetEvent(evExitSignal);
  69. hThread = m_hThread;
  70. m_hThread = NULL;
  71. // the thread might need the mutex to exit
  72. RELMUTEX(g_hQoSMutex);
  73. // wait for the thread to terminate
  74. if (WaitForSingleObject(hThread, 1000) == WAIT_TIMEOUT)
  75. {
  76. // if it didn't take its own life, you take it...
  77. DEBUGMSG(ZONE_THREAD,("StopQoSThread: QoS thread didn't properly terminate within 1 second. Terminating it\n"));
  78. TerminateThread(hThread, 0);
  79. }
  80. // re-acquire the mutex (for balance)
  81. ACQMUTEX(g_hQoSMutex);
  82. CloseHandle(hThread);
  83. }
  84. return hr;
  85. }
  86. /***************************************************************************
  87. Name : CQoS::NotifyQoSClient
  88. Purpose : Notifies a QoS client on change in resource availability
  89. Parameters:
  90. Returns : HRESULT
  91. Comment : prrl is a pointer to a list of resource requests. This
  92. list has two purposes:
  93. 1. The QoS module will fill the list with the current
  94. availability of resources
  95. 2. The client will fill the list with its resource requests
  96. The QoS module is allocating the memory for the resource
  97. requests list. It will allocate one resource request per
  98. each available resource.
  99. ***************************************************************************/
  100. HRESULT CQoS::NotifyQoSClient(void)
  101. {
  102. HRESULT hr=NOERROR;
  103. LPRESOURCEREQUESTLIST prrl=NULL;
  104. LPRESOURCEINT pr=NULL;
  105. LPCLIENTINT pc=NULL;
  106. ULONG cResources=m_cResources;
  107. LPRESOURCEINT pResourceList=m_pResourceList;
  108. ULONG i=0;
  109. LPFNQOSNOTIFY pNotifyProc=NULL;
  110. DWORD dwParam=NULL;
  111. /*
  112. * here's what happens:
  113. *
  114. * the QoS module creates a new resource list from the old one,
  115. * making all resources fully available. It satisfies the new
  116. * client resource requests from this new list.
  117. */
  118. // don't bother if no clients or no resources
  119. if (!m_pClientList || !m_pResourceList)
  120. {
  121. goto out;
  122. }
  123. // first update the request list for all clients
  124. pc = m_pClientList;
  125. while (pc)
  126. {
  127. UpdateRequestsForClient (&(pc->client.guidClientGUID));
  128. pc = pc->fLink;
  129. }
  130. // we are going to wipe all requests from the resource
  131. // lists, and set all resources back to full availability
  132. pr = m_pResourceList;
  133. while (pr)
  134. {
  135. // free the request list
  136. FreeListOfRequests(&(pr->pRequestList));
  137. // set the resource back to full availability
  138. pr->nNowAvailUnits = pr->resource.nUnits;
  139. // next resource
  140. pr = pr->fLink;
  141. }
  142. /*
  143. * Build resource request lists for each client and call it
  144. */
  145. // allocate space for the resource list (which already includes
  146. // space for one resource), plus (cResources-1) more resources
  147. prrl = (LPRESOURCEREQUESTLIST) MEMALLOC(sizeof(RESOURCEREQUESTLIST) +
  148. (cResources-1)*sizeof(RESOURCEREQUEST));
  149. if (!prrl)
  150. {
  151. hr = E_OUTOFMEMORY;
  152. ERRORMSG(("NotifyQoSClient: MEMALLOC failed in NotifyQoSClient\n"));
  153. goto out;
  154. }
  155. RtlZeroMemory((PVOID) prrl, sizeof(RESOURCEREQUESTLIST) +
  156. (cResources-1)*sizeof(RESOURCEREQUEST));
  157. // call each client, in order of priority, with the available resource list
  158. pc = m_pClientList;
  159. while (pc)
  160. {
  161. LPFNQOSNOTIFY pNotifyProc=NULL;
  162. DWORD_PTR dwParam=0;
  163. LPGUID lpGUID=NULL;
  164. ULONG i=0;
  165. LPRESOURCEREQUESTINT pcrr=NULL;
  166. ULONG nSamePriClients=1;
  167. ULONG nLowerPriClients=0;
  168. /*
  169. * Building the request list
  170. */
  171. pcrr = pc->pRequestList;
  172. while (pcrr)
  173. {
  174. // remember the address of the notify proc for this client and its GUID
  175. pNotifyProc = pcrr->pfnQoSNotify;
  176. dwParam = pcrr->dwParam;
  177. lpGUID = &(pcrr->guidClientGUID);
  178. // add the resource to the requestlist we'll send to this client
  179. prrl->aRequests[i].resourceID = pcrr->sResourceRequest.resourceID;
  180. // find current availability of the resource
  181. pr = m_pResourceList;
  182. while (pr)
  183. {
  184. if (pr->resource.resourceID == pcrr->sResourceRequest.resourceID)
  185. {
  186. ULONG nNowAvailUnits=pr->nNowAvailUnits;
  187. // find if there are other clients for this resource
  188. FindClientsForResource( pr->resource.resourceID,
  189. pc,
  190. &nSamePriClients,
  191. &nLowerPriClients);
  192. // leave some of the resource for the next priority clients, if any
  193. if (nLowerPriClients)
  194. nNowAvailUnits = (nNowAvailUnits * (100 - m_nLeaveForNextPri)) / 100;
  195. prrl->aRequests[i].nUnitsMin = nNowAvailUnits / nSamePriClients;
  196. prrl->aRequests[i].nUnitsMax = nNowAvailUnits;
  197. break;
  198. }
  199. // next resource
  200. pr = pr->fLink;
  201. }
  202. // next request in the list we're making
  203. i++;
  204. // next request
  205. pcrr = pcrr->fLink;
  206. }
  207. // if we have requests from this client, call its notify callback
  208. prrl->cRequests = i;
  209. if (pNotifyProc)
  210. {
  211. // call the notify callback
  212. hr = (pNotifyProc)(prrl, dwParam);
  213. if (SUCCEEDED(hr))
  214. {
  215. // the returned request list contains what the client wants
  216. // request them on behalf of the client
  217. // let RequestResources know that we're calling from the notify proc
  218. m_bInNotify = TRUE;
  219. hr = RequestResources(lpGUID, prrl, pNotifyProc, dwParam);
  220. if (FAILED(hr))
  221. {
  222. ERRORMSG(("NotifyQoSClient: client returned bad resource request list\n"));
  223. }
  224. m_bInNotify = FALSE;
  225. }
  226. }
  227. pc = pc->fLink;
  228. }
  229. out:
  230. if (prrl)
  231. MEMFREE(prrl);
  232. return hr;
  233. }
  234. /***************************************************************************
  235. Name : CQoS::QoSThread
  236. Purpose : QoS notification thread
  237. Parameters: None
  238. Returns :
  239. Comment :
  240. ***************************************************************************/
  241. DWORD CQoS::QoSThread(void)
  242. {
  243. int nTimeout;
  244. ULONG rc=0;
  245. HANDLE evSignalExit=m_evThreadExitSignal;
  246. HANDLE aHandles[2] = {m_evThreadExitSignal, m_evImmediateNotify};
  247. // wake up every N seconds and notify clients
  248. RegEntry reQoS(QOS_KEY,
  249. HKEY_LOCAL_MACHINE,
  250. FALSE,
  251. KEY_READ);
  252. nTimeout = reQoS.GetNumberIniStyle(TEXT("Timeout"), 3000);
  253. while (1)
  254. {
  255. rc = WaitForMultipleObjects(2, aHandles, FALSE, nTimeout);
  256. // if a timeout or a signal to do an immediate notify cycle...
  257. if ((rc == WAIT_TIMEOUT) || ((rc - WAIT_OBJECT_0) == 1))
  258. { // ..do it
  259. ACQMUTEX(g_hQoSMutex);
  260. // NOTE: it is possible that while waiting on the mutex, the thread
  261. // was stopped (no more requests). In this case, the thread will do
  262. // a unnecessary (though harmless, since no requests) notify cycle
  263. DEBUGMSG(ZONE_THREAD,("QoSThread: Notify thread heartbeat, why=%s\n",
  264. (rc == WAIT_TIMEOUT ? "timeout" : "notify")));
  265. // notify clients, unless this heartbeat should be skipped
  266. if (m_nSkipHeartBeats == 0)
  267. {
  268. DEBUGMSG(ZONE_THREAD,("QoSThread: Notifying client\n"));
  269. NotifyQoSClient();
  270. }
  271. // update the skip counter
  272. (m_nSkipHeartBeats ? m_nSkipHeartBeats-- : 0);
  273. RELMUTEX(g_hQoSMutex);
  274. }
  275. // anything else (WAIT_FAILED, Exit signal), bail out
  276. else
  277. break;
  278. }
  279. // this is just like ExitThread()
  280. DEBUGMSG(ZONE_THREAD,("QoSThread: Notify thread exiting...\n"));
  281. return 0L;
  282. }
  283. /***************************************************************************
  284. Name : QoSThreadWrapper
  285. Purpose : Wrapper for the QoS notification thread
  286. Parameters: pQoS - pointer to the QoS object
  287. Returns :
  288. Comment :
  289. ***************************************************************************/
  290. DWORD QoSThreadWrapper(CQoS *pQoS)
  291. {
  292. return pQoS->QoSThread();
  293. }
  294.