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.

1049 lines
30 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2000 Microsoft Corporation
  3. Module Name :
  4. tasksched.cpp
  5. Abstract :
  6. Source file for task manager classes and routines.
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #if !defined(BITS_V12_ON_NT4)
  12. #include "tasksched.tmh"
  13. #endif
  14. ////////////////////////////////////////////////////////////////////////////////////
  15. //
  16. // TaskSchedulerWorkItem
  17. //
  18. ////////////////////////////////////////////////////////////////////////////////////
  19. ////////////////////////////////////////////////////////////////////////////////////
  20. // Constructor/Destructor
  21. ////////////////////////////////////////////////////////////////////////////////////
  22. TaskSchedulerWorkItem::TaskSchedulerWorkItem( FILETIME *pTimeToRun ) :
  23. m_Container( NULL ),
  24. m_CancelEvent(NULL),
  25. m_ItemComplete(NULL),
  26. m_ItemCanceled(NULL),
  27. m_State(TASK_STATE_NOTHING),
  28. m_WorkGroup(NULL)
  29. {
  30. try
  31. {
  32. // All events are manual reset.
  33. m_ItemCanceled = CreateEvent( NULL, TRUE, FALSE, NULL );
  34. if ( !m_ItemCanceled )
  35. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  36. // new items are complete
  37. m_CancelEvent = CreateEvent( NULL, TRUE, TRUE, NULL );
  38. if ( !m_CancelEvent )
  39. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  40. m_ItemComplete = CreateEvent( NULL, TRUE, FALSE, NULL );
  41. if ( !m_ItemComplete )
  42. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  43. }
  44. catch ( ComError Error )
  45. {
  46. this->~TaskSchedulerWorkItem();
  47. throw;
  48. }
  49. }
  50. TaskSchedulerWorkItem::~TaskSchedulerWorkItem()
  51. {
  52. if ( m_ItemComplete ) SetEvent( m_ItemComplete );
  53. if ( m_CancelEvent ) CloseHandle( m_CancelEvent );
  54. if ( m_ItemCanceled ) CloseHandle( m_ItemCanceled );
  55. if ( m_ItemComplete ) CloseHandle( m_ItemComplete );
  56. }
  57. void
  58. TaskSchedulerWorkItem::Serialize(
  59. HANDLE hFile
  60. )
  61. {
  62. //
  63. // If this function changes, be sure that the metadata extension
  64. // constants are adequate.
  65. //
  66. bool fActive = g_Manager->m_TaskScheduler.IsWorkItemInScheduler( this );
  67. SafeWriteFile( hFile, fActive );
  68. if (fActive)
  69. {
  70. SafeWriteFile( hFile, m_InsertionTime );
  71. SafeWriteFile( hFile, m_TimeToRun );
  72. }
  73. }
  74. void
  75. TaskSchedulerWorkItem::Unserialize(
  76. HANDLE hFile
  77. )
  78. {
  79. bool fActive;
  80. SafeReadFile( hFile, &fActive );
  81. if (fActive)
  82. {
  83. SafeReadFile( hFile, &m_InsertionTime );
  84. SafeReadFile( hFile, &m_TimeToRun );
  85. LogTask("workitem %p : adding to scheduler for %I64d", this, FILETIMEToUINT64(m_TimeToRun) );
  86. g_Manager->m_TaskScheduler.InsertWorkItem( this, &m_TimeToRun );
  87. }
  88. else
  89. {
  90. LogTask("workitem %p: not in scheduler", this );
  91. }
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////////
  94. //
  95. // TaskScheduler
  96. //
  97. ////////////////////////////////////////////////////////////////////////////////////
  98. ////////////////////////////////////////////////////////////////////////////////////
  99. // Constructor/Destructor
  100. ////////////////////////////////////////////////////////////////////////////////////
  101. TaskScheduler::TaskScheduler() :
  102. m_bShouldDie(false),
  103. m_WaitableTimer(NULL),
  104. m_ReaderLock(NULL),
  105. m_WriterSemaphore(NULL),
  106. m_ReaderCount(0),
  107. m_WorkItemTLS((DWORD)-1),
  108. m_WriterOwner(0),
  109. m_WorkerInitialized(NULL)
  110. {
  111. try
  112. {
  113. m_WorkItemTLS = TlsAlloc();
  114. if ( (DWORD)-1 == m_WorkItemTLS)
  115. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  116. m_SchedulerLock = CreateMutex( NULL, FALSE, NULL );
  117. if ( !m_SchedulerLock )
  118. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  119. m_WaitableTimer = CreateWaitableTimer( NULL, FALSE, NULL );
  120. if ( !m_WaitableTimer )
  121. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  122. // Create and autoreset event for synchronization on startup
  123. m_WorkerInitialized = CreateEvent( NULL, FALSE, FALSE, NULL );
  124. if ( !m_WorkerInitialized )
  125. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  126. m_ReaderLock = CreateMutex( NULL, FALSE, NULL );
  127. if ( !m_ReaderLock )
  128. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  129. m_WriterSemaphore = CreateSemaphore( NULL, 1, 1, NULL );
  130. if ( !m_WriterSemaphore )
  131. throw ComError( HRESULT_FROM_WIN32(GetLastError()));
  132. }
  133. catch ( ComError Error )
  134. {
  135. this->~TaskScheduler();
  136. throw;
  137. }
  138. }
  139. TaskScheduler::~TaskScheduler()
  140. {
  141. if ((DWORD)-1 != m_WorkItemTLS)
  142. TlsFree( m_WorkItemTLS );
  143. if ( m_SchedulerLock )
  144. CloseHandle( m_SchedulerLock );
  145. if ( m_WaitableTimer )
  146. CloseHandle( m_WaitableTimer );
  147. if ( m_WorkerInitialized )
  148. CloseHandle( m_WorkerInitialized );
  149. if ( m_ReaderLock )
  150. CloseHandle( m_ReaderLock );
  151. if ( m_WriterSemaphore )
  152. CloseHandle( m_WriterSemaphore );
  153. }
  154. //////////////////////////////////////////////////////////////////////////////////////////
  155. // WorkItem control
  156. //////////////////////////////////////////////////////////////////////////////////////////
  157. bool TaskScheduler::CancelWorkItem( TaskSchedulerWorkItem * pWorkItem )
  158. {
  159. LogTask( "cancelling %p", pWorkItem );
  160. RTL_VERIFY( WAIT_OBJECT_0 == WaitForSingleObject( m_SchedulerLock, INFINITE ) );
  161. HANDLE hHandles[2];
  162. hHandles[0] = pWorkItem->m_ItemCanceled;
  163. hHandles[1] = pWorkItem->m_ItemComplete;
  164. DWORD dwResult = WaitForMultipleObjects( 2, hHandles, FALSE, 0 );
  165. if ( (WAIT_OBJECT_0 == dwResult) ||
  166. ((WAIT_OBJECT_0 + 1) == dwResult ) )
  167. {
  168. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  169. return true; // Job completed before the cancel
  170. }
  171. // If canceling the current work item, call Acknowlege immedialtly
  172. if ( GetCurrentWorkItem() == pWorkItem )
  173. {
  174. LogTask( "Canceling work item %p, we are the owner", pWorkItem );
  175. RTL_VERIFY( SetEvent( pWorkItem->m_CancelEvent ) );
  176. AcknowledgeWorkItemCancel();
  177. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  178. return false; // Job canceled
  179. }
  180. //
  181. // Remove the work item from its list.
  182. //
  183. switch( pWorkItem->m_State )
  184. {
  185. case TASK_STATE_WAITING:
  186. {
  187. m_WaitingList.erase( *pWorkItem );
  188. pWorkItem->m_State = TASK_STATE_CANCELED;
  189. pWorkItem->m_WorkGroup = NULL;
  190. Reschedule();
  191. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  192. return false;
  193. }
  194. case TASK_STATE_READY:
  195. {
  196. TaskSchedulerWorkGroup *pGroup =
  197. static_cast<TaskSchedulerWorkGroup*>(pWorkItem->m_WorkGroup);
  198. pGroup->m_ReadyList.erase( *pWorkItem );
  199. // Kill one on the semaphore
  200. RTL_VERIFY( WAIT_OBJECT_0 == WaitForSingleObject( pGroup->m_ItemAvailableSemaphore, 0 ) );
  201. pWorkItem->m_State = TASK_STATE_CANCELED;
  202. pWorkItem->m_WorkGroup = NULL;
  203. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  204. return false;
  205. }
  206. case TASK_STATE_RUNNING:
  207. {
  208. // cancelling on another thread
  209. RTL_VERIFY( SetEvent( pWorkItem->m_CancelEvent ) );
  210. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  211. dwResult = WaitForMultipleObjects( 2, hHandles, FALSE, INFINITE );
  212. ASSERT( ( WAIT_OBJECT_0 == dwResult ) || ( WAIT_OBJECT_0 + 1 == dwResult ) );
  213. return WAIT_OBJECT_0 != dwResult;
  214. }
  215. case TASK_STATE_CANCELED:
  216. case TASK_STATE_COMPLETE:
  217. case TASK_STATE_NOTHING:
  218. default:
  219. ASSERT( TASK_STATE_CANCELED == pWorkItem->m_State ||
  220. TASK_STATE_COMPLETE == pWorkItem->m_State ||
  221. TASK_STATE_NOTHING == pWorkItem->m_State );
  222. ASSERT( NULL == pWorkItem->m_WorkGroup );
  223. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  224. return true;
  225. }
  226. }
  227. void TaskScheduler::CompleteWorkItem( bool bCancel )
  228. {
  229. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  230. TaskSchedulerWorkItem *pWorkItem = GetCurrentWorkItem();
  231. LogTask( "completing %p", pWorkItem );
  232. // ASSERT( pWorkItem );
  233. if (pWorkItem)
  234. {
  235. RTL_VERIFY( TlsSetValue( m_WorkItemTLS, NULL ) );
  236. TaskSchedulerWorkGroup *pGroup =
  237. static_cast<TaskSchedulerWorkGroup*>(pWorkItem->m_WorkGroup);
  238. pGroup->m_RunningList.erase( *pWorkItem );
  239. pWorkItem->m_WorkGroup = NULL;
  240. pWorkItem->m_State = bCancel ? TASK_STATE_CANCELED : TASK_STATE_COMPLETE;
  241. RTL_VERIFY( SetEvent( bCancel ? pWorkItem->m_ItemCanceled : pWorkItem->m_ItemComplete ) );
  242. }
  243. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  244. }
  245. void TaskScheduler::DispatchWorkItem()
  246. {
  247. TaskSchedulerWorkItem *pWorkItem = NULL;
  248. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  249. // Move all the jobs that are available from waiting
  250. // to ready
  251. while ( !m_WaitingList.empty() )
  252. {
  253. FILETIME ftCurrentTime;
  254. GetSystemTimeAsFileTime( &ftCurrentTime );
  255. TaskSchedulerWorkItem * pHeadItem = &(*m_WaitingList.begin());
  256. UINT64 CurrentTime = FILETIMEToUINT64( ftCurrentTime );
  257. UINT64 HeadTime = FILETIMEToUINT64( pHeadItem->m_TimeToRun );
  258. if ( HeadTime > CurrentTime )
  259. {
  260. // All the jobs in the list are still waiting,
  261. // let them continue waiting
  262. break;
  263. }
  264. // transfer the head work item from the waiting list
  265. // to the ready list of the correct work group
  266. m_WaitingList.erase( *pHeadItem );
  267. AddItemToWorkGroup( pHeadItem->GetSid(), pHeadItem );
  268. }
  269. Reschedule();
  270. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  271. }
  272. void
  273. TaskScheduler::InsertDelayedWorkItem(
  274. TaskSchedulerWorkItem *pWorkItem,
  275. UINT64 Delay100Nsec
  276. )
  277. {
  278. FILETIME ftCurrentTime;
  279. GetSystemTimeAsFileTime( &ftCurrentTime );
  280. UINT64 TimeToRun = Delay100Nsec + FILETIMEToUINT64( ftCurrentTime );
  281. FILETIME ftTimeToRun = UINT64ToFILETIME( TimeToRun );
  282. InsertWorkItem( pWorkItem, &ftTimeToRun );
  283. }
  284. void
  285. TaskScheduler::RescheduleDelayedTask(
  286. TaskSchedulerWorkItem *pWorkItem,
  287. UINT64 Delay100Nsec
  288. )
  289. {
  290. // Resets the time for the work item to run to be Delay100NSec after
  291. // the insertion time.
  292. // If the work item is not in the queue, running, completed,
  293. // or canceled then this operation is ignored.
  294. // Otherwise, the job is rescheduled.
  295. LogTask( "rescheduling %p", pWorkItem );
  296. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  297. // If the work item is not on a running list or the pending list,
  298. // ignore the call.
  299. if ( TASK_STATE_READY == pWorkItem->m_State )
  300. {
  301. TaskSchedulerWorkGroup *pGroup =
  302. static_cast<TaskSchedulerWorkGroup*>( pWorkItem->m_WorkGroup );
  303. pGroup->m_ReadyList.erase( *pWorkItem );
  304. RTL_VERIFY( WAIT_OBJECT_0 == WaitForSingleObject( pGroup->m_ItemAvailableSemaphore, 0 ) );
  305. }
  306. else if ( TASK_STATE_WAITING == pWorkItem->m_State )
  307. {
  308. m_WaitingList.erase( *pWorkItem );
  309. }
  310. else
  311. {
  312. LogTask( "item %p not pending. Ignoring.", pWorkItem );
  313. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  314. return;
  315. }
  316. UINT64 TimeToRun = Delay100Nsec + FILETIMEToUINT64( pWorkItem->m_InsertionTime );
  317. pWorkItem->m_TimeToRun = UINT64ToFILETIME( TimeToRun );
  318. m_WaitingList.insert( *pWorkItem );
  319. pWorkItem->m_State = TASK_STATE_WAITING;
  320. pWorkItem->m_WorkGroup = NULL;
  321. Reschedule();
  322. LogTask( "item %p rescheduled", pWorkItem );
  323. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  324. }
  325. inline INT64 abs(INT64 x)
  326. {
  327. if (x >= 0)
  328. {
  329. return x;
  330. }
  331. else
  332. {
  333. return -x;
  334. }
  335. }
  336. void TaskScheduler::InsertWorkItem( TaskSchedulerWorkItem *pWorkItem, FILETIME *pTimeToRun )
  337. {
  338. {
  339. INT64 Difference;
  340. FILETIME ftCurrentTime;
  341. GetSystemTimeAsFileTime( &ftCurrentTime );
  342. if (pTimeToRun)
  343. {
  344. Difference = INT64(FILETIMEToUINT64( *pTimeToRun )) - INT64(FILETIMEToUINT64( ftCurrentTime ));
  345. if (abs(Difference) > 86400 * NanoSec100PerSec)
  346. {
  347. LogTask( "inserting %p; activates in %f days", pWorkItem, float(Difference) / (float(NanoSec100PerSec) * 86400) );
  348. }
  349. else
  350. {
  351. LogTask( "inserting %p; activates in %f seconds", pWorkItem, float(Difference) / float(NanoSec100PerSec) );
  352. }
  353. }
  354. else
  355. {
  356. LogTask( "inserting %p; activates now", pWorkItem );
  357. }
  358. }
  359. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  360. GetSystemTimeAsFileTime( &pWorkItem->m_InsertionTime );
  361. RTL_VERIFY( ResetEvent( pWorkItem->m_CancelEvent ) );
  362. RTL_VERIFY( ResetEvent( pWorkItem->m_ItemComplete ) );
  363. RTL_VERIFY( ResetEvent( pWorkItem->m_ItemCanceled ) );
  364. if ( !pTimeToRun && !m_bShouldDie )
  365. {
  366. pWorkItem->m_TimeToRun = pWorkItem->m_InsertionTime;
  367. AddItemToWorkGroup( pWorkItem->GetSid(), pWorkItem );
  368. }
  369. else
  370. {
  371. if (pTimeToRun)
  372. {
  373. pWorkItem->m_TimeToRun = *pTimeToRun;
  374. }
  375. else
  376. {
  377. GetSystemTimeAsFileTime( &pWorkItem->m_TimeToRun );
  378. }
  379. pWorkItem->m_State = TASK_STATE_WAITING;
  380. m_WaitingList.insert( *pWorkItem );
  381. Reschedule();
  382. }
  383. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  384. }
  385. bool TaskScheduler::IsWorkItemInScheduler( TaskSchedulerWorkItem *pWorkItem )
  386. {
  387. bool b;
  388. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  389. b = ( TASK_STATE_WAITING == pWorkItem->m_State ||
  390. TASK_STATE_READY == pWorkItem->m_State ||
  391. TASK_STATE_RUNNING == pWorkItem->m_State );
  392. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  393. return b;
  394. }
  395. void TaskScheduler::Reschedule()
  396. {
  397. if ( m_WaitingList.empty() )
  398. {
  399. // Nothing to do, cancel waitable timer.
  400. RTL_VERIFY( CancelWaitableTimer( m_WaitableTimer ) );
  401. return;
  402. }
  403. LARGE_INTEGER NextItemTime;
  404. FILETIME ftNextItemTime = (*m_WaitingList.begin()).m_TimeToRun;
  405. NextItemTime.QuadPart = (INT64)FILETIMEToUINT64( ftNextItemTime );
  406. RTL_VERIFY(
  407. SetWaitableTimer(
  408. m_WaitableTimer,
  409. &NextItemTime,
  410. 0,
  411. NULL,
  412. NULL,
  413. FALSE ) );
  414. }
  415. /////////////////////////////////////////////////////////////////////////////////////////////////
  416. // Reader/Writer lock
  417. //
  418. // Algorithm:
  419. //
  420. // Writer:
  421. // Wait on writer lock and cancel event. Return when either is signaled
  422. //
  423. // Unlock writer:
  424. // Release the writer lock
  425. //
  426. // Lock reader:
  427. // Lock reader lock to protect count. If I am the first reader, grab the writer semaphore.
  428. // Unlock reader lock. If on either wait the cancel event is signaled, abort.
  429. //
  430. // Unlock reader:
  431. // Decrement the reader count. If last reader, release the writer lock.
  432. //
  433. /////////////////////////////////////////////////////////////////////////////////////////////////
  434. bool TaskScheduler::LockReader()
  435. {
  436. LogLock( "reader" );
  437. HANDLE hCancel = GetCancelEvent();
  438. if ( !hCancel )
  439. {
  440. RTL_VERIFY( WaitForSingleObject( m_ReaderLock, INFINITE ) == WAIT_OBJECT_0 );
  441. // InterlockedIncrement returns the new value
  442. if ( InterlockedIncrement( &m_ReaderCount ) == 1 )
  443. {
  444. RTL_VERIFY( WaitForSingleObject( m_WriterSemaphore, INFINITE ) == WAIT_OBJECT_0 );
  445. }
  446. RTL_VERIFY( ReleaseMutex( m_ReaderLock ) );
  447. LogLock("reader lock acquired");
  448. ASSERT( !m_WriterOwner );
  449. return false;
  450. }
  451. DWORD dwResult;
  452. HANDLE hReaderLockHandles[2];
  453. hReaderLockHandles[0] = hCancel;
  454. hReaderLockHandles[1] = m_ReaderLock;
  455. dwResult = WaitForMultipleObjects( 2, hReaderLockHandles, false, INFINITE );
  456. switch ( dwResult )
  457. {
  458. case WAIT_OBJECT_0 + 0:
  459. // cancel request
  460. LogLock( "Cancel requested, aborting read lock" );
  461. return true;
  462. case WAIT_OBJECT_0 + 1:
  463. // lock acquired
  464. break;
  465. default:
  466. ASSERT(0);
  467. }
  468. bool bReturnVal = false;
  469. ULONG NewReaderCount = InterlockedIncrement( &m_ReaderCount );
  470. if (1 == NewReaderCount )
  471. {
  472. LogLock("First reader, need to block writers");
  473. HANDLE hWriterLockHandles[2];
  474. hWriterLockHandles[0] = hCancel;
  475. hWriterLockHandles[1] = m_WriterSemaphore;
  476. dwResult = WaitForMultipleObjects( 2, hWriterLockHandles, false, INFINITE );
  477. switch ( dwResult )
  478. {
  479. case WAIT_OBJECT_0 + 0:
  480. // cancel request
  481. LogLock( "Cancel requested, aborting acquire of writer lock");
  482. bReturnVal = true;
  483. case WAIT_OBJECT_0 + 1:
  484. // lock acquired
  485. break;
  486. default:
  487. ASSERT(0);
  488. }
  489. }
  490. RTL_VERIFY( ReleaseMutex( m_ReaderLock ) );
  491. if (!bReturnVal)
  492. {
  493. LogLock("reader lock acquired");
  494. ASSERT( !m_WriterOwner );
  495. }
  496. return bReturnVal;
  497. }
  498. void TaskScheduler::UnlockReader()
  499. {
  500. LogLock( "reader unlock" );
  501. LONG lNewReaderCount = InterlockedDecrement( &m_ReaderCount );
  502. ASSERT( lNewReaderCount >= 0 );
  503. if (!lNewReaderCount ) //Last reader
  504. {
  505. LogLock( "Last reader, letting writers pass" );
  506. RTL_VERIFY( ReleaseSemaphore( m_WriterSemaphore, 1, NULL ) );
  507. }
  508. LogLock( "Unlocked read access to lock" );
  509. }
  510. bool TaskScheduler::LockWriter()
  511. {
  512. LogLock( "writer lock" );
  513. HANDLE hCancel = GetCancelEvent();
  514. if (!hCancel)
  515. {
  516. RTL_VERIFY( WaitForSingleObject( m_WriterSemaphore, INFINITE ) == WAIT_OBJECT_0 );
  517. ASSERT( !m_WriterOwner );
  518. m_WriterOwner = GetCurrentThreadId();
  519. LogLock("Lock acquired with write access");
  520. return false;
  521. }
  522. HANDLE hHandles[2];
  523. hHandles[0] = hCancel;
  524. hHandles[1] = m_WriterSemaphore;
  525. DWORD dwResult = WaitForMultipleObjects( 2, hHandles, false, INFINITE );
  526. switch ( dwResult )
  527. {
  528. case WAIT_OBJECT_0 + 0:
  529. // cancel request
  530. LogLock("Cancel requested, aborting lock with write access");
  531. return true;
  532. case WAIT_OBJECT_0 + 1:
  533. // lock acquired
  534. ASSERT( !m_WriterOwner );
  535. m_WriterOwner = GetCurrentThreadId();
  536. LogLock("Lock acquired with write access");
  537. return false;
  538. default:
  539. ASSERT(0);
  540. return false;
  541. }
  542. }
  543. void TaskScheduler::UnlockWriter()
  544. {
  545. LogLock( "writer unlock" );
  546. ASSERT( GetCurrentThreadId() == m_WriterOwner );
  547. m_WriterOwner = 0;
  548. RTL_VERIFY( ReleaseSemaphore( m_WriterSemaphore, 1, NULL ) );
  549. LogLock("Unlocked lock with write access");
  550. }
  551. TaskScheduler::TaskSchedulerWorkGroup::TaskSchedulerWorkGroup(
  552. SidHandle Sid ) :
  553. m_Sid(Sid),
  554. m_ItemAvailableSemaphore(NULL),
  555. m_Threads(0),
  556. m_BusyThreads(0)
  557. {
  558. memset( m_Thread, 0, sizeof( m_Thread ) );
  559. memset( m_ThreadId, 0, sizeof( m_ThreadId ) );
  560. m_ItemAvailableSemaphore =
  561. CreateSemaphore(
  562. NULL,
  563. 0, // InitialCount
  564. 0x7FFFFFFF, // MaxCount
  565. NULL );
  566. if ( !m_ItemAvailableSemaphore )
  567. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  568. }
  569. TaskScheduler::TaskSchedulerWorkGroup::~TaskSchedulerWorkGroup()
  570. {
  571. if ( m_ItemAvailableSemaphore )
  572. CloseHandle( m_ItemAvailableSemaphore );
  573. }
  574. void
  575. TaskScheduler::AddItemToWorkGroup(
  576. SidHandle Sid,
  577. TaskSchedulerWorkItem *pWorkItem )
  578. {
  579. // If the work group has alread been created,
  580. // don't create it again
  581. WorkGroupMapType::iterator i = m_WorkGroupMap.find( Sid );
  582. TaskSchedulerWorkGroup *pWorkGroup = NULL;
  583. if ( m_WorkGroupMap.end() != i )
  584. {
  585. pWorkGroup = (*i).second;
  586. }
  587. else
  588. {
  589. LogTask( "Creating a new work group" );
  590. while(1)
  591. {
  592. try
  593. {
  594. pWorkGroup = new TaskSchedulerWorkGroup( Sid );
  595. m_WorkGroupMap.insert( WorkGroupMapType::value_type( Sid, pWorkGroup ) );
  596. LogTask( "Created new workgroup %p", pWorkGroup );
  597. break;
  598. }
  599. catch( ComError Error )
  600. {
  601. LogError( "Unable to create new workgroup sleeping, error %!winerr!", Error.Error() );
  602. m_WorkGroupMap.erase( Sid );
  603. delete pWorkGroup;
  604. pWorkGroup = NULL;
  605. Sleep( 5000 );
  606. }
  607. }
  608. }
  609. LogInfo( "Adding %p to workgroup %p", pWorkItem, pWorkGroup );
  610. pWorkGroup->m_ReadyList.insert( *pWorkItem );
  611. pWorkItem->m_State = TASK_STATE_READY;
  612. pWorkItem->m_WorkGroup = pWorkGroup;
  613. RTL_VERIFY( ReleaseSemaphore( pWorkGroup->m_ItemAvailableSemaphore, 1, NULL ) );
  614. // use a very aproximative heuristic to determine when to add more threads.
  615. // The load is the number of work items that are ready to run plus the number
  616. // of items being worked on(busy threads). See the note below why the number of
  617. // ready work items is not a good estimate.
  618. size_t Load = pWorkGroup->m_ReadyList.size() + pWorkGroup->m_BusyThreads;
  619. if ( Load > pWorkGroup->m_Threads &&
  620. pWorkGroup->m_Threads < MAX_WORKGROUP_THREADS )
  621. {
  622. LogInfo( "load of %u and %u threads. Add another thread",
  623. Load, pWorkGroup->m_Threads );
  624. while(1)
  625. {
  626. m_NewWorkerGroup = pWorkGroup;
  627. ASSERT( m_WorkGroupMap.end() != m_WorkGroupMap.find( m_NewWorkerGroup->m_Sid ) );
  628. RTL_VERIFY( ResetEvent( m_WorkerInitialized ) );
  629. HANDLE & ThreadHandle = pWorkGroup->m_Thread[ pWorkGroup->m_Threads ];
  630. DWORD & ThreadId = pWorkGroup->m_ThreadId[ pWorkGroup->m_Threads ];
  631. ThreadHandle =
  632. CreateThread(
  633. NULL, // security descriptor
  634. 0, // Use default stack
  635. TaskScheduler::WorkGroupWorkerThunk,
  636. static_cast<LPVOID>( this ),
  637. 0,
  638. &ThreadId );
  639. if ( !ThreadHandle )
  640. {
  641. LogError( "Unable to create new worker, error %!winerr!", GetLastError() );
  642. Sleep( 5000 );
  643. continue;
  644. }
  645. LogTask( "Created new worker with a handle %p, ID %u", ThreadHandle, ThreadId );
  646. HANDLE WaitHandles[2] = { ThreadHandle, m_WorkerInitialized };
  647. DWORD dwResult =
  648. WaitForMultipleObjectsEx(
  649. 2,
  650. WaitHandles,
  651. FALSE,
  652. INFINITE,
  653. FALSE );
  654. switch( dwResult )
  655. {
  656. case WAIT_OBJECT_0:
  657. GetExitCodeThread( ThreadHandle, &dwResult );
  658. LogError( "Thread exited with code %!winerr!, sleeping", dwResult );
  659. CloseHandle( ThreadHandle );
  660. ThreadHandle = 0;
  661. ThreadId = 0;
  662. Sleep( 5000 );
  663. continue;
  664. case WAIT_OBJECT_0 + 1:
  665. break;
  666. default:
  667. LogError( "Unexpected error, %!winerr!", dwResult );
  668. ASSERT( 0 );
  669. }
  670. LogTask( "Worker signaled success" );
  671. m_NewWorkerGroup = NULL;
  672. pWorkGroup->m_Threads++;
  673. break;
  674. }
  675. }
  676. }
  677. void
  678. TaskScheduler::KillBackgroundTasks()
  679. {
  680. LogTask( "Killing background threads" );
  681. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  682. m_bShouldDie = TRUE;
  683. DWORD Result;
  684. while(1)
  685. {
  686. if ( m_WorkGroupMap.empty() )
  687. {
  688. LogTask( "No more work groups, all done" );
  689. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  690. return;
  691. }
  692. TaskSchedulerWorkGroup *pGroup = (*m_WorkGroupMap.begin()).second;
  693. RTL_VERIFY( ReleaseSemaphore( pGroup->m_ItemAvailableSemaphore, pGroup->m_Threads, NULL ) );
  694. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  695. Result = WaitForMultipleObjects( pGroup->m_Threads, pGroup->m_Thread, TRUE, INFINITE );
  696. // WAIT_OBJECT_0 == 0 so Result >= WAIT_OBJECT_0 is always true
  697. ASSERT( Result < WAIT_OBJECT_0 + pGroup->m_Threads );
  698. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  699. for(size_t c=0; c < pGroup->m_Threads; c++ )
  700. {
  701. CloseHandle( pGroup->m_Thread[c] );
  702. }
  703. m_WorkGroupMap.erase( pGroup->m_Sid );
  704. delete pGroup;
  705. LogTask( "Killed everyone in work group %p", pGroup );
  706. }
  707. }
  708. DWORD BackgroundThreadProcFilter(
  709. LPEXCEPTION_POINTERS ExceptionPointers );
  710. DWORD
  711. TaskScheduler::WorkGroupWorkerThunk( void *pContext )
  712. {
  713. __try
  714. {
  715. return
  716. static_cast<TaskScheduler*>( pContext )->WorkGroupWorker();
  717. }
  718. __except( BackgroundThreadProcFilter(
  719. GetExceptionInformation() ) )
  720. {
  721. ASSERT( 0 );
  722. }
  723. ASSERT( 0 );
  724. return 0;
  725. }
  726. DWORD
  727. TaskScheduler::WorkGroupWorker( )
  728. {
  729. HRESULT Hr;
  730. LogTask( "I'm alive!" );
  731. Hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
  732. if ( FAILED( Hr ) )
  733. {
  734. LogError( "CoInitializeEx failed, %!winerr!", Hr );
  735. return (DWORD)(Hr);
  736. }
  737. TaskSchedulerWorkGroup *pGroup = m_NewWorkerGroup;
  738. ASSERT( m_WorkGroupMap.end() != m_WorkGroupMap.find( pGroup->m_Sid ) );
  739. RTL_VERIFY( SetEvent( m_WorkerInitialized ) );
  740. LogTask( "Initialization complete" );
  741. while(1)
  742. {
  743. TaskSchedulerWorkItem *pWorkItem = NULL;
  744. HANDLE Handles[] = { pGroup->m_ItemAvailableSemaphore, m_SchedulerLock };
  745. DWORD dwWaitResult =
  746. WaitForMultipleObjectsEx(
  747. sizeof(Handles)/sizeof(*Handles),
  748. Handles,
  749. TRUE, // Wait for all events
  750. 30000,
  751. FALSE ); // ablertable wait
  752. switch( dwWaitResult )
  753. {
  754. case WAIT_OBJECT_0:
  755. case WAIT_OBJECT_0+1:
  756. break;
  757. case WAIT_TIMEOUT:
  758. {
  759. LogInfo( "Timeout expired, check if we have something to do");
  760. RTL_VERIFY( WaitForSingleObject( m_SchedulerLock, INFINITE ) == WAIT_OBJECT_0 );
  761. if ( pGroup->m_ReadyList.empty() )
  762. {
  763. goto cleanup_on_timeout;
  764. }
  765. else
  766. {
  767. LogTask( "Still stuff to do, stay alive" );
  768. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  769. continue;
  770. }
  771. }
  772. default:
  773. ASSERT(0);
  774. }
  775. if ( m_bShouldDie )
  776. {
  777. LogTask( "Ordered to die, do so" );
  778. goto dodie;
  779. }
  780. ASSERT( !pGroup->m_ReadyList.empty() );
  781. // Get first item in ready list and move
  782. // it over to running list.
  783. pWorkItem = &(*pGroup->m_ReadyList.begin());
  784. pGroup->m_ReadyList.erase( *pWorkItem );
  785. pGroup->m_RunningList.insert( *pWorkItem );
  786. pWorkItem->m_State = TASK_STATE_RUNNING;
  787. ASSERT( pGroup == pWorkItem->m_WorkGroup );
  788. // Mark this thread as busy
  789. // NOTE: This counter is needed because some
  790. // code marks work items as complete even though
  791. // the really arn't complete yet. So we need
  792. // to have this to indicatate has many threads
  793. // are really available.
  794. InterlockedIncrement( &pGroup->m_BusyThreads );
  795. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  796. // Now do the real dispatching
  797. LogTask( "dispatching %p", pWorkItem );
  798. RTL_VERIFY( TlsSetValue( m_WorkItemTLS, pWorkItem ) );
  799. pWorkItem->OnDispatch();
  800. if (GetCurrentWorkItem())
  801. CompleteWorkItem();
  802. // Mark this thread as free
  803. InterlockedDecrement( &pGroup->m_BusyThreads );
  804. }
  805. cleanup_on_timeout:
  806. if ( 1 == pGroup->m_Threads )
  807. {
  808. // If were the last thread, destroy the workgroup
  809. LogTask( "We are the only thread, destroy work group %p", pGroup );
  810. CloseHandle( pGroup->m_Thread[0] );
  811. WorkGroupMapType::iterator i = m_WorkGroupMap.find( pGroup->m_Sid );
  812. ASSERT( m_WorkGroupMap.end() != i );
  813. m_WorkGroupMap.erase( i );
  814. delete pGroup;
  815. }
  816. else
  817. {
  818. // we were not the last thread, so remove ourselves from the list.
  819. // First, find the slot for this thread.
  820. size_t index = 0;
  821. for (;index < pGroup->m_Threads; index++ )
  822. {
  823. if ( GetCurrentThreadId() == pGroup->m_ThreadId[index] )
  824. break;
  825. }
  826. ASSERT( index < pGroup->m_Threads );
  827. LogTask( "We are not the only thread, remove thread in slot %u", index );
  828. CloseHandle( pGroup->m_Thread[index] );
  829. // collapse the list
  830. size_t slots = pGroup->m_Threads - index - 1;
  831. memmove( &pGroup->m_Thread[index], &pGroup->m_Thread[index+1], slots * sizeof(*pGroup->m_Thread) );
  832. memmove( &pGroup->m_ThreadId[index], &pGroup->m_ThreadId[index+1], slots * sizeof(*pGroup->m_ThreadId) );
  833. pGroup->m_Threads--;
  834. pGroup->m_Thread[pGroup->m_Threads] = 0;
  835. pGroup->m_ThreadId[pGroup->m_Threads] = 0;
  836. }
  837. dodie:
  838. RTL_VERIFY( ReleaseMutex( m_SchedulerLock ) );
  839. CoUninitialize();
  840. return 0;
  841. }