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.

495 lines
10 KiB

  1. /*++
  2. Copyright (c) 1996,1997 Microsoft Corporation
  3. Module Name:
  4. TIMER.C
  5. Abstract:
  6. Handle adjusting timer resolution for throttling and do thread pool
  7. Author:
  8. Aaron Ogus (aarono)
  9. Environment:
  10. Win32
  11. Revision History:
  12. Date Author Description
  13. ====== ====== ============================================================
  14. 6/04/98 aarono Original
  15. --*/
  16. #include <windows.h>
  17. #include "newdpf.h"
  18. #include <mmsystem.h>
  19. #include <dplay.h>
  20. #include <dplaysp.h>
  21. #include <dplaypr.h>
  22. #include "mydebug.h"
  23. #include "arpd.h"
  24. #include "arpdint.h"
  25. #include "macros.h"
  26. #include "mytimer.h"
  27. #define DEFAULT_TIME_RESOLUTION 20 /* ms */
  28. #define MIN_TIMER_THREADS 1
  29. #define MAX_TIMER_THREADS 5
  30. VOID QueueTimeout(PMYTIMER pTimer);
  31. DWORD WINAPI TimerWorkerThread(LPVOID foo);
  32. // Timer Resolution adjustments;
  33. DWORD dwOldPeriod=DEFAULT_TIME_RESOLUTION;
  34. DWORD dwCurrentPeriod=DEFAULT_TIME_RESOLUTION;
  35. DWORD dwPeriodInUse=DEFAULT_TIME_RESOLUTION;
  36. BILINK MyTimerList={&MyTimerList, &MyTimerList};
  37. CRITICAL_SECTION MyTimerListLock;
  38. LPFPOOL pTimerPool=NULL;
  39. DWORD uWorkaroundTimerID;
  40. DWORD twInitCount=0; //number of times init called, only inits on 0->1, deinit on 1->0
  41. DWORD Unique=0;
  42. CRITICAL_SECTION ThreadListLock; // locks ALL this stuff.
  43. BILINK ThreadList={&ThreadList,&ThreadList}; // ThreadPool grabs work from here.
  44. DWORD nThreads=0; // number of running threads.
  45. DWORD ActiveReq=0; // number of requests being processed.
  46. DWORD PeakReqs=0;
  47. DWORD bShutDown=FALSE;
  48. DWORD bAlreadyCleanedUp=FALSE;
  49. DWORD KillCount=0;
  50. DWORD ExtraSignals=0;
  51. HANDLE hWorkToDoSem;
  52. HANDLE hShutDownPeriodicTimer;
  53. DWORD_PTR uAdjustResTimer=0;
  54. DWORD AdjustResUnique=0;
  55. DWORD_PTR uAdjustThreadsTimer=0;
  56. DWORD AdjustThreadsUnique=0;
  57. // Sometimes scheduled retry timers don't run. This runs every 10 seconds to catch
  58. // timers that should have been expired.
  59. void CALLBACK PeriodicTimer (UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
  60. {
  61. DWORD time;
  62. PMYTIMER pTimerWalker;
  63. BILINK *pBilink;
  64. DWORD dwReleaseCount=0;
  65. DWORD slowcount=0;
  66. if(bShutDown){
  67. if(!InterlockedExchange(&bAlreadyCleanedUp,1)){
  68. while(nThreads && slowcount < (60000/50)){ // don't wait more than 60 seconds.
  69. slowcount++;
  70. Sleep(50);
  71. }
  72. if(!nThreads){ // better to leak than to crash.
  73. DeleteCriticalSection(&MyTimerListLock);
  74. DeleteCriticalSection(&ThreadListLock);
  75. }
  76. timeKillEvent(uID);
  77. ASSERT(hShutDownPeriodicTimer);
  78. SetEvent(hShutDownPeriodicTimer);
  79. }
  80. return;
  81. }
  82. time=timeGetTime()+(dwCurrentPeriod/2);
  83. Lock(&MyTimerListLock);
  84. Lock(&ThreadListLock);
  85. pBilink=MyTimerList.next;
  86. while(pBilink!=&MyTimerList){
  87. pTimerWalker=CONTAINING_RECORD(pBilink, MYTIMER, Bilink);
  88. pBilink=pBilink->next;
  89. if(((INT)(time-pTimerWalker->TimeOut) > 0)){
  90. Delete(&pTimerWalker->Bilink);
  91. InsertBefore(&pTimerWalker->Bilink, &ThreadList);
  92. pTimerWalker->TimerState=QueuedForThread;
  93. dwReleaseCount++;
  94. } else {
  95. break;
  96. }
  97. }
  98. ActiveReq += dwReleaseCount;
  99. if(ActiveReq > PeakReqs){
  100. PeakReqs=ActiveReq;
  101. }
  102. ReleaseSemaphore(hWorkToDoSem,dwReleaseCount,NULL);
  103. Unlock(&ThreadListLock);
  104. Unlock(&MyTimerListLock);
  105. }
  106. #define min(a,b) (((a) < (b)) ? (a) : (b))
  107. VOID CALLBACK AdjustTimerResolution(UINT_PTR uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
  108. {
  109. DWORD dwWantPeriod;
  110. dwWantPeriod=min(dwCurrentPeriod,dwOldPeriod);
  111. dwOldPeriod=dwCurrentPeriod;
  112. dwCurrentPeriod=DEFAULT_TIME_RESOLUTION;
  113. if(dwPeriodInUse != dwWantPeriod){
  114. dwPeriodInUse=dwWantPeriod;
  115. timeKillEvent(uWorkaroundTimerID);
  116. uWorkaroundTimerID=timeSetEvent(dwPeriodInUse, dwPeriodInUse, PeriodicTimer, 0, TIME_PERIODIC);
  117. }
  118. uAdjustResTimer=SetMyTimer(1000,500,AdjustTimerResolution,0,&AdjustResUnique);
  119. }
  120. VOID CALLBACK AdjustThreads(UINT_PTR uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
  121. {
  122. Lock(&ThreadListLock);
  123. if((PeakReqs < nThreads) && nThreads){
  124. KillCount=nThreads-PeakReqs;
  125. ReleaseSemaphore(hWorkToDoSem, KillCount, NULL);
  126. }
  127. PeakReqs=0;
  128. Unlock(&ThreadListLock);
  129. uAdjustThreadsTimer=SetMyTimer(60000,500,AdjustThreads,0,&AdjustThreadsUnique);
  130. }
  131. VOID SetTimerResolution(UINT msResolution)
  132. {
  133. if(!msResolution || msResolution >= 20){
  134. return;
  135. }
  136. if(msResolution < dwCurrentPeriod){
  137. dwCurrentPeriod=msResolution;
  138. }
  139. }
  140. DWORD_PTR SetMyTimer(DWORD dwTimeOut, DWORD TimerRes, MYTIMERCALLBACK TimerCallBack, DWORD_PTR UserContext, PUINT pUnique)
  141. {
  142. BILINK *pBilink;
  143. PMYTIMER pMyTimerWalker,pTimer;
  144. DWORD time;
  145. BOOL bInserted=FALSE;
  146. pTimer=pTimerPool->Get(pTimerPool);
  147. if(!pTimer){
  148. *pUnique=0;
  149. return 0;
  150. }
  151. pTimer->CallBack=TimerCallBack;
  152. pTimer->Context=UserContext;
  153. SetTimerResolution(TimerRes);
  154. Lock(&MyTimerListLock);
  155. ++Unique;
  156. if(Unique==0){
  157. ++Unique;
  158. }
  159. *pUnique=Unique;
  160. pTimer->Unique=Unique;
  161. time=timeGetTime();
  162. pTimer->TimeOut=time+dwTimeOut;
  163. pTimer->TimerState=WaitingForTimeout;
  164. // Insert this guy in the list by timeout time.
  165. pBilink=MyTimerList.prev;
  166. while(pBilink != &MyTimerList){
  167. pMyTimerWalker=CONTAINING_RECORD(pBilink, MYTIMER, Bilink);
  168. pBilink=pBilink->prev;
  169. if((int)(pTimer->TimeOut-pMyTimerWalker->TimeOut) > 0 ){
  170. InsertAfter(&pTimer->Bilink, &pMyTimerWalker->Bilink);
  171. bInserted=TRUE;
  172. break;
  173. }
  174. }
  175. if(!bInserted){
  176. InsertAfter(&pTimer->Bilink, &MyTimerList);
  177. }
  178. Unlock(&MyTimerListLock);
  179. return (DWORD_PTR)pTimer;
  180. }
  181. HRESULT CancelMyTimer(DWORD_PTR dwTimer, DWORD Unique)
  182. {
  183. PMYTIMER pTimer=(PMYTIMER)dwTimer;
  184. HRESULT hr=DPERR_GENERIC;
  185. Lock(&MyTimerListLock);
  186. Lock(&ThreadListLock);
  187. if(pTimer->Unique == Unique){
  188. switch(pTimer->TimerState){
  189. case WaitingForTimeout:
  190. Delete(&pTimer->Bilink);
  191. pTimer->TimerState=End;
  192. pTimer->Unique=0;
  193. pTimerPool->Release(pTimerPool, pTimer);
  194. hr=DP_OK;
  195. break;
  196. case QueuedForThread:
  197. Delete(&pTimer->Bilink);
  198. pTimer->TimerState=End;
  199. pTimer->Unique=0;
  200. pTimerPool->Release(pTimerPool, pTimer);
  201. if(ActiveReq)ActiveReq--;
  202. ExtraSignals++;
  203. hr=DP_OK;
  204. break;
  205. default:
  206. break;
  207. }
  208. }
  209. Unlock(&ThreadListLock);
  210. Unlock(&MyTimerListLock);
  211. return hr;
  212. }
  213. HRESULT InitTimerWorkaround()
  214. {
  215. DWORD dwJunk;
  216. HANDLE hWorker=NULL;
  217. if(twInitCount++){//DPLAY LOCK HELD DURING CALL
  218. return DP_OK;
  219. }
  220. pTimerPool=NULL;
  221. nThreads=0; // number of running threads.
  222. ActiveReq=0; // number of requests being processed.
  223. PeakReqs=0;
  224. bShutDown=FALSE;
  225. KillCount=0;
  226. ExtraSignals=0;
  227. bAlreadyCleanedUp=FALSE;
  228. hWorkToDoSem=0;
  229. hShutDownPeriodicTimer=0;
  230. uAdjustResTimer=0;
  231. uAdjustThreadsTimer=0;
  232. uWorkaroundTimerID=0;
  233. hWorkToDoSem=CreateSemaphoreA(NULL,0,65535,NULL);
  234. hShutDownPeriodicTimer=CreateEventA(NULL,FALSE,FALSE,NULL);
  235. InitializeCriticalSection(&MyTimerListLock);
  236. InitializeCriticalSection(&ThreadListLock);
  237. pTimerPool=FPM_Init(sizeof(MYTIMER),NULL,NULL,NULL);
  238. if(!hWorkToDoSem || !pTimerPool || !hShutDownPeriodicTimer){
  239. FiniTimerWorkaround();
  240. return DPERR_OUTOFMEMORY;
  241. }
  242. uWorkaroundTimerID=timeSetEvent(DEFAULT_TIME_RESOLUTION, DEFAULT_TIME_RESOLUTION, PeriodicTimer, 0, TIME_PERIODIC);
  243. if(!uWorkaroundTimerID){
  244. FiniTimerWorkaround();
  245. return DPERR_OUTOFMEMORY;
  246. }
  247. nThreads=1;
  248. hWorker=CreateThread(NULL,4096, TimerWorkerThread, NULL, 0, &dwJunk);
  249. if(!hWorker){
  250. nThreads=0;
  251. FiniTimerWorkaround();
  252. return DPERR_OUTOFMEMORY;
  253. }
  254. CloseHandle(hWorker);
  255. uAdjustResTimer=SetMyTimer(1000,500,AdjustTimerResolution,0,&AdjustResUnique);
  256. uAdjustThreadsTimer=SetMyTimer(60000,500,AdjustThreads,0,&AdjustThreadsUnique);
  257. return DP_OK;
  258. }
  259. VOID FiniTimerWorkaround()
  260. {
  261. UINT slowcount=0;
  262. BILINK *pBilink;
  263. PMYTIMER pTimer;
  264. if(--twInitCount){ //DPLAY LOCK HELD DURING CALL
  265. return;
  266. }
  267. if(uAdjustResTimer){
  268. CancelMyTimer(uAdjustResTimer, AdjustResUnique);
  269. }
  270. if(uAdjustThreadsTimer){
  271. CancelMyTimer(uAdjustThreadsTimer, AdjustThreadsUnique);
  272. }
  273. //ASSERT_EMPTY_BILINK(&MyTimerList);
  274. //ASSERT_EMPTY_BILINK(&ThreadList);
  275. bShutDown=TRUE;
  276. ReleaseSemaphore(hWorkToDoSem,10000,NULL);
  277. while(nThreads && slowcount < (60000/50)){ // don't wait more than 60 seconds.
  278. slowcount++;
  279. Sleep(50);
  280. }
  281. if(uWorkaroundTimerID){
  282. if(hShutDownPeriodicTimer){
  283. WaitForSingleObject(hShutDownPeriodicTimer,INFINITE);
  284. }
  285. } else {
  286. DeleteCriticalSection(&MyTimerListLock);
  287. DeleteCriticalSection(&ThreadListLock);
  288. }
  289. if(hShutDownPeriodicTimer){
  290. CloseHandle(hShutDownPeriodicTimer);
  291. }
  292. CloseHandle(hWorkToDoSem);
  293. while(!EMPTY_BILINK(&MyTimerList)){
  294. pBilink=MyTimerList.next;
  295. pTimer=CONTAINING_RECORD(pBilink, MYTIMER, Bilink);
  296. pTimer->Unique=0;
  297. pTimer->TimerState=End;
  298. Delete(&pTimer->Bilink);
  299. pTimerPool->Release(pTimerPool, pTimer);
  300. }
  301. while(!EMPTY_BILINK(&MyTimerList)){
  302. pBilink=ThreadList.next;
  303. pTimer=CONTAINING_RECORD(pBilink, MYTIMER, Bilink);
  304. pTimer->Unique=0;
  305. pTimer->TimerState=End;
  306. Delete(&pTimer->Bilink);
  307. pTimerPool->Release(pTimerPool, pTimer);
  308. }
  309. if(pTimerPool){
  310. pTimerPool->Fini(pTimerPool,FALSE);
  311. pTimerPool=NULL;
  312. }
  313. }
  314. DWORD WINAPI TimerWorkerThread(LPVOID foo)
  315. {
  316. BILINK *pBilink;
  317. PMYTIMER pTimer;
  318. HANDLE hNewThread;
  319. DWORD dwJunk;
  320. while (1){
  321. WaitForSingleObject(hWorkToDoSem, INFINITE);
  322. Lock(&ThreadListLock);
  323. if(bShutDown || (KillCount && nThreads > 1)){
  324. nThreads--;
  325. if(KillCount && !bShutDown){
  326. KillCount--;
  327. }
  328. Unlock(&ThreadListLock);
  329. break;
  330. }
  331. if(ExtraSignals){
  332. ExtraSignals--;
  333. Unlock(&ThreadListLock);
  334. continue;
  335. }
  336. if(KillCount){
  337. KillCount--;
  338. Unlock(&ThreadListLock);
  339. continue;
  340. }
  341. if(ActiveReq > nThreads && nThreads < MAX_TIMER_THREADS){
  342. nThreads++;
  343. hNewThread=CreateThread(NULL,4096, TimerWorkerThread, NULL, 0, &dwJunk);
  344. if(hNewThread){
  345. CloseHandle(hNewThread);
  346. } else {
  347. nThreads--;
  348. }
  349. }
  350. pBilink=ThreadList.next;
  351. if(pBilink == &ThreadList) {
  352. Unlock(&ThreadListLock);
  353. continue;
  354. };
  355. Delete(pBilink); // pull off the list.
  356. pTimer=CONTAINING_RECORD(pBilink, MYTIMER, Bilink);
  357. // Call a callback
  358. pTimer->TimerState=InCallBack;
  359. Unlock(&ThreadListLock);
  360. (pTimer->CallBack)((UINT_PTR)pTimer, 0, pTimer->Context, 0, 0);
  361. pTimer->Unique=0;
  362. pTimer->TimerState=End;
  363. pTimerPool->Release(pTimerPool, pTimer);
  364. Lock(&ThreadListLock);
  365. if(ActiveReq)ActiveReq--;
  366. Unlock(&ThreadListLock);
  367. }
  368. return 0;
  369. }