Jobs.h 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. // Undefine name #define in OS headers
  7. #undef AddJob
  8. #undef GetJob
  9. class Parser;
  10. class CompileScriptException;
  11. namespace JsUtil
  12. {
  13. class JobManager;
  14. class WaitableJobManager;
  15. class SingleJobManager;
  16. class WaitableSingleJobManager;
  17. class JobProcessor;
  18. class ForegroundJobProcessor;
  19. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  20. class BackgroundJobProcessor;
  21. #endif
  22. struct ParallelThreadData;
  23. // -------------------------------------------------------------------------------------------------------------------------
  24. // Job
  25. //
  26. // Base class for jobs that can be sent to a job processor for processing
  27. // -------------------------------------------------------------------------------------------------------------------------
  28. class Job : public DoublyLinkedListElement<Job>
  29. {
  30. friend SingleJobManager;
  31. friend WaitableSingleJobManager;
  32. private:
  33. JobManager *manager;
  34. // Jobs may be aborted if the job processor is closed while there are still queued jobs, or if a job manager is removed
  35. // while it still has jobs queued to the job processor. Critical jobs are not aborted and rather processed during the
  36. // JobProcessor::Close call. Aborted jobs are not processed and instead the job manager is notified with
  37. // JobManager::JobProcessed(succeeded = false).
  38. const bool isCritical;
  39. private:
  40. Job(const bool isCritical = false);
  41. public:
  42. Job(JobManager *const manager, const bool isCritical = false);
  43. #if ENABLE_DEBUG_CONFIG_OPTIONS
  44. enum class FailureReason
  45. {
  46. NotFailed,
  47. OOM,
  48. StackOverflow,
  49. Aborted,
  50. ExceedJITLimit,
  51. Unknown
  52. };
  53. FailureReason failureReason;
  54. #endif
  55. public:
  56. JobManager *Manager() const;
  57. bool IsCritical() const;
  58. };
  59. // -------------------------------------------------------------------------------------------------------------------------
  60. // JobManager
  61. //
  62. // Base class for job managers that provide and know how to process jobs. Implementers who wish to be able to wait for a job
  63. // or job manager should derive from WaitableJobManager.
  64. // -------------------------------------------------------------------------------------------------------------------------
  65. class JobManager : public DoublyLinkedListElement<JobManager>
  66. {
  67. friend WaitableJobManager;
  68. friend JobProcessor;
  69. friend ForegroundJobProcessor;
  70. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  71. friend BackgroundJobProcessor;
  72. #endif
  73. private:
  74. JobProcessor *const processor;
  75. unsigned int numJobsAddedToProcessor;
  76. // Only job managers derived from WaitableJobManager support waiting for a job or the job manager's queued jobs
  77. const bool isWaitable;
  78. protected:
  79. JobManager(JobProcessor *const processor);
  80. private:
  81. JobManager(JobProcessor *const processor, const bool isWaitable); // only for use by WaitableJobManager
  82. public:
  83. JobProcessor *Processor() const;
  84. protected:
  85. // Called by the job processor (outside the lock) to process a job. A job manager may choose to return false to indicate
  86. // a failure. Throwing OutOfMemoryException or OperationAbortedException also indicate a processing failure.
  87. // 'pageAllocator' will be null if the job is being processed in the foreground.
  88. virtual bool Process(Job *const job, ParallelThreadData *threadData) = 0;
  89. // Called soon after Process by the job processor (inside the lock) to indicate that a job was processed. The
  90. // 'succeeded' parameter will be false if Process returned false, or threw one of the exceptions that trigger a failure
  91. // (see comments for Process). A job manager may choose to delete the job during this call.
  92. virtual void JobProcessed(Job *const job, const bool succeeded) = 0;
  93. // Called by the job processor (inside the lock) after the last job queued by the job manager to the job processor, is
  94. // processed. A job manager may choose to remove itself from the job processor and optionally delete itself during this
  95. // call, provided that it doesn't happen during a call that passes in the job manager as a parameter. For instance, a
  96. // job manager must not remove or delete itself during any calls to AddManager, RemoveManager, or Prioritize functions.
  97. virtual void LastJobProcessed();
  98. // JobProcessor (particularly background jobProcessor) can invoke multiple threads to process the jobs.
  99. // Manager will get one call back for each spawned thread before the AddManager call completes.
  100. // This is called outside any lock.
  101. virtual void ProcessorThreadSpecificCallBack(PageAllocator *pageAllocator);
  102. // Called when background thread page allocator is decommitted (currently after 1 idle second).
  103. virtual void OnDecommit(ParallelThreadData *threadData);
  104. // The following functions are called by the job processor in response to JobProcessor::AddJobAndProcessProactively,
  105. // PrioritizeManagerAndWait, PrioritizeJob, or PrioritizeJobAndWait. They are not virtual functions because the
  106. // aforementioned functions are templates that take the actual type of the manager, so derived job managers that wish to
  107. // use the aforementioned functions should implement these functions (and hence hide the base bare-bones
  108. // implementations).
  109. //
  110. // Job *GetJobToProcessProactively();
  111. // Called by the job processor as part of PrioritizeManagerAndWait (outside the lock) when its job queue depletes,
  112. // to obtain a job to process proactively. A job manager that does not have a job to provide should return 0. Jobs
  113. // provided by this function will be processed in the calling thread, so the job manager must support this.
  114. //
  115. // Job *GetJob(const <job holder type> jobHolder) const;
  116. // Called in response to PrioritizeJob or PrioritizeJobAndWait (inside the lock), to get the job from the job
  117. // holder. The purpose of this is that the job processor does not know the lifetime of a job. When a job manager
  118. // asks the job processor to prioritize a job and wait for it, by passing in a job directly, that contract doesn't
  119. // make sense because the job manager must ensure that the job object is not deleted during the call. If the job
  120. // manager wants to delete jobs after they're processed, it cannot guarantee that. So, the job manager provides a
  121. // job holder, and GetJob would then return the associated Job object if it's not yet processed, or 0 if it was
  122. // already processed. This function and JobProcessed are both called from inside the lock to enable deterministic
  123. // determination of whether a job is already processed, and if it hasn't been processed, to ensure that is won't be
  124. // processed until it is prioritized and the lock is released.
  125. //
  126. // bool WasAddedToJobProcessor(JsUtil::Job *const job) const;
  127. // Called in response to PrioritizeJob or PrioritizeJobAndWait (inside the lock), to determine whether the job to
  128. // prioritize was actually added to the job processor. Again, the job processor does not know this if the job
  129. // manager has other jobs that are not yet queued to the job processor. However, since doing this check and
  130. // prioritizing the job must both happen within a lock, a job manager should always call PrioritizeJob or
  131. // PrioritizeJobAndWait on the job processor to prioritize jobs that have or have not been added to the job
  132. // processor, and then use the return value of this function to indicate to the job processor whether the job was
  133. // added to the job processor.
  134. //
  135. // bool ShouldProcessInForeground(const bool willWaitForJob, const unsigned int numJobsInQueue) const;
  136. // Called by the BackgroundJobProcessor in response to PrioritizeJob or PrioritizeJobAndWait (inside the lock), to
  137. // determine whether the job may be processed in the current (foreground) thread synchronously.
  138. //
  139. // void Prioritize(JsUtil::Job *const job, const bool forceAddJobToProcessor = false);
  140. // Called in response to PrioritizeJob or PrioritizeJobAndWait (inside the lock), if false is returned from
  141. // WasAddedToJobProcessor. If the initial call was PrioritizeJobAndWait, the job processor will pass
  142. // forceAddToJobProcessor = true since it needs to wait for the job, and the job manager should ensure to add the
  143. // job to the job processor during the Prioritize call in that case.
  144. //
  145. // void PrioritizedButNotYetProcessed(JsUtil::Job *const job) const;
  146. // Called in response to PrioritizeJob (inside the lock), if the job was not yet processed. May be useful for
  147. // tracking how often the job manager asks to prioritize jobs and the jobs are not yet processed. Although the
  148. // return value of PrioritizeJob also indicates whether the job was already processed, work done in this function
  149. // may need to use the job object, in which case it's necessary to ensure that the job won't be deleted during that
  150. // time, and hence the existence of this function and why it's called inside the lock.
  151. //
  152. // void BeforeWaitForJob(Js::FunctionBody *const functionBody) const;
  153. // void AfterWaitForJob(Js::FunctionBody *const functionBody) const;
  154. // Called in response to PrioritizeJobAndWait (outside the lock), before and after waiting for the prioritized job
  155. // to be processed, respectively.
  156. //
  157. // The following are bare-bones implementations of some of the above functions and should be hidden if a job manager
  158. // wishes to provide its own implementation.
  159. Job *GetJobToProcessProactively();
  160. bool ShouldProcessInForeground(const bool willWaitForJob, const unsigned int numJobsInQueue) const;
  161. void Prioritize(JsUtil::Job *const job, const bool forceAddJobToProcessor = false, void* function = nullptr) const;
  162. void PrioritizedButNotYetProcessed(JsUtil::Job *const job) const;
  163. void BeforeWaitForJob(bool) const;
  164. void AfterWaitForJob(bool) const;
  165. };
  166. // -------------------------------------------------------------------------------------------------------------------------
  167. // WaitableJobManager
  168. //
  169. // Base class for job managers that provide and know how to process jobs, and wish to be able to wait for a job or job
  170. // manager.
  171. // -------------------------------------------------------------------------------------------------------------------------
  172. class WaitableJobManager : public JobManager
  173. {
  174. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  175. friend BackgroundJobProcessor;
  176. #endif
  177. private:
  178. Job *jobBeingWaitedUpon;
  179. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  180. Event jobBeingWaitedUponProcessed;
  181. #endif
  182. bool isWaitingForQueuedJobs;
  183. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  184. Event queuedJobsProcessed;
  185. #endif
  186. protected:
  187. WaitableJobManager(JobProcessor *const processor);
  188. };
  189. // -------------------------------------------------------------------------------------------------------------------------
  190. // SingleJobManager
  191. //
  192. // Base class for a job manager that queues a single job, to quickly create the job processing implementation inline and
  193. // queue the job. Since this class derives from JobManager, it does not support waiting and hence, it must be heap-allocated
  194. // and a derived class should override LastJobProcessed to remove itself from the job processor and delete itself. Derived
  195. // classes need only override Process and LastJobProcessed.
  196. // -------------------------------------------------------------------------------------------------------------------------
  197. class SingleJobManager : public JobManager
  198. {
  199. friend ForegroundJobProcessor;
  200. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  201. friend BackgroundJobProcessor;
  202. #endif
  203. private:
  204. JsUtil::Job job;
  205. bool processed;
  206. protected:
  207. SingleJobManager(JobProcessor *const processor, const bool isCritical = false);
  208. public:
  209. void AddJobToProcessor(const bool prioritize);
  210. protected:
  211. virtual void JobProcessed(JsUtil::Job *const job, const bool succeeded) override;
  212. JsUtil::Job *GetJob(bool);
  213. bool WasAddedToJobProcessor(JsUtil::Job *const job) const;
  214. };
  215. // -------------------------------------------------------------------------------------------------------------------------
  216. // WaitableSingleJobManager
  217. //
  218. // Base class for a job manager that queues a single job, to quickly create the job processing implementation inline and
  219. // queue the job. This class supports waiting for a job and hence, does not delete itself automatically. As a result, it's
  220. // possible to instantiate this class on the stack, and queue the job and wait for it, all inline without any heap
  221. // allocation. Derived classes need only override the Process function.
  222. // -------------------------------------------------------------------------------------------------------------------------
  223. class WaitableSingleJobManager : public WaitableJobManager
  224. {
  225. friend ForegroundJobProcessor;
  226. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  227. friend BackgroundJobProcessor;
  228. #endif
  229. private:
  230. JsUtil::Job job;
  231. bool processed;
  232. protected:
  233. WaitableSingleJobManager(JobProcessor *const processor, const bool isCritical = false);
  234. public:
  235. void AddJobToProcessor(const bool prioritize);
  236. void WaitForJobProcessed();
  237. protected:
  238. virtual void JobProcessed(JsUtil::Job *const job, const bool succeeded) override;
  239. JsUtil::Job *GetJob(bool);
  240. bool WasAddedToJobProcessor(JsUtil::Job *const job) const;
  241. };
  242. // -------------------------------------------------------------------------------------------------------------------------
  243. // JobProcessor
  244. //
  245. // Base class for job processors.
  246. // -------------------------------------------------------------------------------------------------------------------------
  247. class JobProcessor
  248. {
  249. private:
  250. bool processesInBackground;
  251. protected:
  252. DoublyLinkedList<JobManager> managers;
  253. DoublyLinkedList<Job> jobs;
  254. private:
  255. bool isClosed;
  256. protected:
  257. JobProcessor(const bool processesInBackground);
  258. public:
  259. // Ideally, a job manager should not need to depend on this, but there may be cases where it's needed (such as if
  260. // processing jobs needs to support the -profile switch)
  261. bool ProcessesInBackground() const;
  262. // Ideally, a job manager should not need to depend on this, but there may be cases where it's needed (such as if the
  263. // job manager needs to queue a critical cleanup job to the job processor when it's being destructed, it may need to
  264. // check first if the job processor is already closed, because it's illegal then)
  265. bool IsClosed() const;
  266. // A job manager generally should not need to take the lock, as most functions called by the job processor are called
  267. // from inside the lock as necessary. The main exception is when adding a job to the job processor; a job manager must
  268. // call JobProcessor::AddJob inside the lock. Use it sparingly.
  269. CriticalSection *GetCriticalSection();
  270. // Adds or removes a job manager
  271. virtual void AddManager(JobManager *const manager);
  272. virtual void RemoveManager(JobManager *const manager) = 0;
  273. bool HasManager(JobManager *const manager) const;
  274. template<class Fn> void ForEachManager(Fn fn);
  275. // Prioritizes a job manager, and optionally processes its jobs for a certain amount of time. When a job manager is
  276. // prioritized, its jobs are moved to the front of the queue. If the queue depletes of this job manager's jobs, the job
  277. // processor will call JobManager::GetJobToProcessProactively to proactively process jobs that have not yet been queued,
  278. // until the time limit. See comments in JobManager for details on why templates are used.
  279. void PrioritizeManager(JobManager *const manager);
  280. template<class TJobManager> void PrioritizeManagerAndWait(
  281. TJobManager *const manager,
  282. const unsigned int milliseconds = INFINITE);
  283. // Add a job to the queue, and optionally put it in front of the queue. Must be called from inside the lock. A job
  284. // manager should use JobManager::AcquireLock and JobManager::ReleaseLock for this purpose.
  285. virtual void AddJob(Job *const job, const bool prioritize = false);
  286. // Must be called from inside the lock
  287. virtual bool RemoveJob(Job *const job);
  288. template<class TJobManager, class TJobHolder>
  289. void AddJobAndProcessProactively(TJobManager *const jobManager, const TJobHolder holder);
  290. // Prioritizes a job by moving it to the front of the queue. For details on what more happens during this and on why
  291. // templates are used, see comments in JobManager. PrioritizeJob returns true if the job was already processed.
  292. template<class TJobManager, class TJobHolder> bool PrioritizeJob(
  293. TJobManager *const manager,
  294. const TJobHolder holder,
  295. void* function = nullptr);
  296. template<class TJobManager, class TJobHolder> void PrioritizeJobAndWait(
  297. TJobManager *const manager,
  298. const TJobHolder holder,
  299. void* function = nullptr);
  300. virtual void AssociatePageAllocator(PageAllocator* const pageAllocator) = 0;
  301. virtual void DissociatePageAllocator(PageAllocator* const pageAllocator) = 0;
  302. protected:
  303. void JobProcessed(JobManager *const manager, Job *const job, const bool succeeded);
  304. void LastJobProcessed(JobManager *const manager);
  305. public:
  306. // Closes the job processor and closes the handle of background threads.
  307. virtual void Close();
  308. };
  309. // -------------------------------------------------------------------------------------------------------------------------
  310. // ForegroundJobProcessor
  311. // -------------------------------------------------------------------------------------------------------------------------
  312. class ForegroundJobProcessor sealed : public JobProcessor
  313. {
  314. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  315. friend BackgroundJobProcessor;
  316. #endif
  317. public:
  318. ForegroundJobProcessor();
  319. public:
  320. virtual void RemoveManager(JobManager *const manager) override;
  321. template<class TJobManager> void PrioritizeManagerAndWait(
  322. TJobManager *const manager,
  323. const unsigned int milliseconds = INFINITE);
  324. template<class TJobManager, class TJobHolder>
  325. void AddJobAndProcessProactively(TJobManager *const jobManager, const TJobHolder holder);
  326. template<class TJobManager, class TJobHolder> bool PrioritizeJob(
  327. TJobManager *const manager,
  328. const TJobHolder holder,
  329. void* function = nullptr);
  330. template<class TJobManager, class TJobHolder> void PrioritizeJobAndWait(
  331. TJobManager *const manager,
  332. const TJobHolder holder,
  333. void* function = nullptr);
  334. private:
  335. // Calls JobManager::Process, handling specific exception types. The return value indicates whether the job succeeded
  336. // (true) or failed (false).
  337. static bool Process(Job *const job);
  338. virtual void Close() override;
  339. virtual void AssociatePageAllocator(PageAllocator* const pageAllocator) override;
  340. virtual void DissociatePageAllocator(PageAllocator* const pageAllocator) override;
  341. };
  342. // -------------------------------------------------------------------------------------------------------------------------
  343. // BackgroundJobProcessor
  344. // -------------------------------------------------------------------------------------------------------------------------
  345. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  346. struct ParallelThreadData
  347. {
  348. HANDLE threadHandle;
  349. bool isWaitingForJobs;
  350. bool canDecommit;
  351. Job *currentJob;
  352. Event threadStartedOrClosing; //This is only used for shutdown scenario to indicate background thread is shutting down or starting
  353. PageAllocator backgroundPageAllocator;
  354. ArenaAllocator *threadArena;
  355. BackgroundJobProcessor *processor;
  356. Parser *parser;
  357. CompileScriptException *pse;
  358. ParallelThreadData(AllocationPolicyManager* policyManager) :
  359. threadHandle(0),
  360. isWaitingForJobs(false),
  361. canDecommit(true),
  362. currentJob(nullptr),
  363. threadStartedOrClosing(false),
  364. backgroundPageAllocator(policyManager, Js::Configuration::Global.flags, PageAllocatorType_BGJIT,
  365. (AutoSystemInfo::Data.IsLowMemoryProcess() ?
  366. PageAllocator::DefaultLowMaxFreePageCount :
  367. PageAllocator::DefaultMaxFreePageCount)),
  368. threadArena(nullptr),
  369. processor(nullptr),
  370. parser(nullptr),
  371. pse(nullptr)
  372. {
  373. }
  374. PageAllocator* const GetPageAllocator() { return &backgroundPageAllocator; }
  375. bool CanDecommit() const { return canDecommit; }
  376. };
  377. class BackgroundJobProcessor sealed : public JobProcessor
  378. {
  379. private:
  380. CriticalSection criticalSection;
  381. Event jobReady; //This is an auto reset event, only one thread wakes up when the event is signaled.
  382. Event wakeAllBackgroundThreads; //This is a manual reset event.
  383. unsigned int numJobs;
  384. ThreadContextId threadId;
  385. ThreadService *threadService;
  386. unsigned int threadCount;
  387. unsigned int maxThreadCount;
  388. ParallelThreadData **parallelThreadData;
  389. #if DBG_DUMP
  390. static char16 const * const DebugThreadNames[16];
  391. #endif
  392. public:
  393. BackgroundJobProcessor(AllocationPolicyManager* policyManager, ThreadService *threadService, bool disableParallelThreads);
  394. ~BackgroundJobProcessor();
  395. private:
  396. bool WaitWithThread(ParallelThreadData *parallelThreadData, const Event &e, const unsigned int milliseconds = INFINITE);
  397. bool WaitWithThreadForThreadStartedOrClosingEvent(ParallelThreadData *parallelThreadData, const unsigned int milliseconds = INFINITE);
  398. void WaitWithAllThreadsForThreadStartedOrClosingEvent();
  399. bool WaitForJobReadyOrShutdown(ParallelThreadData *threadData); //Returns true for JobReady event is signaled.
  400. void IndicateNewJob();
  401. bool AreAllThreadsWaitingForJobs();
  402. uint NumberOfThreadsWaitingForJobs ();
  403. Job* GetCurrentJobOfManager(JobManager *const manager);
  404. ParallelThreadData * GetThreadDataFromCurrentJob(Job* job);
  405. void InitializeThreadCount();
  406. void InitializeParallelThreadData(AllocationPolicyManager* policyManager, bool disableParallelThreads);
  407. void InitializeParallelThreadDataForThreadServiceCallBack(AllocationPolicyManager* policyManager);
  408. public:
  409. virtual void AddManager(JobManager *const manager) override;
  410. virtual void RemoveManager(JobManager *const manager) override;
  411. template<class Fn> void ForEachManager(Fn fn); //This takes lock for criticalSection
  412. template<class TJobManager> void PrioritizeManagerAndWait(
  413. TJobManager *const manager,
  414. const unsigned int milliseconds = INFINITE);
  415. virtual void AddJob(Job *const job, const bool prioritize = false) override;
  416. virtual bool RemoveJob(Job *const job) override;
  417. template<class TJobManager, class TJobHolder>
  418. void AddJobAndProcessProactively(TJobManager *const jobManager, const TJobHolder holder);
  419. template<class TJobManager, class TJobHolder> bool PrioritizeJob(
  420. TJobManager *const manager,
  421. const TJobHolder holder,
  422. void* function = nullptr);
  423. template<class TJobManager, class TJobHolder> void PrioritizeJobAndWait(
  424. TJobManager *const manager,
  425. const TJobHolder holder,
  426. void* function = nullptr);
  427. // Calls JobManager::Process, handling specific exception types. The return value indicates whether the job succeeded
  428. // (true) or failed (false).
  429. bool Process(Job *const job, ParallelThreadData *threadData);
  430. bool IsBeingProcessed(Job *job);
  431. CriticalSection * GetCriticalSection() { return &criticalSection; }
  432. //Iterates each background thread, callback returns true when it needs to terminate the iteration.
  433. template<class Fn>
  434. bool IterateBackgroundThreads(Fn callback)
  435. {
  436. for (uint i = 0; i < this->threadCount; i++)
  437. {
  438. if (callback(this->parallelThreadData[i]))
  439. {
  440. return false;
  441. }
  442. }
  443. return true;
  444. }
  445. private:
  446. void Run(ParallelThreadData *threadData);
  447. public:
  448. virtual void Close() override;
  449. virtual void AssociatePageAllocator(PageAllocator* const pageAllocator) override;
  450. virtual void DissociatePageAllocator(PageAllocator* const pageAllocator) override;
  451. private:
  452. static unsigned int WINAPI StaticThreadProc(void *lpParam);
  453. #ifndef DISABLE_SEH
  454. static int ExceptFilter(LPEXCEPTION_POINTERS pEP);
  455. #endif
  456. static void CALLBACK ThreadServiceCallback(void * callbackData);
  457. };
  458. #endif
  459. }