Leaked source code of windows server 2003

802 lines
20 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. sched.cpp
  5. Abstract:
  6. Scheduling work items
  7. Allows scheduling callbacks based on timeout or sognalling event handle
  8. Author:
  9. Vlad Sadovsky (vlads) 31-Jan-1997
  10. Environment:
  11. User Mode - Win32
  12. Revision History:
  13. 31-Jan-1997 VladS created
  14. 30-Apr-1997 VladS Added support for asyncronous events
  15. --*/
  16. //
  17. // Include Headers
  18. //
  19. #include "precomp.h"
  20. #include "stiexe.h"
  21. #include <stilib.h>
  22. //
  23. // Global definitions
  24. //
  25. //
  26. // Use reference counting on context objects
  27. //
  28. #define USE_REF 1
  29. #define EVENT_ARRAY_SIZE 32
  30. #define LockScheduleList() g_SchedulerLock.Lock();
  31. #define UnlockScheduleList() g_SchedulerLock.Unlock();
  32. #define SIGNATURE_SCHED (DWORD)'SCHa'
  33. #define SIGNATURE_SCHED_FREE (DWORD)'SCHf'
  34. class SCHED_ITEM
  35. {
  36. public:
  37. SCHED_ITEM( PFN_SCHED_CALLBACK pfnCallback,
  38. PVOID pContext,
  39. DWORD msecTime,
  40. int nPriority,
  41. DWORD dwSerial,
  42. HANDLE hEvent)
  43. :m_pfnCallback ( pfnCallback ),
  44. m_pContext ( pContext ),
  45. m_nPriority ( nPriority ),
  46. m_dwSerialNumber( dwSerial ),
  47. m_hRegisteredEventHandle(hEvent),
  48. m_Signature ( SIGNATURE_SCHED )
  49. {
  50. if (m_hRegisteredEventHandle) {
  51. m_msecExpires = INFINITE;
  52. }
  53. else {
  54. m_msecExpires = GetTickCount() + msecTime;
  55. }
  56. }
  57. ~SCHED_ITEM( VOID )
  58. {
  59. ASSERT(m_ListEntry.Flink == NULL );
  60. m_Signature = SIGNATURE_SCHED_FREE;
  61. }
  62. BOOL CheckSignature( VOID ) const
  63. { return m_Signature == SIGNATURE_SCHED; }
  64. #ifdef DEBUG
  65. VOID DumpObject(VOID)
  66. {
  67. /* This will cause problems in 64bit (the m_Signature)....
  68. DBG_TRC(("ScheduleWorkItem: Dumping itself:this(%X) Sign(%4c) ListEntry(%X,%X,%X) Ser#(%d) Context(%X)"), \
  69. this,(char *)m_Signature,
  70. &m_ListEntry,m_ListEntry.Flink,m_ListEntry.Blink,
  71. m_dwSerialNumber,m_pContext);
  72. */
  73. }
  74. #endif
  75. LIST_ENTRY m_ListEntry; // Connection field
  76. DWORD m_Signature; // Validity verification
  77. PFN_SCHED_CALLBACK m_pfnCallback; // Work processing callback
  78. PVOID m_pContext; // Context pointer ( usually object ptr)
  79. DWORD m_msecExpires; // Time when timeout expires for this item (in ms)
  80. int m_nPriority; //
  81. DWORD m_dwSerialNumber; // To identify work item when removing
  82. HANDLE m_hRegisteredEventHandle; //
  83. };
  84. DWORD
  85. SchedulerThread(
  86. LPDWORD lpdwParam
  87. );
  88. //
  89. // Global data items
  90. //
  91. CRIT_SECT g_SchedulerLock;
  92. LIST_ENTRY g_ScheduleListHead;
  93. BOOL g_fSchedulerInitialized = FALSE;
  94. BOOL g_fSchedulePaused = FALSE;
  95. BOOL g_fSchedShutdown = FALSE;
  96. HANDLE g_hSchedulerEvent;
  97. HANDLE g_aEventArray[EVENT_ARRAY_SIZE];
  98. UINT g_uiUsedHandles = 0;
  99. //
  100. // Used as identification for work items, incremented on each new item allocated
  101. //
  102. static LONG g_dwSchedSerial = 0;
  103. BOOL
  104. SchedulerInitialize(
  105. VOID
  106. )
  107. /*++
  108. Routine Description:
  109. Initializes the scheduler package
  110. Arguments:
  111. Return Value:
  112. TRUE if successful, FALSE on error (call GetLastError)
  113. --*/
  114. {
  115. DWORD idThread;
  116. HANDLE hSchedulerThread;
  117. if ( g_fSchedulerInitialized )
  118. return TRUE;
  119. ::ZeroMemory(g_aEventArray,sizeof(g_aEventArray));
  120. g_hSchedulerEvent = CreateEvent( NULL,
  121. FALSE,
  122. FALSE,
  123. NULL );
  124. if ( !g_hSchedulerEvent ) {
  125. return FALSE;
  126. }
  127. // Save event handle in global array as first element
  128. g_aEventArray[g_uiUsedHandles++] = g_hSchedulerEvent;
  129. InitializeListHead( &g_ScheduleListHead );
  130. hSchedulerThread = ::CreateThread( NULL,
  131. 0,
  132. (LPTHREAD_START_ROUTINE) SchedulerThread,
  133. NULL,
  134. 0,
  135. &idThread );
  136. if ( !hSchedulerThread ) {
  137. CloseHandle( g_hSchedulerEvent );
  138. return FALSE;
  139. }
  140. CloseHandle( hSchedulerThread );
  141. g_fSchedulerInitialized = TRUE;
  142. STIMONWPRINTF(TEXT("Work item scheduler initialized"));
  143. return TRUE;
  144. }
  145. VOID
  146. SchedulerTerminate(
  147. VOID
  148. )
  149. /*++
  150. Routine Description:
  151. Terminates and cleans up the scheduling package. Any items left on the
  152. list are *not* called during cleanup.
  153. --*/
  154. {
  155. SCHED_ITEM *psi;
  156. if ( !g_fSchedulerInitialized )
  157. return;
  158. g_fSchedShutdown = TRUE;
  159. SetEvent( g_hSchedulerEvent ) ;
  160. // Protected code block
  161. {
  162. TAKE_CRIT_SECT t(g_SchedulerLock);
  163. //
  164. // Delete all of the items that were scheduled, note we do *not*
  165. // call any scheduled items in the list (there shouldn't be any)
  166. //
  167. while ( !IsListEmpty( &g_ScheduleListHead ) ) {
  168. psi = CONTAINING_RECORD( g_ScheduleListHead.Flink,
  169. SCHED_ITEM,
  170. m_ListEntry );
  171. ASSERT( psi->CheckSignature() );
  172. RemoveEntryList( &psi->m_ListEntry );
  173. psi->m_ListEntry.Flink = NULL;
  174. delete psi;
  175. }
  176. }
  177. Sleep( 250 );
  178. CloseHandle( g_hSchedulerEvent );
  179. g_fSchedulerInitialized = FALSE;
  180. }
  181. BOOL
  182. SchedulerSetPauseState(
  183. BOOL fNewState
  184. )
  185. {
  186. BOOL fOldState = g_fSchedulePaused;
  187. g_fSchedulePaused = fNewState;
  188. return fOldState;
  189. }
  190. DWORD
  191. ScheduleWorkItem(
  192. PFN_SCHED_CALLBACK pfnCallback,
  193. PVOID pContext,
  194. DWORD msecTime,
  195. HANDLE hEvent,
  196. int nPriority
  197. )
  198. /*++
  199. Routine Description:
  200. Adds a timed work item to the work list
  201. Arguments:
  202. pfnCallback - Function to call
  203. pContext - Context to pass to the callback
  204. hEvent - handle of event to wait on before signalling callback
  205. msecTime - number of milliseconds to wait before calling timeout
  206. nPriority - Thread priority to set for work item
  207. Return Value:
  208. zero on failure, non-zero on success. The return value can be used to
  209. remove the scheduled work item.
  210. --*/
  211. {
  212. SCHED_ITEM *psi;
  213. SCHED_ITEM *psiList;
  214. LIST_ENTRY *pEntry;
  215. DWORD dwRet = 0;
  216. BOOL fValidRequest = FALSE;
  217. ASSERT( g_fSchedulerInitialized );
  218. if ( !g_fSchedulerInitialized )
  219. return 0;
  220. //
  221. // Scheduler currently only supports normal thread priority
  222. //
  223. ASSERT( nPriority == THREAD_PRIORITY_NORMAL );
  224. InterlockedIncrement(&g_dwSchedSerial);
  225. psi = new SCHED_ITEM( pfnCallback,
  226. pContext,
  227. msecTime,
  228. nPriority,
  229. g_dwSchedSerial,
  230. hEvent );
  231. if ( !psi ) {
  232. return 0;
  233. }
  234. // BEGIN PROTECTED CODE
  235. {
  236. TAKE_CRIT_SECT t(g_SchedulerLock);
  237. // Commented out to reduce debug output when lock management auto-unlocking is enabled
  238. //DBG_TRC(("Scheduler adding work item (%X) "),psi);
  239. //
  240. // Validate scheduling request. If it timer based - always valid
  241. // if it is passing event handle, we are limited by the size of wait array, so first check to see
  242. // if array is not full
  243. //
  244. fValidRequest = TRUE;
  245. if (hEvent && (hEvent!=INVALID_HANDLE_VALUE)) {
  246. if (g_uiUsedHandles < EVENT_ARRAY_SIZE) {
  247. g_aEventArray[g_uiUsedHandles++] = hEvent;
  248. }
  249. else {
  250. fValidRequest = FALSE;
  251. dwRet = 0;
  252. }
  253. }
  254. //
  255. // Insert the list in order based on expires time
  256. //
  257. if(fValidRequest) {
  258. for ( pEntry = g_ScheduleListHead.Flink;
  259. pEntry != &g_ScheduleListHead;
  260. pEntry = pEntry->Flink ) {
  261. psiList = CONTAINING_RECORD( pEntry, SCHED_ITEM,m_ListEntry );
  262. if ( psiList->m_msecExpires > psi->m_msecExpires ) {
  263. break;
  264. }
  265. }
  266. //
  267. // This should work in whether the list is empty or this is the last item
  268. // on the list
  269. //
  270. psi->m_ListEntry.Flink = pEntry;
  271. psi->m_ListEntry.Blink = pEntry->Blink;
  272. pEntry->Blink->Flink = &psi->m_ListEntry;
  273. pEntry->Blink = &psi->m_ListEntry;
  274. dwRet = psi->m_dwSerialNumber;
  275. #if 0
  276. DBG_TRC(("Scheduler added work item (%X) with cookie(%d) before (%X). Head=(%X) "),psi,psi->m_dwSerialNumber,pEntry,&g_ScheduleListHead);
  277. psi->DumpObject();
  278. #endif
  279. }
  280. }
  281. // END PROTECTED CODE
  282. //
  283. // Kick off scheduler thread
  284. //
  285. if(fValidRequest) {
  286. SetEvent( g_hSchedulerEvent );
  287. }
  288. else {
  289. delete psi;
  290. }
  291. return dwRet;
  292. }
  293. BOOL
  294. RemoveWorkItem(
  295. DWORD dwCookie
  296. )
  297. /*++
  298. Routine Description:
  299. Removes a scheduled work item
  300. Arguments:
  301. dwCookie - The return value from a previous call to ScheduleWorkItem
  302. Return Value:
  303. TRUE if the item was found, FALSE if the item was not found.
  304. --*/
  305. {
  306. SCHED_ITEM * psi;
  307. LIST_ENTRY * pEntry;
  308. // BEGIN PROTECTED CODE
  309. {
  310. TAKE_CRIT_SECT t(g_SchedulerLock);
  311. DBG_TRC(("Schedule::RemoveWorkItem (%X) ", dwCookie));
  312. //
  313. // Walk the list to find an item with matching id
  314. //
  315. for ( pEntry = g_ScheduleListHead.Flink;
  316. pEntry != &g_ScheduleListHead;
  317. pEntry = pEntry->Flink ) {
  318. psi = CONTAINING_RECORD( pEntry, SCHED_ITEM,m_ListEntry );
  319. ASSERT( psi->CheckSignature() );
  320. if ( dwCookie == psi->m_dwSerialNumber ) {
  321. //
  322. // Found our item
  323. //
  324. #if 0
  325. DBG_TRC(("Scheduler removing work item (%X) "),psi);
  326. psi->DumpObject();
  327. #endif
  328. RemoveEntryList( pEntry );
  329. pEntry->Flink = NULL;
  330. //
  331. // If this work item is associated with asyncronous event , remove event handle
  332. // from wait array
  333. //
  334. if (psi->m_hRegisteredEventHandle && psi->m_hRegisteredEventHandle != INVALID_HANDLE_VALUE) {
  335. UINT uiIndex;
  336. // Find handle in wait array
  337. for (uiIndex = 0;
  338. uiIndex < g_uiUsedHandles;
  339. uiIndex++ ) {
  340. if ( g_aEventArray[uiIndex] == psi->m_hRegisteredEventHandle ) {
  341. memcpy(&g_aEventArray[uiIndex],
  342. &g_aEventArray[uiIndex+1],
  343. sizeof(g_aEventArray[0])*(g_uiUsedHandles - uiIndex - 1)
  344. );
  345. g_aEventArray[g_uiUsedHandles--] = NULL;
  346. }
  347. }
  348. }
  349. // Destroy work item now
  350. delete psi;
  351. return TRUE;
  352. }
  353. }
  354. }
  355. // END PROTECTED CODE
  356. //
  357. // Item with given number not found
  358. //
  359. return FALSE;
  360. }
  361. DWORD
  362. SchedulerThread(
  363. LPDWORD lpdwParam
  364. )
  365. /*++
  366. Routine Description:
  367. Initializes the scheduler/timer package
  368. Arguments:
  369. Return Value:
  370. TRUE if successful, FALSE on error (call GetLastError)
  371. --*/
  372. {
  373. DWORD cmsecWait = INFINITE;
  374. DWORD TickCount;
  375. SCHED_ITEM *psi = NULL;
  376. LIST_ENTRY *pEntry;
  377. DWORD dwErr;
  378. UINT uiSignalledIndex;
  379. BOOL fFoundSignalledItem = FALSE;
  380. while ( TRUE ) {
  381. dwErr = ::WaitForMultipleObjects(g_uiUsedHandles,
  382. g_aEventArray,
  383. FALSE,
  384. cmsecWait );
  385. uiSignalledIndex = dwErr - WAIT_OBJECT_0;
  386. //
  387. // If we're shutting down, get out
  388. //
  389. if ( g_fSchedShutdown ) {
  390. goto Exit;
  391. }
  392. #if 0
  393. DebugDumpScheduleList(TEXT("SchedulerThread"));
  394. #endif
  395. switch (dwErr)
  396. {
  397. default:
  398. if ((uiSignalledIndex > 0) && (uiSignalledIndex < g_uiUsedHandles )) {
  399. //
  400. // One of the devices signalled event. Find work item for this device
  401. // and invoke callback
  402. //
  403. LockScheduleList()
  404. fFoundSignalledItem = FALSE;
  405. for ( pEntry = g_ScheduleListHead.Flink;
  406. pEntry != &g_ScheduleListHead;
  407. pEntry = pEntry->Flink
  408. ) {
  409. psi = CONTAINING_RECORD( pEntry, SCHED_ITEM,m_ListEntry );
  410. ASSERT( psi->CheckSignature() );
  411. if ( g_aEventArray[uiSignalledIndex] == psi->m_hRegisteredEventHandle ) {
  412. fFoundSignalledItem = TRUE;
  413. RemoveEntryList( &psi->m_ListEntry );
  414. psi->m_ListEntry.Flink = NULL;
  415. #ifdef USE_REF
  416. // Reference context object
  417. if(psi->m_pContext) {
  418. ((IUnknown *)psi->m_pContext)->AddRef();
  419. }
  420. #endif
  421. //
  422. // Delete event handle from the array
  423. //
  424. if (uiSignalledIndex < g_uiUsedHandles-1 ) {
  425. memcpy(&g_aEventArray[uiSignalledIndex],
  426. &g_aEventArray[uiSignalledIndex+1],
  427. sizeof(g_aEventArray[0])*(g_uiUsedHandles - uiSignalledIndex - 1)
  428. );
  429. }
  430. g_aEventArray[g_uiUsedHandles--] = NULL;
  431. break;
  432. }
  433. }
  434. UnlockScheduleList()
  435. //
  436. // If signalled item had been found and verified - invoke callback and remove it
  437. //
  438. if (fFoundSignalledItem) {
  439. if(psi->m_pContext) {
  440. psi->m_pfnCallback( psi->m_pContext );
  441. #ifdef USE_REF
  442. ((IUnknown *)psi->m_pContext)->Release();
  443. #endif
  444. }
  445. delete psi;
  446. }
  447. continue;
  448. }
  449. //
  450. // Fall through to signalled scheduler event
  451. //
  452. case WAIT_OBJECT_0:
  453. //
  454. // Means a new item has been scheduled, reset the timeout or
  455. // we are shutting down
  456. //
  457. if ( g_fSchedShutdown ) {
  458. goto Exit;
  459. }
  460. LockScheduleList();
  461. //
  462. // Get the timeout value for the first item in the list
  463. //
  464. if ( !IsListEmpty( &g_ScheduleListHead ) ) {
  465. psi = CONTAINING_RECORD( g_ScheduleListHead.Flink,
  466. SCHED_ITEM,
  467. m_ListEntry );
  468. ASSERT( psi->CheckSignature() );
  469. //
  470. // Make sure the front item hasn't already expired
  471. //
  472. TickCount = GetTickCount();
  473. if ( TickCount > psi->m_msecExpires ) {
  474. // We have at least one work item needing attention
  475. goto RunItems;
  476. }
  477. cmsecWait = psi->m_msecExpires - TickCount;
  478. }
  479. else
  480. {
  481. cmsecWait = INFINITE;
  482. }
  483. UnlockScheduleList();
  484. break;
  485. case WAIT_TIMEOUT:
  486. StartAgain:
  487. //
  488. // If we're shutting down, get out
  489. //
  490. if ( g_fSchedShutdown ) {
  491. goto Exit;
  492. }
  493. if (g_fSchedulePaused ) {
  494. continue;
  495. }
  496. TickCount = GetTickCount();
  497. //
  498. // Walk the sorted list for expired work items
  499. //
  500. LockScheduleList();
  501. RunItems:
  502. //
  503. // If no items, schedule no timeout
  504. //
  505. if ( IsListEmpty( &g_ScheduleListHead ))
  506. {
  507. cmsecWait = INFINITE;
  508. }
  509. for ( pEntry = g_ScheduleListHead.Flink;
  510. pEntry != &g_ScheduleListHead;
  511. )
  512. {
  513. psi = CONTAINING_RECORD( pEntry, SCHED_ITEM,m_ListEntry );
  514. ASSERT( psi->CheckSignature() );
  515. //
  516. // Go through expired items, skipping the ones with event handle set
  517. //
  518. if ( (TickCount > psi->m_msecExpires) &&
  519. !psi->m_hRegisteredEventHandle ) {
  520. pEntry = pEntry->Flink;
  521. // Take item off the list
  522. RemoveEntryList( &psi->m_ListEntry );
  523. psi->m_ListEntry.Flink = NULL;
  524. #ifdef USE_REF
  525. // Reference context object
  526. if(psi->m_pContext) {
  527. ((IUnknown *)psi->m_pContext)->AddRef();
  528. }
  529. #endif
  530. //
  531. // Unlock the list so clients can add additional
  532. // schedule items
  533. //
  534. UnlockScheduleList();
  535. if (psi->m_pContext) {
  536. psi->m_pfnCallback( psi->m_pContext );
  537. #ifdef USE_REF
  538. ((IUnknown *)psi->m_pContext)->Release();
  539. #endif
  540. }
  541. delete psi;
  542. //
  543. // Start looking in the list from the beginning in case
  544. // new items have been added or removed
  545. //
  546. goto StartAgain;
  547. }
  548. else {
  549. //
  550. // Since they are in sorted order once we hit one that's
  551. // not expired we don't need to look further
  552. //
  553. cmsecWait = psi->m_msecExpires - TickCount;
  554. break;
  555. }
  556. }
  557. UnlockScheduleList();
  558. break;
  559. }
  560. } // while ( TRUE )
  561. Exit:
  562. return 0;
  563. }
  564. #ifdef DEBUG
  565. VOID
  566. DebugDumpScheduleList(
  567. LPCTSTR pszId
  568. )
  569. {
  570. if ( !g_fSchedulerInitialized ) {
  571. STIMONWPRINTF(TEXT("Schedule list not initialized"));
  572. return;
  573. }
  574. LIST_ENTRY * pentry;
  575. LIST_ENTRY * pentryNext;
  576. SCHED_ITEM * psi = NULL;
  577. TAKE_CRIT_SECT t(g_SchedulerLock);
  578. DBG_TRC(("Validating schedule list . Called from (%S)" ,pszId ? pszId : TEXT("Unknown")));
  579. for ( pentry = g_ScheduleListHead.Flink;
  580. pentry != &g_ScheduleListHead;
  581. pentry = pentryNext ) {
  582. pentryNext = pentry->Flink;
  583. psi = CONTAINING_RECORD( pentry, SCHED_ITEM,m_ListEntry );
  584. ASSERT( psi->CheckSignature() );
  585. psi->DumpObject();
  586. }
  587. }
  588. #endif