Leaked source code of windows server 2003
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.

832 lines
21 KiB

  1. /*=========================================================================*\
  2. Module: idlethrd.cpp
  3. Copyright Microsoft Corporation 1998, All Rights Reserved.
  4. Author: zyang
  5. Description: Idle thread implementation
  6. \*=========================================================================*/
  7. #include <windows.h>
  8. #include <limits.h>
  9. #include <caldbg.h>
  10. #include <ex\exmem.h>
  11. #include <ex\autoptr.h>
  12. #include <ex\idlethrd.h>
  13. #pragma warning(disable:4127) // conditional expression is constant
  14. #pragma warning(disable:4244) // possible loss of data
  15. class CIdleThread;
  16. // Globals
  17. //
  18. CIdleThread * g_pIdleThread = NULL;
  19. // Debugging -----------------------------------------------------------------
  20. //
  21. DEFINE_TRACE(IdleThrd);
  22. #define IdleThrdTrace DO_TRACE(IdleThrd)
  23. enum
  24. {
  25. EVENT_SHUTDOWN = WAIT_OBJECT_0,
  26. EVENT_REGISTER = WAIT_OBJECT_0 + 1
  27. };
  28. // class CIdleThread
  29. //
  30. // This is the idle thread implementation. it accept clients callback
  31. // registration, and call back to the client when timeout.
  32. //
  33. // Instead of periodically wake up the thread, we maitain the minimal
  34. // time to wait, so that we wake only when it is necessary.
  35. //
  36. // As there could be a huge number of the registrations, so we maintain
  37. // a heap ordered by their next timeout value. so that we don't have to
  38. // iterate through all the registrations each time.
  39. //
  40. // When the callback is registered, DwWait will be called to get
  41. // initial timeout. Client can return zero thus cause the Execute be called
  42. // immediately.
  43. //
  44. class CIdleThread
  45. {
  46. private:
  47. struct REGISTRATION
  48. {
  49. __int64 m_i64Wakeup; // Time to wake up
  50. auto_ref_ptr<IIdleThreadCallBack> m_pCallBack; // Call back object
  51. };
  52. struct IDLETHREADTASKITEM
  53. {
  54. BOOL m_fRegister; // TRUE - register,
  55. // FALSE - unregister
  56. auto_ref_ptr<IIdleThreadCallBack> m_pCallBack; // Call back object
  57. };
  58. // Default starting chunk size (in number of registrations)
  59. //
  60. enum {
  61. #ifdef DBG
  62. CHUNKCOUNT_START = 2 // must be 2 or greater, as our first reg starts at index 1
  63. #else
  64. CHUNKCOUNT_START = 8192 / sizeof (REGISTRATION)
  65. #endif
  66. };
  67. HANDLE m_hIdleThread;
  68. HANDLE m_hevShutDown; // signaled to inform the idle
  69. // thread to shutdown
  70. HANDLE m_hevRegister; // signaled when new registration
  71. // comes.
  72. CRITICAL_SECTION m_csRegister; // Used to serialize registration operations
  73. ULONG m_cSize; // Length of the current priority queue
  74. ULONG m_cAllocated; // Size of the physical array allocated
  75. REGISTRATION * m_pData; // The registration priority queue.
  76. // prioritized on the wake up time.
  77. LONG m_lSleep; // Time to sleep before wakeup.
  78. // Negative or 0 means wakeup immediately.
  79. // To sleep forever, use LONG_MAX instead
  80. // of INFINITE because INFINITE (as a LONG)
  81. // is a *negative* number (i.e. it would be
  82. // interpreted as wake up immediately).
  83. IDLETHREADTASKITEM * m_pTask; // Array of reg/unregs to be processed
  84. ULONG m_cTask; // number of reg/unregs to be processed
  85. ULONG m_cTaskAllocated; // size of the array
  86. BOOL FStartIdleThread();
  87. static DWORD __stdcall DwIdleThreadProc(PVOID pvThreadData);
  88. inline VOID HeapAdd ();
  89. inline VOID HeapDelete (ULONG ulIndex);
  90. //$HACK
  91. // In order to avoid calling SetIndex on the deleted object
  92. // in Exchange, we pass in a flag to indicate whether this
  93. // Exchange() call is to delete a node, if so, then we should
  94. // not call SetIndex on the node that is at the end of the queue
  95. // (which is to be deleted)
  96. //$HACK
  97. inline VOID Exchange (ULONG ulIndex1, ULONG ulIndex2, BOOL fDelete = FALSE);
  98. inline VOID Heapify (ULONG ulIndex);
  99. VOID EnterReg() { EnterCriticalSection (&m_csRegister); }
  100. VOID LeaveReg() { LeaveCriticalSection (&m_csRegister); }
  101. // non-implemented
  102. //
  103. CIdleThread( const CIdleThread& );
  104. CIdleThread& operator=( const CIdleThread& );
  105. public:
  106. CIdleThread () :
  107. m_cSize (0),
  108. m_cAllocated (0),
  109. m_lSleep (LONG_MAX),
  110. m_pData (NULL),
  111. m_pTask (NULL),
  112. m_cTaskAllocated (0),
  113. m_cTask (0),
  114. m_hIdleThread (NULL),
  115. m_hevShutDown (NULL),
  116. m_hevRegister (NULL)
  117. {
  118. INIT_TRACE (IdleThrd);
  119. InitializeCriticalSection (&m_csRegister);
  120. }
  121. ~CIdleThread();
  122. BOOL FAddNewTask (IIdleThreadCallBack * pCallBack, BOOL fRegister);
  123. };
  124. // CIdleThread::DwIdleThreadProc
  125. // This is idle thread implementation.
  126. //
  127. DWORD __stdcall CIdleThread::DwIdleThreadProc(PVOID pvThreadData)
  128. {
  129. // Get the CIdlThread object
  130. //
  131. CIdleThread * pit = reinterpret_cast<CIdleThread *>(
  132. pvThreadData );
  133. HANDLE rgh[2];
  134. FILETIME ftNow;
  135. DWORD dw;
  136. // This thread wait for two events:
  137. // shutdown event, and
  138. // register event.
  139. //
  140. rgh[0] = pit->m_hevShutDown;
  141. rgh[1] = pit->m_hevRegister;
  142. // This thread maintains a mininum timeout it could wait.
  143. // and would wake up when it tiemouts
  144. do
  145. {
  146. DWORD dwRet;
  147. dwRet = WaitForMultipleObjects(2, // two events
  148. rgh, // event handles
  149. FALSE, // return if any event signaled
  150. pit->m_lSleep);// timeout in milliseconds.
  151. // If our shutdown event handle was signalled, suicide.
  152. // (OR if the event object is gonzo....)
  153. //
  154. switch (dwRet)
  155. {
  156. case WAIT_TIMEOUT:
  157. //$REVIEW
  158. // How accurate do we use the time? is a snapshot like this enough?
  159. // or we may need to this inside the loop
  160. //
  161. GetSystemTimeAsFileTime( &ftNow );
  162. // Now that Unregister is supported, we need to check the size of
  163. // the heap before we call back, as it's possible the call back
  164. // has been unregistered.
  165. //
  166. while (pit->m_cSize &&
  167. (pit->m_pData[1].m_i64Wakeup <= *(__int64 *)(&ftNow)))
  168. {
  169. // Call back to client
  170. // Unregister if client required
  171. //
  172. Assert (pit->m_pData[1].m_pCallBack->UlIndex() == 1);
  173. if (!pit->m_pData[1].m_pCallBack->FExecute())
  174. {
  175. pit->m_pData[1].m_pCallBack.clear();
  176. //$HACK
  177. // In order to avoid calling SetIndex on the deleted object
  178. // in Exchange, we pass in a flag to indicate whether this
  179. // Exchange() call is to delete a node, if so, then we should
  180. // not call SetIndex on the node that is at the end of the queue
  181. // (which is to be deleted)
  182. //$HACK
  183. pit->Exchange (1, pit->m_cSize, TRUE);
  184. pit->m_cSize--;
  185. if (!pit->m_cSize)
  186. break;
  187. }
  188. else
  189. {
  190. // Get the next wakeup time
  191. // 1 millisecond = 10,000 of 100-nanoseconds
  192. //
  193. pit->m_pData[1].m_i64Wakeup = *(__int64 *)(&ftNow) +
  194. static_cast<__int64>(pit->m_pData[1].m_pCallBack->DwWait()) * 10000;
  195. }
  196. // Get the next value
  197. //
  198. pit->Heapify(1);
  199. }
  200. // Compute how long to wait before the next timeout
  201. //
  202. if (!pit->m_cSize)
  203. pit->m_lSleep = LONG_MAX;
  204. else
  205. {
  206. pit->m_lSleep = (pit->m_pData[1].m_i64Wakeup - *(__int64 *)(&ftNow)) / 10000;
  207. if (pit->m_lSleep < 0)
  208. {
  209. IdleThrdTrace ("Dav: Idle: zero or negative sleep: idle too active?");
  210. pit->m_lSleep = 0;
  211. }
  212. }
  213. IdleThrdTrace ("Dav: Idle: next idle action in:\n"
  214. "- milliseconds: %ld\n"
  215. "- seconds: %ld\n",
  216. pit->m_lSleep,
  217. pit->m_lSleep / 1000);
  218. break;
  219. case EVENT_REGISTER:
  220. {
  221. ULONG ul;
  222. ULONG ulNew;
  223. // Register the callback and obtain the initial timeout setting
  224. //$REVIEW
  225. // How accurate do we use the time? is a snapshot like this enough?
  226. // or we may need to this inside the loop
  227. //
  228. GetSystemTimeAsFileTime( &ftNow );
  229. // Make sure no one would add a new reg when we process the
  230. // new regs
  231. //
  232. pit->EnterReg();
  233. // It's possbile we've processed all the new regs in the
  234. // last time we were signaled
  235. //
  236. if (pit->m_cTask)
  237. {
  238. // Expand the queue to the maximum possible required length
  239. //
  240. if (pit->m_cSize + pit->m_cTask >= pit->m_cAllocated)
  241. {
  242. REGISTRATION * pData = NULL;
  243. ULONG cNewSize = 0;
  244. if (!pit->m_pData)
  245. {
  246. // Initial size of the priority queue
  247. //$Note: we need at least one more slot for exchange
  248. //
  249. cNewSize = max(pit->m_cTask + 1, CHUNKCOUNT_START);
  250. pData = static_cast<REGISTRATION *>(ExAlloc (
  251. cNewSize * sizeof (REGISTRATION)));
  252. }
  253. else
  254. {
  255. // Double the size, to get "logarithmic allocation behavior"
  256. //
  257. cNewSize = (pit->m_cSize + pit->m_cTask) * 2;
  258. // Realloc the array
  259. // If the realloc fails, the original remain unchanged
  260. //
  261. pData = static_cast<REGISTRATION *>(ExRealloc (pit->m_pData,
  262. cNewSize * sizeof(REGISTRATION)));
  263. }
  264. // It's possible that allocation failed
  265. //
  266. if (!pData)
  267. {
  268. //$REVIEW: Anything else can we do other than a debugtrace ?
  269. //
  270. IdleThrdTrace ("Cannot allocate more space\n");
  271. pit->LeaveReg();
  272. break;
  273. }
  274. // Initialize
  275. //
  276. ZeroMemory (pData + pit->m_cSize + 1,
  277. sizeof(REGISTRATION) * (cNewSize - pit->m_cSize - 1));
  278. // Update information
  279. //
  280. pit->m_pData = pData;
  281. pit->m_cAllocated = cNewSize;
  282. IdleThrdTrace ("priority queue size = %d\n", pit->m_cAllocated);
  283. }
  284. for (ul=0; ul < pit->m_cTask; ul++)
  285. {
  286. if (pit->m_pTask[ul].m_fRegister)
  287. {
  288. // New position of the reg
  289. //
  290. ulNew = pit->m_cSize + 1;
  291. IdleThrdTrace ("Dav: Idle: add new reg %x\n", pit->m_pTask[ul].m_pCallBack.get());
  292. dw = pit->m_pTask[ul].m_pCallBack->DwWait();
  293. pit->m_pData[ulNew].m_pCallBack.take_ownership (pit->m_pTask[ul].m_pCallBack.relinquish());
  294. // dw is give in milliseconds, FILETIME unit is 100-nanoseconds.
  295. //
  296. pit->m_pData[ulNew].m_i64Wakeup = *(__int64 *)(&ftNow) +
  297. static_cast<__int64>(dw) * 10000;
  298. // Update the index
  299. //
  300. pit->m_pData[ulNew].m_pCallBack->SetIndex(ulNew);
  301. // Add to the heap, m_cSize is updated inside
  302. //
  303. pit->HeapAdd();
  304. }
  305. else
  306. {
  307. Assert (pit->m_pTask[ul].m_pCallBack->UlIndex() <= pit->m_cSize);
  308. IdleThrdTrace ("Dav: Idle: delete reg %x\n", pit->m_pTask[ul].m_pCallBack.get());
  309. // Delete from the priority queue, m_cSize is updated inside
  310. // it also release our ref on the deleted object
  311. //
  312. pit->HeapDelete (pit->m_pTask[ul].m_pCallBack->UlIndex());
  313. pit->m_pTask[ul].m_pCallBack.clear();
  314. }
  315. }
  316. // Now that all task item are processed, reset
  317. //
  318. pit->m_cTask = 0;
  319. }
  320. // Done with the task array
  321. //
  322. pit->LeaveReg();
  323. // Compute the mininum time to wait
  324. //
  325. if (pit->m_cSize)
  326. {
  327. pit->m_lSleep = (pit->m_pData[1].m_i64Wakeup -
  328. *(__int64 *)(&ftNow)) / 10000;
  329. if (pit->m_lSleep < 0)
  330. {
  331. IdleThrdTrace ("Dav: Idle: zero or negative sleep: "
  332. "idle too active?");
  333. pit->m_lSleep = 0;
  334. }
  335. }
  336. else
  337. pit->m_lSleep = LONG_MAX;
  338. break;
  339. }
  340. default:
  341. // Either shutdown event is signaled or other failure
  342. //
  343. #ifdef DBG
  344. if (dwRet != EVENT_SHUTDOWN)
  345. {
  346. IdleThrdTrace ("Dav: Idle: thread quit because of failure\n");
  347. if (WAIT_FAILED == dwRet)
  348. {
  349. IdleThrdTrace ("Dav: Idle: last error = %d\n", GetLastError());
  350. }
  351. }
  352. #endif
  353. for (UINT i = 1; i <= pit->m_cSize; i++)
  354. {
  355. // Tell clients that the idle thread is being shutdown
  356. //
  357. Assert (pit->m_pData[i].m_pCallBack->UlIndex() == i);
  358. pit->m_pData[i].m_pCallBack->Shutdown ();
  359. pit->m_pData[i].m_pCallBack.clear();
  360. }
  361. IdleThrdTrace ("Dav: Idle: thread is stopping\n");
  362. // Shutdown this thread
  363. //
  364. return 0;
  365. }
  366. } while (TRUE);
  367. }
  368. CIdleThread::~CIdleThread()
  369. {
  370. if (m_hevShutDown && INVALID_HANDLE_VALUE != m_hevShutDown)
  371. {
  372. // Signal the idle thread to shutdown
  373. //
  374. SetEvent(m_hevShutDown);
  375. // Wait for the idle thread to shutdown
  376. //
  377. WaitForSingleObject(m_hIdleThread, INFINITE);
  378. }
  379. CloseHandle (m_hevShutDown);
  380. CloseHandle (m_hevRegister);
  381. //$REVIEW
  382. // Do I need to close the thread handle?
  383. //$REVIEW
  384. // Yes,
  385. //
  386. CloseHandle (m_hIdleThread);
  387. DeleteCriticalSection (&m_csRegister);
  388. // Free our array of items.
  389. ExFree (m_pData);
  390. ExFree (m_pTask);
  391. IdleThrdTrace ("DAV: Idle: CIdleThread destroyed\n");
  392. }
  393. //
  394. // CIdleThread::FStartIdleThead
  395. //
  396. // Helper method to start the idle thread and create the events
  397. //
  398. BOOL
  399. CIdleThread::FStartIdleThread()
  400. {
  401. BOOL fRet = FALSE;
  402. DWORD dwThreadId;
  403. m_hevShutDown = CreateEvent( NULL, // handle cannot be inherited
  404. FALSE, // auto reset
  405. FALSE, // nosignaled
  406. NULL); // no name
  407. if (!m_hevShutDown)
  408. {
  409. IdleThrdTrace( "Failed to create event: error: %d", GetLastError() );
  410. TrapSz( "Failed to create idle thread event" );
  411. goto ret;
  412. }
  413. m_hevRegister = CreateEvent( NULL, // handle cannot be inherited
  414. FALSE, // auto reset
  415. FALSE, // nosignaled
  416. NULL); // no name
  417. if (!m_hevRegister)
  418. {
  419. IdleThrdTrace( "Failed to create register event: error: %d", GetLastError() );
  420. TrapSz( "Failed to create register event" );
  421. goto ret;
  422. }
  423. m_hIdleThread = CreateThread( NULL,
  424. 0,
  425. DwIdleThreadProc,
  426. this,
  427. 0, // Start immediately -- no need to resume...
  428. &dwThreadId );
  429. if (!m_hIdleThread)
  430. {
  431. IdleThrdTrace( "Failed to create thread: error: %d", GetLastError() );
  432. TrapSz( "Failed to create Notif Cache Timer thread" );
  433. goto ret;
  434. }
  435. fRet = TRUE;
  436. ret:
  437. if (!fRet)
  438. {
  439. if (m_hevShutDown)
  440. {
  441. if (m_hevRegister && (INVALID_HANDLE_VALUE != m_hevRegister))
  442. {
  443. CloseHandle (m_hevRegister);
  444. m_hevRegister = NULL;
  445. }
  446. CloseHandle (m_hevShutDown);
  447. m_hevShutDown = NULL;
  448. }
  449. }
  450. return fRet;
  451. }
  452. //
  453. // CIdleThread::HeapAdd
  454. //
  455. // Add the the next node into the priority queue
  456. //
  457. inline
  458. VOID
  459. CIdleThread::HeapAdd ()
  460. {
  461. ULONG ulCur = m_cSize + 1;
  462. ULONG ulParent;
  463. Assert (m_pData);
  464. // We go bottom up, compare the node with the parent node,
  465. // exchange the two nodes if the child win. it stops when
  466. // the parent win.
  467. //
  468. while ( ulCur != 1)
  469. {
  470. ulParent = ulCur >> 1;
  471. if (m_pData[ulParent].m_i64Wakeup <= m_pData[ulCur].m_i64Wakeup)
  472. break;
  473. Exchange (ulCur, ulParent);
  474. ulCur = ulParent;
  475. }
  476. m_cSize++;
  477. }
  478. //
  479. // CIdleThread::HeapDelete
  480. //
  481. // Delete an arbitary node from the priority queue
  482. //
  483. inline
  484. VOID
  485. CIdleThread::HeapDelete (ULONG ulIndex)
  486. {
  487. Assert (ulIndex <= m_cSize);
  488. // Exchange the node to the end of the queue first
  489. // then heapify to maintain the heap property
  490. //
  491. //$HACK
  492. // In order to avoid calling SetIndex on the deleted object
  493. // in Exchange, we pass in a flag to indicate whether this
  494. // Exchange() call is to delete a node, if so, then we should
  495. // not call SetIndex on the node that is at the end of the queue
  496. // (which is to be deleted)
  497. //$HACK
  498. Exchange (ulIndex, m_cSize, TRUE);
  499. m_cSize--;
  500. Heapify (ulIndex);
  501. // Must Release our ref. m_cSize+1 is the one just deleted
  502. //
  503. m_pData[m_cSize+1].m_pCallBack.clear();
  504. }
  505. //
  506. // CIdleThread::Exchange
  507. //
  508. // Exchange two nodes
  509. //
  510. //$HACK
  511. // In order to avoid calling SetIndex on the deleted object
  512. // in Exchange, we pass in a flag to indicate whether this
  513. // Exchange() call is to delete a node, if so, then we should
  514. // not call SetIndex on the node that is at the end of the queue
  515. // (which is to be deleted)
  516. //$HACK
  517. inline
  518. VOID
  519. CIdleThread::Exchange (ULONG ulIndex1, ULONG ulIndex2, BOOL fDelete)
  520. {
  521. Assert ((ulIndex1 != 0) && (ulIndex1 <= m_cAllocated) &&
  522. (ulIndex2 != 0) && (ulIndex2 <= m_cAllocated));
  523. if (ulIndex1 != ulIndex2)
  524. {
  525. // Use the 0th node as the temp node
  526. //
  527. CopyMemory (m_pData, m_pData + ulIndex1, sizeof(REGISTRATION));
  528. CopyMemory (m_pData + ulIndex1, m_pData + ulIndex2, sizeof(REGISTRATION));
  529. CopyMemory (m_pData + ulIndex2, m_pData, sizeof(REGISTRATION));
  530. // Remember the index to facilitate unregister
  531. // Note the index is set if only the node is not deleted
  532. //
  533. if (!((ulIndex1 == m_cSize) && fDelete))
  534. m_pData[ulIndex1].m_pCallBack->SetIndex (ulIndex1);
  535. if (!((ulIndex2 == m_cSize) && fDelete))
  536. m_pData[ulIndex2].m_pCallBack->SetIndex (ulIndex2);
  537. }
  538. }
  539. //
  540. // CIdleThread::Heapify
  541. //
  542. // Maintain the heap property
  543. //
  544. inline
  545. VOID
  546. CIdleThread::Heapify (ULONG ulIndex)
  547. {
  548. ULONG ulLeft;
  549. ULONG ulRight;
  550. ULONG ulWin;
  551. Assert (m_pData);
  552. while (ulIndex <= m_cSize)
  553. {
  554. // Find out the winner (i.e. the one with earlier wakeup time)
  555. // between the parent and left node.
  556. //
  557. ulLeft = ulIndex * 2;
  558. if (ulLeft > m_cSize)
  559. break;
  560. if (m_pData[ulIndex].m_i64Wakeup > m_pData[ulLeft].m_i64Wakeup)
  561. ulWin = ulLeft;
  562. else
  563. ulWin = ulIndex;
  564. // Compare with the right node, and find out the final winner
  565. //
  566. ulRight = ulLeft + 1;
  567. if (ulRight <= m_cSize)
  568. {
  569. if (m_pData[ulWin].m_i64Wakeup > m_pData[ulRight].m_i64Wakeup)
  570. ulWin = ulRight;
  571. }
  572. // If the parent node is already the winner, then we are done,
  573. //
  574. if (ulIndex == ulWin)
  575. break;
  576. // Otherwise, exchange the parent node and winner node,
  577. //
  578. Exchange (ulWin, ulIndex);
  579. ulIndex = ulWin;
  580. }
  581. }
  582. //
  583. // CIdleThread::FAddNewTask
  584. //
  585. // Called by client to register or unregister a callback object
  586. //
  587. BOOL
  588. CIdleThread::FAddNewTask (IIdleThreadCallBack * pCallBack, BOOL fRegister)
  589. {
  590. BOOL fRet = TRUE;
  591. // Caller must garantee a valid callback object
  592. //
  593. Assert (pCallBack);
  594. EnterReg();
  595. Assert (!m_cTaskAllocated || (m_cTask <= m_cTaskAllocated));
  596. // Allocate more space if necessary
  597. //
  598. if (m_cTask == m_cTaskAllocated)
  599. {
  600. IDLETHREADTASKITEM * pTask = NULL;
  601. ULONG cNewSize = 0;
  602. if (!m_pTask)
  603. {
  604. Assert (m_cTask == 0);
  605. // Initial size of the priority queue
  606. // Starting at 8, we don't expect this queue will grow too big
  607. //
  608. cNewSize = 8;
  609. // Start idle thread when add the first registration
  610. //
  611. if (!FStartIdleThread ())
  612. {
  613. fRet = FALSE;
  614. goto ret;
  615. }
  616. pTask = static_cast<IDLETHREADTASKITEM *>(ExAlloc (
  617. cNewSize * sizeof (IDLETHREADTASKITEM)));
  618. }
  619. else
  620. {
  621. // Double the size, to get "logarithmic allocation behavior"
  622. //
  623. cNewSize = m_cTaskAllocated * 2;
  624. // Realloc the array
  625. // If the realloc fails, the original remain unchanged
  626. //
  627. pTask = static_cast<IDLETHREADTASKITEM *>(ExRealloc (m_pTask,
  628. cNewSize * sizeof(IDLETHREADTASKITEM)));
  629. }
  630. // It's possible that allocation failed
  631. //
  632. if (!pTask)
  633. {
  634. fRet = FALSE;
  635. goto ret;
  636. }
  637. // Must initialize, otherwise, we may start with uninitialize auto_ref_ptr
  638. //
  639. ZeroMemory (pTask + m_cTask, sizeof(IDLETHREADTASKITEM) * (cNewSize - m_cTask));
  640. // Update information
  641. //
  642. m_pTask = pTask;
  643. m_cTaskAllocated = cNewSize;
  644. IdleThrdTrace ("Taskitem queue size = %d\n", m_cTaskAllocated);
  645. }
  646. // Remember the new registration
  647. //
  648. m_pTask[m_cTask].m_pCallBack = pCallBack;
  649. m_pTask[m_cTask].m_fRegister = fRegister;
  650. m_cTask++;
  651. IdleThrdTrace ("New reg %x added at %d\n", pCallBack, m_cTask-1);
  652. ret:
  653. LeaveReg();
  654. // Inform the idle thread that new registration arrived
  655. //
  656. if (fRet)
  657. SetEvent (m_hevRegister);
  658. return fRet;
  659. }
  660. // FInitIdleThread
  661. //
  662. // Initialize the idle thread object. It can be out only once,
  663. // Note this call only initialize the CIdleThread object, the
  664. // idle thread is not started until the first registration
  665. //
  666. BOOL FInitIdleThread()
  667. {
  668. Assert (!g_pIdleThread);
  669. g_pIdleThread = new CIdleThread();
  670. return (g_pIdleThread != NULL);
  671. }
  672. // FDeleteIdleThread
  673. //
  674. // Delete the idle thread object. again, it can be called only once.
  675. //
  676. // Note this must be called before any other uninitialization work,
  677. // Because we don't own a ref to the callback object, all what we
  678. // have is a pointer to the object. in the shutdown time, we must
  679. // clear all the callback registration before the callback object
  680. // go away.
  681. //
  682. VOID DeleteIdleThread()
  683. {
  684. if (g_pIdleThread)
  685. delete g_pIdleThread;
  686. }
  687. // FRegister
  688. //
  689. // Register a callback
  690. //
  691. BOOL FRegister (IIdleThreadCallBack * pCallBack)
  692. {
  693. Assert (g_pIdleThread);
  694. return g_pIdleThread->FAddNewTask (pCallBack, TRUE);
  695. }
  696. VOID Unregister (IIdleThreadCallBack * pCallBack)
  697. {
  698. Assert (g_pIdleThread);
  699. g_pIdleThread->FAddNewTask (pCallBack, FALSE);
  700. }