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.

1146 lines
35 KiB

  1. #include "priv.h"
  2. #include "schedule.h"
  3. // debug stuff for tracking critical section owners.....
  4. #ifdef DEBUG
  5. #define DECLARE_CRITICAL_SECTION(x) CRITICAL_SECTION x; \
  6. DWORD dwThread##x;
  7. #define STATIC_DECLARE_CRITICAL_SECTION(x) static CRITICAL_SECTION x; \
  8. static DWORD dwThread##x;
  9. #define STATIC_INIT_CRITICAL_SECTION(c,x) CRITICAL_SECTION c::x = {0}; \
  10. DWORD c::dwThread##x;
  11. #define ASSERT_CRITICAL_SECTION(x) ASSERT( dwThread##x == GetCurrentThreadId() );
  12. #define OBJECT_ASSERT_CRITICAL_SECTION(o,x) ASSERT( o->dwThread##x == GetCurrentThreadId() );
  13. #define ENTER_CRITICAL_SECTION(x) EnterCriticalSection(&x); \
  14. dwThread##x = GetCurrentThreadId();
  15. #define OBJECT_ENTER_CRITICAL_SECTION(o,x) EnterCriticalSection(&o->x); \
  16. o->dwThread##x = GetCurrentThreadId();
  17. #define LEAVE_CRITICAL_SECTION(x) ASSERT_CRITICAL_SECTION(x); \
  18. LeaveCriticalSection(&x);
  19. #define OBJECT_LEAVE_CRITICAL_SECTION(o,x) OBJECT_ASSERT_CRITICAL_SECTION(o,x); \
  20. LeaveCriticalSection(&o->x);
  21. #else
  22. #define DECLARE_CRITICAL_SECTION(x) CRITICAL_SECTION x;
  23. #define STATIC_DECLARE_CRITICAL_SECTION(x) static CRITICAL_SECTION x;
  24. #define STATIC_INIT_CRITICAL_SECTION(c,x) CRITICAL_SECTION c::x = {0};
  25. #define ASSERT_CRITICAL_SECTION(x)
  26. #define OBJECT_ASSERT_CRITICAL_SECTION(o,x)
  27. #define ENTER_CRITICAL_SECTION(x) EnterCriticalSection(&x);
  28. #define OBJECT_ENTER_CRITICAL_SECTION(o,x) EnterCriticalSection(&o->x);
  29. #define LEAVE_CRITICAL_SECTION(x) LeaveCriticalSection(&x);
  30. #define OBJECT_LEAVE_CRITICAL_SECTION(o,x) LeaveCriticalSection(&o->x);
  31. #endif
  32. #define TF_SCHEDULER 0x20
  33. // struct to hold the details for each task that is to be executed....
  34. struct TaskNode
  35. {
  36. LPRUNNABLETASK pTask;
  37. TASKOWNERID toid;
  38. DWORD dwPriority;
  39. DWORD_PTR dwLParam;
  40. BOOL fSuspended;
  41. };
  42. class CShellTaskScheduler : public IShellTaskScheduler2
  43. {
  44. public:
  45. CShellTaskScheduler( HRESULT * pHr );
  46. ~CShellTaskScheduler();
  47. STDMETHOD (QueryInterface) (REFIID riid, LPVOID * ppvObj );
  48. STDMETHOD_(ULONG, AddRef)( void );
  49. STDMETHOD_(ULONG,Release)( void );
  50. STDMETHOD (AddTask)(IRunnableTask * pTask,
  51. REFTASKOWNERID rtoid,
  52. DWORD_PTR lParam,
  53. DWORD dwPriority );
  54. STDMETHOD (RemoveTasks)( REFTASKOWNERID rtoid,
  55. DWORD_PTR dwLParam,
  56. BOOL fWaitIfRunning );
  57. STDMETHOD (Status)( DWORD dwStatus, DWORD dwThreadTimeout );
  58. STDMETHOD_(UINT, CountTasks)(REFTASKOWNERID rtoid);
  59. STDMETHOD (AddTask2)(IRunnableTask * pTask,
  60. REFTASKOWNERID rtoid,
  61. DWORD_PTR lParam,
  62. DWORD dwPriority,
  63. DWORD grfFlags);
  64. STDMETHOD (MoveTask)(REFTASKOWNERID rtoid,
  65. DWORD_PTR dwLParam,
  66. DWORD dwPriority,
  67. DWORD grfFlags );
  68. protected:
  69. // data held by a task scheduler to refer to the current worker that it has....
  70. struct WorkerData
  71. {
  72. BOOL Init(CShellTaskScheduler *pts);
  73. // this (pThis) is used to pass the controlling
  74. // object back and forth to the thread, so that threads can be moved
  75. // back and forth from objects as they need them.
  76. CShellTaskScheduler * pThis;
  77. #ifdef DEBUG
  78. DWORD dwThreadID;
  79. #endif
  80. };
  81. friend UINT CShellTaskScheduler_ThreadProc( LPVOID pParam );
  82. friend int CALLBACK ListDestroyCallback( LPVOID p, LPVOID pData );
  83. VOID _KillScheduler( BOOL bKillCurTask );
  84. BOOL _WakeScheduler( void );
  85. BOOL _RemoveTasksFromList( REFTASKOWNERID rtoid, DWORD_PTR dwLParam );
  86. // create a worker thread data block that can be associated with a task scheduler....
  87. WorkerData * FetchWorker( void );
  88. // from a worker thread, let go of the scheduler it is associated...
  89. static BOOL ReleaseWorker( WorkerData * pThread );
  90. /***********PERINSTANCE DATA ************/
  91. DECLARE_CRITICAL_SECTION( m_csListLock )
  92. BOOL m_bListLockInited;
  93. HDPA m_hTaskList;
  94. WorkerData * m_pWorkerThread;
  95. // the currently running task...
  96. TaskNode * m_pRunning;
  97. // a semaphore that counts, so that all waiters canbe released...
  98. HANDLE m_hCurTaskEnded;
  99. DWORD m_dwStatus;
  100. int m_iSignalCurTask; // - tell the thread to signal when the
  101. // current task is finished if non-zero
  102. // the other thread will signal the
  103. // handle as many times as this variable
  104. // holds.
  105. BOOL m_fEmptyQueueAndSleep; // - tell the thread to empty itself and
  106. // go to sleep (usually it is dying....
  107. int m_iGoToSleep; // - tell the tread to go to sleep without emptying the queue
  108. long m_cRef;
  109. #ifdef DEBUG
  110. void AssertForNoOneWaiting( void )
  111. {
  112. // no one should be queued for waiting
  113. ASSERT( m_iSignalCurTask == 0 );
  114. // release the semaphore by zero to get the current count....
  115. LONG lPrevCount = 0;
  116. ReleaseSemaphore( m_hCurTaskEnded, 0, &lPrevCount );
  117. ASSERT( lPrevCount == 0 );
  118. };
  119. #endif
  120. void IWantToKnowWhenCurTaskDone( void )
  121. {
  122. m_iSignalCurTask ++;
  123. };
  124. };
  125. // private messages sent to the scheduler thread...
  126. #define WM_SCH_WAKEUP WM_USER + 0x600
  127. #define WM_SCH_TERMINATE WM_USER + 0x601
  128. STDAPI CShellTaskScheduler_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  129. {
  130. if ( pUnkOuter )
  131. {
  132. return CLASS_E_NOAGGREGATION;
  133. }
  134. HRESULT hr = NOERROR;
  135. CShellTaskScheduler * pScheduler = new CShellTaskScheduler( & hr );
  136. if ( !pScheduler )
  137. {
  138. return E_OUTOFMEMORY;
  139. }
  140. if ( FAILED( hr ))
  141. {
  142. delete pScheduler;
  143. return hr;
  144. }
  145. *ppunk = SAFECAST(pScheduler, IShellTaskScheduler *);
  146. return NOERROR;
  147. }
  148. // Global ExplorerTaskScheduler object that is used by multiple components.
  149. IShellTaskScheduler * g_pTaskScheduler = NULL;
  150. // This is the class factory routine for creating the one and only ExplorerTaskScheduler object.
  151. // We have a static object (g_pTaskScheduler) that everyone who wants to use it shares.
  152. STDAPI CSharedTaskScheduler_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  153. {
  154. HRESULT hr = NOERROR;
  155. if (pUnkOuter)
  156. return CLASS_E_NOAGGREGATION;
  157. ENTERCRITICAL;
  158. if (g_pTaskScheduler)
  159. {
  160. g_pTaskScheduler->AddRef();
  161. }
  162. else
  163. {
  164. hr = CShellTaskScheduler_CreateInstance(NULL, (LPUNKNOWN*)&g_pTaskScheduler, NULL);
  165. if (SUCCEEDED(hr))
  166. {
  167. // set timeout to be 1 minute.....
  168. g_pTaskScheduler->Status( ITSSFLAG_KILL_ON_DESTROY, 1 * 60 * 1000 );
  169. // keep an additional ref for us..
  170. g_pTaskScheduler->AddRef();
  171. }
  172. }
  173. *ppunk = SAFECAST(g_pTaskScheduler, IShellTaskScheduler*);
  174. LEAVECRITICAL;
  175. return hr;
  176. }
  177. STDAPI SHIsThereASystemScheduler( void )
  178. {
  179. return ( g_pTaskScheduler ? S_OK : S_FALSE );
  180. }
  181. // use CoCreateInstance - thread pool removes need for global scheduler
  182. STDAPI SHGetSystemScheduler( LPSHELLTASKSCHEDULER * ppScheduler )
  183. {
  184. if ( !ppScheduler )
  185. {
  186. return E_INVALIDARG;
  187. }
  188. return CSharedTaskScheduler_CreateInstance(NULL, (IUnknown **)ppScheduler, NULL );
  189. }
  190. // use CoCreateInstance - thread pool removes need for global scheduler
  191. STDAPI SHFreeSystemScheduler( void )
  192. {
  193. TraceMsg(TF_SCHEDULER, "SHfss: g_pTaskSched=%x", g_pTaskScheduler);
  194. IShellTaskScheduler * pSched;
  195. ENTERCRITICAL;
  196. pSched = g_pTaskScheduler;
  197. g_pTaskScheduler = NULL;
  198. LEAVECRITICAL;
  199. if ( pSched )
  200. {
  201. // assume the scheduler is empty....
  202. pSched->RemoveTasks( TOID_NULL, ITSAT_DEFAULT_LPARAM, FALSE );
  203. pSched->Release();
  204. }
  205. return NOERROR;
  206. }
  207. #ifdef DEBUG
  208. STDAPI_(void) SHValidateEmptySystemScheduler()
  209. {
  210. if ( g_pTaskScheduler )
  211. {
  212. ASSERT( g_pTaskScheduler->CountTasks( TOID_NULL ) == 0 );
  213. }
  214. }
  215. #endif
  216. int InsertInPriorityOrder( HDPA hTaskList, TaskNode * pNewNode, BOOL fBefore );
  217. int CALLBACK ListDestroyCallback( LPVOID p, LPVOID pData )
  218. {
  219. ASSERT( p != NULL );
  220. if ( ! p )
  221. {
  222. TraceMsg( TF_ERROR, "ListDestroyCallback() - p is NULL!" );
  223. return TRUE;
  224. }
  225. CShellTaskScheduler * pThis = (CShellTaskScheduler *) pData;
  226. ASSERT( pThis );
  227. if ( ! pThis )
  228. {
  229. TraceMsg( TF_ERROR, "ListDestroyCallback() - pThis is NULL!" );
  230. return TRUE;
  231. }
  232. TaskNode * pNode = (TaskNode *) p;
  233. ASSERT( pNode != NULL );
  234. ASSERT( pNode->pTask != NULL );
  235. #ifdef DEBUG
  236. if ( pThis->m_pWorkerThread )
  237. {
  238. // notify the thread that we are emptying the list from here, so remove these
  239. // items from its mem track list
  240. }
  241. #endif
  242. // if it is suspended, kill it. If it is not suspended, then it has
  243. // probably never been started..
  244. if ( pNode->fSuspended )
  245. {
  246. pNode->pTask->Kill( pThis->m_dwStatus == ITSSFLAG_COMPLETE_ON_DESTROY );
  247. }
  248. pNode->pTask->Release();
  249. delete pNode;
  250. return TRUE;
  251. }
  252. STDMETHODIMP CShellTaskScheduler::QueryInterface( REFIID riid, LPVOID * ppvObj )
  253. {
  254. static const QITAB qit[] = {
  255. QITABENT(CShellTaskScheduler, IShellTaskScheduler),
  256. QITABENT(CShellTaskScheduler, IShellTaskScheduler2),
  257. { 0 },
  258. };
  259. return QISearch(this, qit, riid, ppvObj);
  260. }
  261. STDMETHODIMP_ (ULONG) CShellTaskScheduler::AddRef()
  262. {
  263. return InterlockedIncrement( &m_cRef );
  264. }
  265. STDMETHODIMP_ (ULONG) CShellTaskScheduler::Release()
  266. {
  267. ASSERT( 0 != m_cRef );
  268. ULONG cRef = InterlockedDecrement( &m_cRef );
  269. if ( 0 == cRef)
  270. {
  271. delete this;
  272. }
  273. return cRef;
  274. }
  275. CShellTaskScheduler::CShellTaskScheduler( HRESULT * pHr) : m_cRef(1)
  276. {
  277. *pHr = S_OK;
  278. ASSERT(!m_bListLockInited);
  279. __try
  280. {
  281. InitializeCriticalSection(&m_csListLock);
  282. m_bListLockInited = TRUE;
  283. }
  284. __except (EXCEPTION_EXECUTE_HANDLER)
  285. {
  286. *pHr = E_OUTOFMEMORY;
  287. }
  288. ASSERT(m_pWorkerThread == NULL);
  289. ASSERT(m_pRunning == NULL);
  290. m_dwStatus = ITSSFLAG_COMPLETE_ON_DESTROY;
  291. // grow queue by five each time...
  292. m_hTaskList = DPA_Create( 5 );
  293. if ( !m_hTaskList )
  294. {
  295. *pHr = E_OUTOFMEMORY;
  296. }
  297. m_hCurTaskEnded = CreateSemaphoreWrap( NULL, 0, 0xffff, NULL );
  298. if ( !m_hCurTaskEnded )
  299. {
  300. *pHr = E_FAIL;
  301. }
  302. DllAddRef();
  303. }
  304. CShellTaskScheduler::~CShellTaskScheduler()
  305. {
  306. // if we don't have a tasklist and semaphore (constructor failure), we can't have a workerthread
  307. ASSERT((m_hTaskList && m_hCurTaskEnded) || !m_pWorkerThread);
  308. // but if we have a task list...
  309. if ( m_hTaskList )
  310. {
  311. EnterCriticalSection( &m_csListLock );
  312. // if we have a background worker thread, then it MUST be doing something as we
  313. // are now in the crit section so it can't go away
  314. if ( m_pWorkerThread )
  315. {
  316. // we tell the object we need to know when it has done with its stuff....
  317. // we reuse the event we already have...
  318. m_fEmptyQueueAndSleep = TRUE;
  319. #ifdef DEBUG
  320. AssertForNoOneWaiting();
  321. #endif
  322. IWantToKnowWhenCurTaskDone();
  323. // tell the cur task to go away.....
  324. TraceMsg(TF_SCHEDULER, "(%x)csts.dtor: call _KillScheduler", GetCurrentThreadId());
  325. _KillScheduler( m_dwStatus == ITSSFLAG_KILL_ON_DESTROY );
  326. // free the thread. At this point there is always
  327. LeaveCriticalSection( &m_csListLock );
  328. TraceMsg(TF_SCHEDULER, "csts.dtor: call u.WFSMT(m_hCurTaskEnded=%x)", m_hCurTaskEnded);
  329. DWORD dwRes = SHWaitForSendMessageThread(m_hCurTaskEnded, INFINITE);
  330. ASSERT(dwRes == WAIT_OBJECT_0);
  331. TraceMsg(TF_SCHEDULER, "csts.dtor: u.WFSMT() done");
  332. ASSERT( !m_pWorkerThread );
  333. }
  334. else
  335. {
  336. LeaveCriticalSection( &m_csListLock );
  337. }
  338. // empty the list incase it is not empty (it should be)
  339. DPA_EnumCallback( m_hTaskList, ListDestroyCallback, this );
  340. DPA_DeleteAllPtrs( m_hTaskList );
  341. DPA_Destroy( m_hTaskList );
  342. m_hTaskList = NULL;
  343. }
  344. if ( m_hCurTaskEnded )
  345. CloseHandle( m_hCurTaskEnded );
  346. if (m_bListLockInited)
  347. DeleteCriticalSection( &m_csListLock );
  348. DllRelease();
  349. }
  350. STDMETHODIMP CShellTaskScheduler::AddTask( IRunnableTask * pTask,
  351. REFTASKOWNERID rtoid,
  352. DWORD_PTR dwLParam,
  353. DWORD dwPriority )
  354. {
  355. return AddTask2(pTask, rtoid, dwLParam, dwPriority, ITSSFLAG_TASK_PLACEINBACK);
  356. }
  357. STDMETHODIMP CShellTaskScheduler::AddTask2( IRunnableTask * pTask,
  358. REFTASKOWNERID rtoid,
  359. DWORD_PTR dwLParam,
  360. DWORD dwPriority,
  361. DWORD grfFlags )
  362. {
  363. if ( !pTask )
  364. return E_INVALIDARG;
  365. HRESULT hr = E_OUTOFMEMORY; // assume failure
  366. TaskNode * pNewNode = new TaskNode;
  367. if ( pNewNode )
  368. {
  369. pNewNode->pTask = pTask;
  370. pTask->AddRef();
  371. pNewNode->toid = rtoid;
  372. pNewNode->dwPriority = dwPriority;
  373. pNewNode->dwLParam = dwLParam;
  374. pNewNode->fSuspended = FALSE;
  375. EnterCriticalSection( &m_csListLock );
  376. int iPos = -1;
  377. if (grfFlags & ITSSFLAG_TASK_PLACEINFRONT)
  378. {
  379. iPos = InsertInPriorityOrder( m_hTaskList, pNewNode, TRUE );
  380. }
  381. else if (grfFlags & ITSSFLAG_TASK_PLACEINBACK)
  382. {
  383. iPos = InsertInPriorityOrder( m_hTaskList, pNewNode, FALSE );
  384. }
  385. if ( iPos != -1 && m_pRunning )
  386. {
  387. if ( m_pRunning->dwPriority < dwPriority )
  388. {
  389. // try to suspend the current task. If this works, the task will
  390. // return to the scheduler with E_PENDING. It will then be added
  391. // suspended in the queue to be Resumed later....
  392. m_pRunning->pTask->Suspend();
  393. }
  394. }
  395. BOOL bRes = FALSE;
  396. if ( iPos != -1 )
  397. {
  398. // get a worker thread and awaken it...
  399. // we do this in the crit section because we need to test m_pWorkerThread and
  400. // to save us from releasing and grabbing it again...
  401. bRes = _WakeScheduler();
  402. #ifdef DEBUG
  403. if ( bRes && m_pWorkerThread )
  404. {
  405. //
  406. // We are putting this memory block in a linked list and it will most likely be freed
  407. // from the background thread. Remove it from the per-thread memory list to avoid
  408. // detecting it as a memory leak.
  409. //
  410. // WARNING - WARNING - WARNING:
  411. // We cannot...
  412. // assume that when pTask is Released it will be deleted, so move it
  413. // to the other thread's memory list.
  414. //
  415. // This will be incorrect some of the time and we don't want to investigate
  416. // fake leaks. -BryanSt
  417. //transfer_to_thread_memlist( m_pWorkerThread->dwThreadID, pNewNode->pTask );
  418. }
  419. #endif
  420. }
  421. LeaveCriticalSection( &m_csListLock );
  422. // we failed to add it to the list
  423. if ( iPos == -1 )
  424. {
  425. // we failed to add it to the list, must have been a memory failure...
  426. pTask->Release(); // for the AddRef above
  427. delete pNewNode;
  428. goto Leave;
  429. }
  430. hr = bRes ? NOERROR : E_FAIL;
  431. }
  432. Leave:
  433. return hr;
  434. }
  435. STDMETHODIMP CShellTaskScheduler::RemoveTasks( REFTASKOWNERID rtoid,
  436. DWORD_PTR dwLParam,
  437. BOOL fWaitIfRunning )
  438. {
  439. BOOL fRemoveAll = IsEqualGUID( TOID_NULL, rtoid );
  440. BOOL fAllItems = (dwLParam == ITSAT_DEFAULT_LPARAM );
  441. BOOL fWaitOnHandle = FALSE;
  442. // note, this ignores the current
  443. EnterCriticalSection( &m_csListLock );
  444. _RemoveTasksFromList( rtoid, dwLParam );
  445. if ( m_pRunning && ( fWaitIfRunning || m_dwStatus == ITSSFLAG_KILL_ON_DESTROY ))
  446. {
  447. // kill the current task ...
  448. if (( fRemoveAll || IsEqualGUID( rtoid, m_pRunning->toid )) &&
  449. ( fAllItems || dwLParam == m_pRunning->dwLParam ))
  450. {
  451. ASSERT( m_pRunning->pTask );
  452. if ( m_dwStatus == ITSSFLAG_KILL_ON_DESTROY )
  453. {
  454. m_pRunning->pTask->Kill( fWaitIfRunning );
  455. }
  456. // definitive support for waiting until they are done...
  457. // (note, only do it is there is a task running, otherwise we'll sit
  458. // on a handle that will never fire)
  459. if ( fWaitIfRunning )
  460. {
  461. IWantToKnowWhenCurTaskDone();
  462. // don't use this directly outside of the cirtical section because it can change...
  463. ASSERT ( m_iSignalCurTask );
  464. fWaitOnHandle = TRUE;
  465. m_iGoToSleep++;
  466. }
  467. }
  468. }
  469. LeaveCriticalSection( &m_csListLock );
  470. // now wait if we need to......
  471. if ( fWaitOnHandle )
  472. {
  473. DWORD dwRes = SHWaitForSendMessageThread(m_hCurTaskEnded, INFINITE);
  474. ASSERT(dwRes == WAIT_OBJECT_0);
  475. EnterCriticalSection( &m_csListLock );
  476. // Remove tasks that might have been added while the last task was finishing
  477. _RemoveTasksFromList( rtoid, dwLParam );
  478. m_iGoToSleep--;
  479. // See if we need to wake the thread now.
  480. if ( m_iGoToSleep == 0 && DPA_GetPtrCount( m_hTaskList ) > 0 )
  481. _WakeScheduler();
  482. LeaveCriticalSection( &m_csListLock );
  483. }
  484. return NOERROR;
  485. }
  486. BOOL CShellTaskScheduler::_RemoveTasksFromList( REFTASKOWNERID rtoid, DWORD_PTR dwLParam )
  487. {
  488. // assumes that we are already holding the critical section
  489. BOOL fRemoveAll = IsEqualGUID( TOID_NULL, rtoid );
  490. BOOL fAllItems = (dwLParam == ITSAT_DEFAULT_LPARAM );
  491. int iIndex = 0;
  492. do
  493. {
  494. TaskNode * pNode = (TaskNode *) DPA_GetPtr( m_hTaskList, iIndex );
  495. if ( !pNode )
  496. {
  497. break;
  498. }
  499. ASSERT( pNode );
  500. ASSERT( pNode->pTask );
  501. if (( fRemoveAll || IsEqualGUID( pNode->toid, rtoid )) && ( fAllItems || dwLParam == pNode->dwLParam ))
  502. {
  503. // remove it
  504. DPA_DeletePtr( m_hTaskList, iIndex );
  505. if ( pNode->fSuspended )
  506. {
  507. // kill it just incase....
  508. pNode->pTask->Kill( FALSE );
  509. }
  510. pNode->pTask->Release();
  511. delete pNode;
  512. }
  513. else
  514. {
  515. iIndex ++;
  516. }
  517. }
  518. while ( TRUE );
  519. return TRUE;
  520. }
  521. //
  522. // CShellTaskScheduler::MoveTask
  523. //
  524. STDMETHODIMP CShellTaskScheduler::MoveTask( REFTASKOWNERID rtoid,
  525. DWORD_PTR dwLParam,
  526. DWORD dwPriority,
  527. DWORD grfFlags )
  528. {
  529. int iInsert;
  530. int iIndex;
  531. BOOL fMoveAll = IsEqualGUID( TOID_NULL, rtoid );
  532. BOOL fAllItems = (dwLParam == ITSAT_DEFAULT_LPARAM );
  533. BOOL bMatch = FALSE ;
  534. int iIndexStart;
  535. int iIndexInc;
  536. EnterCriticalSection( &m_csListLock );
  537. // Init direction of search
  538. if (grfFlags & ITSSFLAG_TASK_PLACEINFRONT)
  539. {
  540. iIndexStart = 0;
  541. iInsert = DPA_GetPtrCount( m_hTaskList );
  542. iIndexInc = 1;
  543. }
  544. else if (grfFlags & ITSSFLAG_TASK_PLACEINBACK)
  545. {
  546. iIndexStart = iInsert = DPA_GetPtrCount( m_hTaskList );
  547. iIndexInc = -1;
  548. }
  549. // Find insert point (based on priority)
  550. iIndex = 0;
  551. do
  552. {
  553. TaskNode * pNode = (TaskNode *) DPA_GetPtr( m_hTaskList, iIndex );
  554. if ( !pNode )
  555. {
  556. break;
  557. }
  558. if (grfFlags & ITSSFLAG_TASK_PLACEINFRONT)
  559. {
  560. if (pNode->dwPriority <= dwPriority)
  561. {
  562. iInsert = iIndex;
  563. break;
  564. }
  565. }
  566. else if (grfFlags & ITSSFLAG_TASK_PLACEINBACK)
  567. {
  568. if (pNode->dwPriority > dwPriority)
  569. {
  570. iInsert = iIndex;
  571. }
  572. else
  573. {
  574. break;
  575. }
  576. }
  577. iIndex++;
  578. }
  579. while (TRUE);
  580. // Now try and locate any items.
  581. iIndex = iIndexStart;
  582. do
  583. {
  584. TaskNode * pNode = (TaskNode *) DPA_GetPtr( m_hTaskList, iIndex );
  585. if ( !pNode )
  586. {
  587. break;
  588. }
  589. if (( fMoveAll || IsEqualGUID( pNode->toid, rtoid )) &&
  590. ( fAllItems || dwLParam == pNode->dwLParam ))
  591. {
  592. bMatch = TRUE;
  593. // Can we move this node?
  594. if ( iIndex != iInsert )
  595. {
  596. int iPos = DPA_InsertPtr( m_hTaskList, iInsert, pNode );
  597. if (iPos != -1)
  598. {
  599. if ( iIndex > iInsert )
  600. {
  601. DPA_DeletePtr( m_hTaskList, iIndex + 1); // Will have shifted one
  602. }
  603. else
  604. {
  605. DPA_DeletePtr( m_hTaskList, iIndex);
  606. }
  607. }
  608. }
  609. }
  610. iIndex += iIndexInc;
  611. }
  612. while ( !bMatch );
  613. LeaveCriticalSection( &m_csListLock );
  614. return (bMatch ? S_OK : S_FALSE);
  615. }
  616. BOOL CShellTaskScheduler::_WakeScheduler( )
  617. {
  618. // assume we are in the object's critsection.....
  619. if ( NULL == m_pWorkerThread )
  620. {
  621. // we need a worker quick ....
  622. m_pWorkerThread = FetchWorker();
  623. }
  624. return ( NULL != m_pWorkerThread );
  625. }
  626. VOID CShellTaskScheduler::_KillScheduler( BOOL bKillCurTask )
  627. {
  628. // assumes that we are already holding the critical section
  629. if ( m_pRunning != NULL && bKillCurTask )
  630. {
  631. ASSERT( m_pRunning->pTask );
  632. // tell the currently running task that it should die
  633. // quickly, because we are a separate thread than the
  634. // one that is running the task, it can be notified
  635. m_pRunning->pTask->Kill( FALSE );
  636. }
  637. }
  638. UINT CShellTaskScheduler_ThreadProc( LPVOID pParam )
  639. {
  640. // make sure we have a message QUEUE // BOGUS - why do we need this?
  641. MSG msg;
  642. PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE );
  643. ASSERT( pParam );
  644. HRESULT hrInit = SHCoInitialize();
  645. CShellTaskScheduler::WorkerData * pWorker = (CShellTaskScheduler::WorkerData *) pParam;
  646. DWORD dwRes = 0;
  647. TraceMsg(TF_SCHEDULER, "(?%x)ShellTaskScheduler::Thread Started", GetCurrentThreadId());
  648. #ifdef DEBUG
  649. pWorker->dwThreadID = GetCurrentThreadId();
  650. #endif
  651. // figure out who we are attatched to (where the queue is we get tasks from)
  652. CShellTaskScheduler * pThis = pWorker->pThis;
  653. // we must always have a valid parent object at this point....
  654. ASSERT( pThis && IS_VALID_WRITE_PTR( pThis, CShellTaskScheduler ));
  655. do
  656. {
  657. MSG msg;
  658. HRESULT hr = NOERROR;
  659. TaskNode * pTask = NULL;
  660. OBJECT_ENTER_CRITICAL_SECTION( pThis, m_csListLock );
  661. // this means we are being told to quit...
  662. if ( pThis->m_fEmptyQueueAndSleep )
  663. {
  664. // we are being told to empty the queue .....
  665. DPA_EnumCallback( pThis->m_hTaskList, ListDestroyCallback, pThis );
  666. DPA_DeleteAllPtrs( pThis->m_hTaskList );
  667. }
  668. else if ( !pThis->m_iGoToSleep )
  669. {
  670. // get the first item...
  671. pTask = (TaskNode *) DPA_GetPtr( pThis->m_hTaskList, 0 );
  672. }
  673. if ( pTask )
  674. {
  675. // remove from the list...
  676. DPA_DeletePtr( pThis->m_hTaskList, 0 );
  677. }
  678. pThis->m_pRunning = pTask;
  679. OBJECT_LEAVE_CRITICAL_SECTION( pThis, m_csListLock );
  680. if ( pTask == NULL )
  681. {
  682. // cache the scheduler pointer, as we need it to leave the crit section
  683. CShellTaskScheduler * pScheduler = pThis;
  684. // queue is empty, go back on the thread pool.....
  685. // we are about to enter a deep deep sleep/coma, so remove us from the object....
  686. OBJECT_ENTER_CRITICAL_SECTION( pScheduler, m_csListLock );
  687. HANDLE hSleep = pThis->m_fEmptyQueueAndSleep ? pThis->m_hCurTaskEnded : NULL;
  688. BOOL fEmptyAndLeave = pThis->m_fEmptyQueueAndSleep;
  689. // make sure they didn't just add something to the queue, or have we been asked to go to sleep
  690. if ( pThis->m_iGoToSleep || DPA_GetPtrCount( pThis->m_hTaskList ) == 0)
  691. {
  692. if ( CShellTaskScheduler::ReleaseWorker( pWorker ))
  693. {
  694. pThis = NULL;
  695. }
  696. }
  697. OBJECT_LEAVE_CRITICAL_SECTION( pScheduler, m_csListLock );
  698. if ( pThis && !fEmptyAndLeave )
  699. {
  700. // they must have added something at the last moment...
  701. continue;
  702. }
  703. // we are being emptied, tell them we are no longer attatched....
  704. if ( hSleep )
  705. {
  706. ReleaseSemaphore( hSleep, 1, NULL);
  707. }
  708. break;
  709. }
  710. else
  711. {
  712. #ifndef DEBUG
  713. //__try
  714. {
  715. #endif
  716. if ( pTask->fSuspended )
  717. {
  718. pTask->fSuspended = FALSE;
  719. hr = pTask->pTask->Resume();
  720. }
  721. else
  722. {
  723. // run the task...
  724. hr = pTask->pTask->Run( );
  725. }
  726. #ifndef DEBUG
  727. }
  728. //__except( EXCEPTION_EXECUTE_HANDLER )
  729. // {
  730. // ignore it.... and pray we are fine...
  731. //}
  732. // __endexcept
  733. #endif
  734. BOOL fEmptyQueue;
  735. OBJECT_ENTER_CRITICAL_SECTION( pThis, m_csListLock );
  736. {
  737. pThis->m_pRunning = NULL;
  738. // check to see if we have been asked to notify them
  739. // on completion....
  740. // NOTE: the NOT clause is needed so that we release ourselves
  741. // NOTE: and signal them at the right point, if we do it here,
  742. // NOTE: they leave us stranded, delete the crit section and
  743. // NOTE: we fault.
  744. if ( pThis->m_iSignalCurTask && !pThis->m_fEmptyQueueAndSleep )
  745. {
  746. LONG lPrevCount = 0;
  747. // release all those that are waiting. (we are using a semaphore
  748. // because we are a free threaded object and God knows how many
  749. // threads are waiting, and he passed on the information in the
  750. // iSignalCurTask variable
  751. ReleaseSemaphore( pThis->m_hCurTaskEnded, pThis->m_iSignalCurTask, &lPrevCount );
  752. // reset the count.
  753. pThis->m_iSignalCurTask = 0;
  754. }
  755. fEmptyQueue = pThis->m_fEmptyQueueAndSleep;
  756. }
  757. OBJECT_LEAVE_CRITICAL_SECTION( pThis, m_csListLock );
  758. if ( hr != E_PENDING || fEmptyQueue )
  759. {
  760. ULONG cRef = pTask->pTask->Release();
  761. delete pTask;
  762. pTask = NULL;
  763. }
  764. // empty the message queue...
  765. while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
  766. {
  767. {
  768. #ifdef DEBUG
  769. if (msg.message == WM_ENDSESSION)
  770. TraceMsg(TF_SCHEDULER, "(?%x)csts.tp: peek #2 got WM_ENDESSION", GetCurrentThreadId());
  771. #endif
  772. TranslateMessage( &msg );
  773. DispatchMessage( &msg );
  774. }
  775. }
  776. ASSERT( pThis && IS_VALID_WRITE_PTR( pThis, CShellTaskScheduler ));
  777. // the task must have been suspended because a higher priority
  778. // task has been added to the queue..... (this only works if the
  779. // task supports the Suspend() method).
  780. if ( hr == E_PENDING && pTask && !fEmptyQueue )
  781. {
  782. // put the task on the Suspended Queue ....
  783. pTask->fSuspended = TRUE;
  784. OBJECT_ENTER_CRITICAL_SECTION( pThis, m_csListLock );
  785. int iIndex = InsertInPriorityOrder( pThis->m_hTaskList, pTask, TRUE );
  786. OBJECT_LEAVE_CRITICAL_SECTION( pThis, m_csListLock );
  787. if ( iIndex == -1 )
  788. {
  789. // we are so low on memory, kill it...
  790. pTask->pTask->Kill( FALSE );
  791. pTask->pTask->Release();
  792. delete pTask;
  793. }
  794. pTask = NULL;
  795. }
  796. }
  797. }
  798. while ( TRUE );
  799. TraceMsg(TF_SCHEDULER, "(?%x)ShellTaskScheduler::Thread Ended", GetCurrentThreadId());
  800. SHCoUninitialize(hrInit);
  801. return 0;
  802. }
  803. STDMETHODIMP CShellTaskScheduler::Status( DWORD dwStatus, DWORD dwThreadTimeout )
  804. {
  805. m_dwStatus = dwStatus & ITSSFLAG_FLAGS_MASK;
  806. if ( dwThreadTimeout != ITSS_THREAD_TIMEOUT_NO_CHANGE )
  807. {
  808. /*
  809. * We don't support thread termination or pool timeout any more
  810. if ( dwStatus & ITSSFLAG_THREAD_TERMINATE_TIMEOUT )
  811. {
  812. m_dwThreadRlsKillTimeout = dwThreadTimeout;
  813. }
  814. else if ( dwStatus & ITSSFLAG_THREAD_POOL_TIMEOUT )
  815. {
  816. CShellTaskScheduler::s_dwComaTimeout = dwThreadTimeout;
  817. }
  818. */
  819. }
  820. return NOERROR;
  821. }
  822. STDMETHODIMP_(UINT) CShellTaskScheduler::CountTasks(REFTASKOWNERID rtoid)
  823. {
  824. UINT iMatch = 0;
  825. BOOL fMatchAll = IsEqualGUID( TOID_NULL, rtoid );
  826. ENTER_CRITICAL_SECTION( m_csListLock );
  827. if ( fMatchAll )
  828. {
  829. iMatch = DPA_GetPtrCount( m_hTaskList );
  830. }
  831. else
  832. {
  833. int iIndex = 0;
  834. do
  835. {
  836. TaskNode * pNode = (TaskNode * )DPA_GetPtr( m_hTaskList, iIndex ++ );
  837. if ( !pNode )
  838. {
  839. break;
  840. }
  841. if ( IsEqualGUID( pNode->toid, rtoid ))
  842. {
  843. iMatch ++;
  844. }
  845. }
  846. while ( TRUE );
  847. }
  848. if ( m_pRunning )
  849. {
  850. if ( fMatchAll || IsEqualGUID( rtoid, m_pRunning->toid ))
  851. {
  852. iMatch ++;
  853. }
  854. }
  855. LEAVE_CRITICAL_SECTION( m_csListLock );
  856. return iMatch;
  857. }
  858. ////////////////////////////////////////////////////////////////////////////////////
  859. int InsertInPriorityOrder( HDPA hTaskList, TaskNode * pNewNode, BOOL fStart )
  860. {
  861. // routine assumes that we are thread safe, therfore grab the crit-section
  862. // prior to calling this function
  863. int iPos = -1;
  864. int iIndex = 0;
  865. do
  866. {
  867. TaskNode * pNode = (TaskNode *) DPA_GetPtr( hTaskList, iIndex );
  868. if ( !pNode )
  869. {
  870. break;
  871. }
  872. // the fStart allows us to either add it before other tasks of the same
  873. // priority or after.
  874. if ((( pNode->dwPriority < pNewNode->dwPriority ) && !fStart ) || (( pNode->dwPriority <= pNewNode->dwPriority ) && fStart ))
  875. {
  876. iPos = DPA_InsertPtr( hTaskList, iIndex, pNewNode );
  877. break;
  878. }
  879. iIndex ++;
  880. }
  881. while ( TRUE );
  882. if ( iPos == -1 )
  883. {
  884. // add item to end of list...
  885. iPos = DPA_AppendPtr( hTaskList, pNewNode );
  886. }
  887. return iPos;
  888. }
  889. CShellTaskScheduler::WorkerData * CShellTaskScheduler::FetchWorker()
  890. {
  891. WorkerData * pWorker = new WorkerData;
  892. if ( pWorker )
  893. {
  894. // call to Shlwapi thread pool
  895. if ( pWorker->Init(this) && SHQueueUserWorkItem( (LPTHREAD_START_ROUTINE)CShellTaskScheduler_ThreadProc,
  896. pWorker,
  897. 0,
  898. (DWORD_PTR)NULL,
  899. (DWORD_PTR *)NULL,
  900. "browseui.dll",
  901. TPS_LONGEXECTIME | TPS_DEMANDTHREAD
  902. ) )
  903. {
  904. return pWorker;
  905. }
  906. else
  907. delete pWorker;
  908. }
  909. return NULL;
  910. }
  911. // used by main thread proc to release its link the the task scheduler because it
  912. // has run out of things to do....
  913. BOOL CShellTaskScheduler::ReleaseWorker( WorkerData * pWorker )
  914. {
  915. ASSERT( pWorker && IS_VALID_WRITE_PTR( pWorker, WorkerData ));
  916. CShellTaskScheduler * pThis = pWorker->pThis;
  917. OBJECT_ASSERT_CRITICAL_SECTION( pThis, m_csListLock );
  918. ASSERT( pWorker && IS_VALID_WRITE_PTR( pWorker, CShellTaskScheduler::WorkerData ));
  919. if ( DPA_GetPtrCount( pThis->m_hTaskList ) > 0 )
  920. {
  921. // something was added to the queue at the last minute....
  922. return FALSE;
  923. }
  924. // we assume we have entered the critsection of pThis
  925. pThis->m_pWorkerThread = NULL;
  926. pWorker->pThis = NULL;
  927. delete pWorker;
  928. return TRUE;
  929. }
  930. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  931. BOOL CShellTaskScheduler::WorkerData::Init(CShellTaskScheduler *pts)
  932. {
  933. pThis = pts;
  934. return TRUE;
  935. }