ThrdPool.cpp
上传用户:dengkfang
上传日期:2008-12-30
资源大小:5233k
文件大小:24k
源码类别:

CA认证

开发平台:

Visual C++

  1. /*
  2. Module : ThrdPool.cpp
  3. Purpose: implementation for an MFC wrapper class for thread pools
  4. Created: PJN / 15-04-2001
  5. History: PJN / 21-07-2001 1. Made destructors of the two classes virtual as both can be used as base classes
  6.          PJN / 25-07-2001 1. Code now uses a Win9x compatible IO completion port if we fail to use the build 
  7.                           in OS one. This IOCP is implemented in the class "CIOCP9x" in IOCP9x.h/cpp.
  8.          PJN / 07-08-2001 1. Added a WaitForThreadsInitInstance. This allows code in the main thread to synchronise
  9.                           with the all the InitInstance calls in the thread pool
  10.          PJN / 23-08-2001 1. Removed unnecessary "#pragma message"
  11.          PJN / 15-04-2002 1. Class now uses new "CDirectedIOCPRequest" class instead of an SDK IOCP or dummy one
  12.                           for Win9x. This allows the thread pool to now support recycling of threads after a 
  13.                           specified interval in the thread pool.
  14.                           2. Tidied up the exposed API to not reflect the exact attributes of IO completion
  15.                           ports and instead be more object oriented.
  16.                           3. Change some of the class API parameters to provide for more flexibility
  17.          PJN / 29-04-2002 1. Fixed a bug in the CDirectedIOCP class which was causing multiple threads in the thread 
  18.                           pool to be released into the depths of the CDirectedIOCP::GetRequest code.
  19.                           2. Fixed a bug which was causing m_Threads array to be accessed from multiple threads
  20.                           when thread recycling was enabled.
  21.          PJN / 16-05-2002 1. Now provides an option to have a Q size different than the thread pool size.
  22.                           2. Also provides a method to post to the IOCP without first checking the limit
  23.          PJN / 18-08-2002 1. Renamed CDirectedIOCP to CDirectedThreadPoolQueue
  24.                           2. Renamed CDirectedIOCPRequest to CThreadPoolRequest
  25.                           3. Now user can decide which queing mechanism to use thro the Start method. 2 pre built
  26.                           classes are provided, namely CDirectedThreadPoolQueue and CIOCPThreadPoolQueue
  27.                           4. Provision of virtual GetNonDirectedRequestIndexToRemove and GetDirectedRequestIndexToRemove
  28.                           methods in the CDirectedThreadPoolQueue class. This allows derived classes to implement 
  29.                           their own schemes as to which requests to prioritize on the thread pool queue
  30.          PJN / 20-08-2002 1. Provided virtual destructors for all the classes which constitute the Thread pool framework
  31.                           2. Removed forward reference of the now defunct class CDirectedIOCP
  32.                           3. Removed unreferenced parameters level 4 warnings in the CThreadPool class declaration
  33.                           4. Fixed usage of 2 int variables in CDirectedThreadPoolQueue::GetNonDirectedRequestIndexToRemove
  34.                           and GetDirectedRequestIndexToRemove which were incorrectly declared as BOOL's. Thanks to 
  35.                           Serhiy Pavlov for these updates.
  36.          PJN / 04-10-2002 1. CThreadPoolClient::Run now has a return value to decide whether or not the worker thread
  37.                           should continue to service requests upon return from handling the current request
  38.          PJN / 08-10-2002 1. Shutting down of the thread pool now uses directed requests instead of undirected requests. 
  39.                           This should improve the speed of shutdown of the thread pool when it contains a lot of requests
  40.                           on the queue.
  41.                           2. Defined enums for m_dwID member of the CThreadPoolRequest class
  42.          PJN / 12-10-2002 1. Removed and replaced the PostRequestWithoutLimitCheck method with the standard PostRequest
  43.                           method. This avoids the problem with TRACE messages appearing along the lines
  44.                           "CDirectedThreadPoolQueue::GetRequest, Failed to release a semaphore". Thanks to Frank Schmidt
  45.                           for reporting this problem.
  46.                           2. Fixed a minor flow in "CDirectedThreadPoolQueue::Create()" where I forgot to call Close() 
  47.                           when the creation of "m_hGetRequestSemaphore" fails. Again thanks to Frank Schmidt for spotting 
  48.                           this.
  49.          PJN / 14-10-2002 1. Reintroduced the function CThreadPoolQueue::PostRequestWithoutLimitCheck as some users of
  50.                           the thread pool class had legit reasons to use this function.
  51.                           2. Changed a VERIFY call into an ASSERT in CThreadPoolServer::RecycleThread
  52.          PJN / 17-10-2002 1. Fixed a problem where CThreadPoolServer::Stop() would hang if an I/O completion port based
  53.                           thread pool is being used. Thanks to Frank Schmidt for spotting this problem.
  54.                           2. Made the thread pool class Win95 compliant by dynamically linking to the waitable timer 
  55.                           API's. Even though the code did gracefully degrade if the waitable timer functions failed, the
  56.                           fact that they did not use GetProcAddress to link to the functions meant that any app / dll
  57.                           which included the thread pool class would fail to load on Win95. Thanks to Frank Schmidt 
  58.                           for this update.
  59.          PJN / 07-11-2002 1. Minor update to the thread pool class to provide a virtual function which gets call when 
  60.                           the m_bRequestToStop is being set. 
  61.          PJN / 13-01-2004 1. Made the m_bRequestToStop member variable "volatile" as it can be modified from
  62.                           multiple threads while possible been read in a loop in another thread. Thanks to Dan Baker
  63.                           for reporting this issue.
  64.          PJN / 25-10-2004 1. Updated to compile cleanly when Detect 64 bit issues and Force conformance in for loop
  65.                           options are enabled in Visual Studio .Net
  66. Copyright (c) 2002 - 2005 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)
  67. All rights reserved.
  68. Copyright / Usage Details:
  69. You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 
  70. when your product is released in binary form. You are allowed to modify the source code in any way you want 
  71. except you cannot modify the copyright details at the top of each module. If you want to distribute source 
  72. code with your application, then you are only allowed to distribute versions released by the author. This is 
  73. to maintain a single distribution point for the source code. 
  74. */
  75. //////////////////// Includes /////////////////////////////////////////////////
  76. #include "stdafx.h"
  77. #include "ThrdPool.h"
  78. /////////////////// Macros / Defines //////////////////////////////////////////
  79. #ifdef _DEBUG
  80. #define new DEBUG_NEW
  81. #undef THIS_FILE
  82. static char THIS_FILE[] = __FILE__;
  83. #endif
  84. /////////////////// Implementation ////////////////////////////////////////////
  85. CThreadPoolRequest::CThreadPoolRequest()
  86. {
  87.   m_bDirectedRequest = FALSE;
  88.   m_nDirectedRequestIndex = -1;
  89.   m_dwID = THREADPOOL_USER_DEFINED_REQUEST;
  90.   m_pData = NULL;
  91. }
  92. CThreadPoolRequest::~CThreadPoolRequest()
  93. {
  94. }
  95. CThreadPoolRequest::CThreadPoolRequest(const CThreadPoolRequest& request)
  96. {
  97.   *this = request;
  98. }
  99. CThreadPoolRequest& CThreadPoolRequest::operator=(const CThreadPoolRequest& request)
  100. {
  101.   m_bDirectedRequest = request.m_bDirectedRequest;
  102.   m_nDirectedRequestIndex = request.m_nDirectedRequestIndex;
  103.   m_dwID = request.m_dwID;
  104.   m_pData = request.m_pData;
  105.   
  106.   return *this;
  107. }
  108. IMPLEMENT_DYNCREATE(CThreadPoolClient, CObject)
  109. CThreadPoolClient::CThreadPoolClient()
  110. {
  111.   //Initialize the member variables to sensible default value
  112.   m_pWorkerThread = NULL;
  113.   m_pPoolServer = NULL;
  114.   m_bInitOK = FALSE;
  115.   InterlockedExchange((LPLONG) &m_bRequestToStop, FALSE);
  116.   m_nStartupThreadPriority = THREAD_PRIORITY_NORMAL;
  117.   m_nStackSize = 0;
  118.   m_nThreadIndex = -1;
  119. }
  120. CThreadPoolClient::~CThreadPoolClient()
  121. {
  122.   ASSERT(m_pWorkerThread == NULL); //Thread should be destroy by now
  123. }
  124. BOOL CThreadPoolClient::InitInstance()
  125. {
  126.   return TRUE; //Default behaviour is to allow "Run" to be called
  127. }
  128. int CThreadPoolClient::ExitInstance()
  129. {
  130.   return 1; //By default, return 1 as the thread exit code
  131. }
  132. BOOL CThreadPoolClient::Run(const CThreadPoolRequest& /*request*/)
  133. {
  134.   ASSERT(FALSE); //You need to override CThreadPoolClient::Run in your derived class
  135.   return FALSE;
  136. }
  137. void CThreadPoolClient::SetRequestToStop()
  138. {
  139.   InterlockedExchange((LPLONG) &m_bRequestToStop, TRUE);
  140. }
  141. UINT CThreadPoolClient::_Run(LPVOID pParam)
  142. {
  143.   //Validate our parameters
  144.   ASSERT(pParam);
  145.   //Get back the "this" pointer
  146.   CThreadPoolClient* pClient = (CThreadPoolClient*) pParam;
  147.   ASSERT(pClient);
  148.   ASSERT(pClient->IsKindOf(RUNTIME_CLASS(CThreadPoolClient)));
  149.   ASSERT(pClient->m_pPoolServer);
  150.   CThreadPoolQueue* pQueue = pClient->m_pPoolServer->GetQueue();
  151.   ASSERT(pQueue);
  152.   //Call InitInstance to allow thread pool initialization customisation,
  153.   //also store its return value in m_bInitOK
  154.   InterlockedExchange((LPLONG) &pClient->m_bInitOK, pClient->InitInstance());
  155.   //Signal the event to let the server know that we have completed InitInstance
  156.   pClient->m_evtInitCompleted.SetEvent();
  157.   //Should we continue
  158.   if (pClient->m_bInitOK)
  159.   {
  160.     //Get the queued packet posted to us from the manager class
  161.     CThreadPoolRequest request;
  162.   while (pQueue->GetRequest(request, pClient->m_nThreadIndex))
  163.   {
  164.   if (request.m_dwID == THREADPOOL_SHUTDOWN_REQUEST)  //Should we break out of the loop
  165.   break;
  166.   else
  167.         if (!pClient->Run(request)) //Allow the virtual function to handle the client request
  168.           break;
  169.   }
  170.   }
  171.   //Let the ExitInstance method decide the return code from the thread
  172.   return pClient->ExitInstance();
  173. }
  174. IMPLEMENT_DYNCREATE(CThreadPoolQueue, CObject);
  175. CThreadPoolQueue::CThreadPoolQueue()
  176. {
  177. }
  178. CThreadPoolQueue::~CThreadPoolQueue()
  179. {
  180. }
  181. CThreadPoolServer::CThreadPoolServer()
  182. {
  183.   //Initialize the member variables to sensible default value
  184.   m_bMaxLifetime = FALSE;
  185.   m_dwMaxLifetime = 0;
  186.   m_pLifetimeMonitorThread = NULL;
  187.   m_nLifetimeThreadIndex = 0;
  188.   m_pQueue = NULL;
  189.   //Dynamically pull in the Waitable timer API
  190.   HMODULE hKernel32 = GetModuleHandle(_T("kernel32.dll"));
  191.   if (hKernel32)
  192.   {
  193. #ifdef UNICODE
  194.     m_lpfnCreateWaitableTimer = (lpfnCreateWaitableTimer)GetProcAddress(hKernel32, "CreateWaitableTimerW");
  195. #else
  196.     m_lpfnCreateWaitableTimer = (lpfnCreateWaitableTimer)GetProcAddress(hKernel32, "CreateWaitableTimerA");
  197. #endif
  198.     m_lpfnSetWaitableTimer = (lpfnSetWaitableTimer)GetProcAddress(hKernel32, "SetWaitableTimer");
  199.   }
  200.   else
  201.   {
  202.     m_lpfnCreateWaitableTimer = NULL;
  203.     m_lpfnSetWaitableTimer = NULL;
  204.   }
  205. }
  206. CThreadPoolServer::~CThreadPoolServer()
  207. {
  208.   Stop();
  209. }
  210. BOOL CThreadPoolServer::Start(CRuntimeClass* pRuntimeClient, CRuntimeClass* pRuntimeQueue, int nPoolSize, int nQueueSize, BOOL bSuspended, int nPriority, UINT nStackSize)
  211. {
  212.   //Validate our parameters
  213.   ASSERT(pRuntimeClient);
  214.   ASSERT(pRuntimeClient->IsDerivedFrom(RUNTIME_CLASS(CThreadPoolClient))); //Your class needs to be derived from CThreadPoolClient
  215.   ASSERT(nPoolSize); //You must have at least 1 thread in the pool
  216.   ASSERT(pRuntimeQueue);
  217.   ASSERT(pRuntimeQueue->IsDerivedFrom(RUNTIME_CLASS(CThreadPoolQueue)));
  218.   //Stop if currently started
  219.   Stop();
  220.   //Try to create the queue
  221.   ASSERT(m_pQueue == NULL);
  222.   m_pQueue = (CThreadPoolQueue*) pRuntimeQueue->CreateObject();
  223.   if (m_pQueue == NULL)
  224.   {
  225.     TRACE(_T("CThreadPoolServer::Start, Failed to create the queuen"));
  226.     return FALSE;
  227.   }
  228.   //Serialize access to the threads array
  229.   CSingleLock sl(&m_csThreads, TRUE);
  230.   ASSERT(m_Threads.GetSize() == 0); //Should be empty by now
  231.   //Create the thread pool queue
  232.   if (!m_pQueue->Create(nQueueSize))
  233.   {
  234.     TRACE(_T("CThreadPoolServer::Start, Failed to create the queue, Error:%dn"), ::GetLastError());
  235.     return FALSE;
  236.   }
  237.   //Assume the best
  238.   BOOL bSuccess = TRUE;
  239.   //Setup the worker threads in the thread pool
  240.   m_Threads.SetSize(0, nPoolSize);
  241.   for (int i=0; i<nPoolSize && bSuccess; i++)
  242.   {
  243.     //Create the thread pool client object
  244.     CThreadPoolClient* pClient = (CThreadPoolClient*) pRuntimeClient->CreateObject();
  245.     if (pClient == NULL)
  246.     {
  247.       TRACE(_T("CThreadPoolServer::Start, Failed to create client thread pool object for thread pool at index %dn"), i);
  248.       bSuccess = FALSE;
  249.     }
  250.     if (pClient)
  251.     {
  252.       //Setup its member variables
  253.       pClient->m_pPoolServer = this;
  254.       pClient->m_nStartupThreadPriority = nPriority;
  255.       pClient->m_nStackSize = nStackSize;
  256.       pClient->m_nThreadIndex = i;
  257.       //Spin of a worker thread for it (initially suspened so that we can setup it correctly!)
  258.       pClient->m_pWorkerThread = AfxBeginThread(CThreadPoolClient::_Run, pClient, nPriority, nStackSize, CREATE_SUSPENDED);
  259.       if (pClient->m_pWorkerThread == NULL)
  260.       {
  261.         TRACE(_T("CThreadPoolServer::Start, Failed to create worker thread for thread pool at index %dn"), i);
  262.         delete pClient;
  263.         bSuccess = FALSE;
  264.       }
  265.       else
  266.       {
  267.         pClient->m_pWorkerThread->m_bAutoDelete = FALSE; //We are in charge of closing the thread
  268.         //Add it to the thread pool
  269.         m_Threads.SetAtGrow(i, pClient);
  270.       }
  271.     }
  272.   }
  273.   //Tidy up if anything failed
  274.   if (!bSuccess)
  275.     Stop();
  276.   else
  277.   {
  278.     //Now that everything is setup we can resume the threads in the thread pool (if need be)
  279.     if (!bSuspended)
  280.     {
  281.       for (int i=0; i<m_Threads.GetSize(); i++)
  282.       {
  283.         CThreadPoolClient* pClient = m_Threads.GetAt(i);
  284.         ASSERT(pClient);
  285.         pClient->m_pWorkerThread->ResumeThread();
  286.       }
  287.     }
  288.   }
  289.   return bSuccess;
  290. }
  291. void CThreadPoolServer::Stop()
  292. {
  293.   //Serialize access to the threads array
  294.   CSingleLock sl(&m_csThreads, TRUE);
  295.   int nThreads = (int) m_Threads.GetSize();
  296.   if (nThreads)
  297.   {
  298.     //Set the m_bRequestToStop in each thread to ask them to exit if they are
  299.     //currently processing a request in CThreadPoolClient::Run
  300.     int i;
  301.     for (i=0; i<nThreads; i++)
  302.     {
  303.       CThreadPoolClient* pClient = m_Threads.GetAt(i);
  304.       pClient->SetRequestToStop();
  305.     }
  306.     //Now post enough requests to get each thread in the thread pool to stop via
  307.     //a "special" request with an ID of THREADPOOL_SHUTDOWN_REQUEST
  308.     for (i=0; i<nThreads; i++)
  309.     {
  310.       CThreadPoolRequest killRequest;
  311.       killRequest.m_dwID = THREADPOOL_SHUTDOWN_REQUEST;
  312.       //Only use directed requests to shut down the thread pool if the pool supports
  313.       //directed requests
  314.       if (m_pQueue->SupportsDirectedRequests())
  315.       {
  316.         killRequest.m_bDirectedRequest = TRUE;
  317.         killRequest.m_nDirectedRequestIndex = i;
  318.       }
  319.       ASSERT(m_pQueue);
  320.       m_pQueue->PostRequest(killRequest);
  321.     }
  322.     //Wait for all the threads to exit in the thread pool
  323.     BOOL bMoreThreads = TRUE;
  324.     int nCurrentThreadIndex = 0;
  325.     while (bMoreThreads)
  326.     {
  327.       //Wait for as many threads at once as possible
  328.       int nCurrentThreadsToStop = min(MAXIMUM_WAIT_OBJECTS, nThreads - nCurrentThreadIndex);
  329.       //Setup the array of threads to wait on to exit
  330.       HANDLE hThreads[MAXIMUM_WAIT_OBJECTS];
  331.       for (int j=0; j<nCurrentThreadsToStop; j++)
  332.       {
  333.         CThreadPoolClient* pClient = m_Threads.GetAt(j + nCurrentThreadIndex);
  334.         ASSERT(pClient);
  335.         ASSERT(pClient->m_pWorkerThread);
  336.         hThreads[j] = pClient->m_pWorkerThread->m_hThread;
  337.       }
  338.       //Wait for the threads to exit
  339.       WaitForMultipleObjects(nCurrentThreadsToStop, hThreads, TRUE, INFINITE);
  340.       //Get ready for the next time around
  341.       nCurrentThreadIndex += nCurrentThreadsToStop;
  342.       bMoreThreads = (nCurrentThreadIndex < nThreads);
  343.     }
  344.     //Now free up all the memory associated with each thread
  345.     for (i=0; i<nThreads; i++)
  346.     {
  347.       CThreadPoolClient* pClient = m_Threads.GetAt(i);
  348.       ASSERT(pClient);
  349.       delete pClient->m_pWorkerThread;
  350.       pClient->m_pWorkerThread = NULL;
  351.       delete pClient;
  352.     }
  353.     m_Threads.RemoveAll();
  354.     //Close our queue object
  355.     delete m_pQueue;
  356.     m_pQueue = NULL;
  357.   }
  358.   //Bring down the monitoring thread if any
  359.   SetMaxThreadClientLifetime(FALSE, 0);  
  360. }
  361. CThreadPoolClient* CThreadPoolServer::GetAtClient(int nIndex)
  362. {
  363.   return m_Threads.GetAt(nIndex);
  364. }
  365. BOOL CThreadPoolServer::WaitForThreadsInitInstance()
  366. {
  367.   //Assume the worst
  368.   BOOL bInitOK = TRUE;
  369.   int nThreads = (int) m_Threads.GetSize();
  370.   if (nThreads)
  371.   {
  372.     BOOL bMoreThreads = TRUE;
  373.     int nCurrentThreadIndex = 0;
  374.     while (bMoreThreads)
  375.     {
  376.       //Wait for as many threads at once as possible
  377.       int nEventsToWaitOn = min(MAXIMUM_WAIT_OBJECTS, nThreads - nCurrentThreadIndex);
  378.       //Setup the array of threads to wait on to exit
  379.       HANDLE hEvents[MAXIMUM_WAIT_OBJECTS];
  380.       int j;
  381.       for (j=0; j<nEventsToWaitOn; j++)
  382.       {
  383.         CThreadPoolClient* pClient = m_Threads.GetAt(j + nCurrentThreadIndex);
  384.         ASSERT(pClient);
  385.         hEvents[j] = pClient->m_evtInitCompleted;
  386.       }
  387.       //Wait for the threads to to complete their InitInstance code
  388.       WaitForMultipleObjects(nEventsToWaitOn, hEvents, TRUE, INFINITE);
  389.       //Update the Init completed which is the logical "And" of all the InitInstances
  390.       for (j=0; j<nEventsToWaitOn && bInitOK; j++)
  391.       {
  392.         CThreadPoolClient* pClient = m_Threads.GetAt(j + nCurrentThreadIndex);
  393.         ASSERT(pClient);
  394.         bInitOK &= pClient->m_bInitOK;
  395.       }
  396.       //Get ready for the next time around
  397.       nCurrentThreadIndex += nEventsToWaitOn;
  398.       bMoreThreads = (nCurrentThreadIndex < nThreads);
  399.     }
  400.   }
  401.   else
  402.     bInitOK = FALSE;
  403.   return bInitOK;
  404. }
  405. BOOL CThreadPoolServer::SetMaxThreadClientLifetime(BOOL bEnableThreadLifetime, DWORD dwMinutes)
  406. {
  407.   //Kill the monitoring thread if currently active
  408.   if (m_pLifetimeMonitorThread)
  409.   {
  410.     m_evtRequestLifetimeThread.SetEvent();
  411.     WaitForSingleObject(m_pLifetimeMonitorThread->m_hThread, INFINITE);
  412.     delete m_pLifetimeMonitorThread;
  413.     m_pLifetimeMonitorThread = NULL;
  414.   }
  415.   //Hive away the member variables
  416.   m_bMaxLifetime = bEnableThreadLifetime;
  417.   m_dwMaxLifetime = dwMinutes;
  418.   //Recreate the monitoring thread if required
  419.   if (m_bMaxLifetime)
  420.   {
  421.     if (!m_pQueue->SupportsDirectedRequests())
  422.     {
  423.       TRACE(_T("Recyclying of threads in the thread pool is not supported because the Queue does not support directed requestsn"));
  424.       return FALSE;
  425.     }
  426.     ASSERT(m_pLifetimeMonitorThread == NULL);
  427.     m_pLifetimeMonitorThread = AfxBeginThread(CThreadPoolServer::_Monitor, this, THREAD_PRIORITY_IDLE, 0, CREATE_SUSPENDED);
  428.     if (m_pLifetimeMonitorThread == NULL)
  429.     {
  430.       TRACE(_T("CThreadPoolServer::SetMaxThreadClientLifetime, Failed to create worker thread for monitoring thread in thread pooln"));
  431.       m_bMaxLifetime = FALSE;
  432.       m_dwMaxLifetime = 0;
  433.       return FALSE;
  434.     }
  435.     else
  436.     {
  437.       m_pLifetimeMonitorThread->m_bAutoDelete = FALSE; //We are in charge of closing the thread
  438.       m_pLifetimeMonitorThread->ResumeThread();
  439.     }
  440.   }
  441.   return TRUE;
  442. }
  443. UINT CThreadPoolServer::_Monitor(LPVOID pParam)
  444. {
  445.   //Validate our parameters
  446.   ASSERT(pParam);
  447.   //Convert from the SDK world to the C++ world
  448.   CThreadPoolServer* pServer = (CThreadPoolServer*) pParam;
  449.   ASSERT(pServer);
  450.   return pServer->Monitor();
  451. }
  452. UINT CThreadPoolServer::Monitor()
  453. {
  454.   //Work out the time interval (in ms) at which threads in the thread pool need to be recycled
  455.   CSingleLock sl(&m_csThreads, TRUE);
  456.   DWORD dwWaitInterval = m_dwMaxLifetime * 60000 / m_Threads.GetSize(); 
  457.   sl.Unlock();
  458.   //Try to use a waitable timer in preference to a Sleep busy loop
  459.   HANDLE hTimer = NULL;
  460.   if (m_lpfnCreateWaitableTimer && m_lpfnSetWaitableTimer)
  461.     hTimer = m_lpfnCreateWaitableTimer(NULL, FALSE, NULL);
  462.   __int64 nFileTimes = ((__int64)-10000) * ((__int64)dwWaitInterval);
  463.   LARGE_INTEGER li;
  464.   li.LowPart = (DWORD) (nFileTimes & 0xFFFFFFFF);
  465.   li.HighPart = (LONG) (nFileTimes >> 32); 
  466.   if (hTimer && m_lpfnSetWaitableTimer(hTimer, &li,dwWaitInterval, NULL, NULL, TRUE))
  467.   {
  468.     //Set up the handle array to wait on
  469.     HANDLE HandlesToWaitOn[2];
  470.     HandlesToWaitOn[0] = hTimer;
  471.     HandlesToWaitOn[1] = m_evtRequestLifetimeThread;
  472.     //Enter into the tight loop
  473.     BOOL bWantStop = FALSE;
  474.     while (!bWantStop)
  475.     {
  476.       //wait for either of the handles to become signalled
  477.       DWORD dwWait = WaitForMultipleObjects(2, HandlesToWaitOn, FALSE, INFINITE);
  478.       int nSignaledHandle = dwWait - WAIT_OBJECT_0;
  479.       if (nSignaledHandle == 1) //It was the stop request
  480.         bWantStop = TRUE;
  481.       else if (nSignaledHandle == 0) //It was the waitable timer
  482.         RecycleThread();
  483.     }
  484.     //Free up the waitable timer now that we are finished with it
  485.     CloseHandle(hTimer);
  486.   }
  487.   else
  488.   {
  489.     TRACE(_T("CThreadPoolServer::Monitor, Waitable timer could not be created and set, falling back to using a sleep busy loopn"));
  490.     //Must use a sleep busy loop since waitable timers are not available
  491.     DWORD dwStartTick = GetTickCount();
  492.     BOOL bWantStop = FALSE;
  493.     while (!bWantStop)
  494.     {
  495.       Sleep(1000);
  496.       //Check to see if the m_evtRequestLifetimeThread event is signaled
  497.       DWORD dwWait = WaitForSingleObject(m_evtRequestLifetimeThread, 0);
  498.       if (dwWait == WAIT_OBJECT_0)
  499.         bWantStop = TRUE;
  500.       else
  501.       {
  502.         //Check to see if it is time to recycle a thread
  503.         DWORD dwNowTick = GetTickCount();
  504.         if ((dwNowTick - dwStartTick) > dwWaitInterval) 
  505.         {
  506.           RecycleThread();
  507.           dwStartTick = dwNowTick;
  508.         }
  509.       }
  510.     }
  511.   }
  512.   return 0L;
  513. }
  514. BOOL CThreadPoolServer::RecycleThread()
  515. {
  516.   //Serialize access to the threads array
  517.   CSingleLock sl(&m_csThreads, TRUE);
  518.   //Assume the best
  519.   BOOL bSuccess = TRUE;
  520.   CThreadPoolClient* pClient = GetAtClient(m_nLifetimeThreadIndex);
  521.   //Get the specified thread to kill
  522.   //Set the m_bRequestToStop to ask it to exit if they are
  523.   //currently processing a request in CThreadPoolClient::Run
  524.   pClient->SetRequestToStop();
  525.    //Check to see if we need to post a directed request to the thread pool to get it to exit
  526.   BOOL bAlreadyExited = (WaitForSingleObject(pClient->m_pWorkerThread->m_hThread, 0) == WAIT_OBJECT_0);
  527.   if (!bAlreadyExited)
  528.   {
  529.     //Also post a directed request to the thread pool directly
  530.     TRACE(_T("CThreadPoolServer::RecycleThread, Killing thread at index %dn"), m_nLifetimeThreadIndex);
  531.     CThreadPoolRequest killRequest;
  532.     killRequest.m_dwID = THREADPOOL_SHUTDOWN_REQUEST;
  533.     killRequest.m_bDirectedRequest = TRUE;
  534.     killRequest.m_nDirectedRequestIndex = m_nLifetimeThreadIndex;
  535.     ASSERT(m_pQueue);
  536.     BOOL bPostedOK = m_pQueue->PostRequest(killRequest); 
  537.     ASSERT(bPostedOK); //If this call fails then you may be using a CThreadPoolQueue derived 
  538.                        //class which does not support directed requests, e.g. CIOCPThreadPoolQueue
  539.   }
  540.   //Wait for the thread to exit
  541.   WaitForSingleObject(pClient->m_pWorkerThread->m_hThread, INFINITE);
  542.   delete pClient->m_pWorkerThread;
  543.   //Now recreate the thread
  544.   //Spin of a worker thread for it (initially suspened so that we can setup it correctly!)
  545.   pClient->m_pWorkerThread = AfxBeginThread(CThreadPoolClient::_Run, pClient, pClient->m_nStartupThreadPriority, pClient->m_nStackSize, CREATE_SUSPENDED);
  546.   if (pClient->m_pWorkerThread == NULL)
  547.   {
  548.     TRACE(_T("CThreadPoolServer::RecycleThread, Failed to create worker thread for thread pool at index %dn"), m_nLifetimeThreadIndex);
  549.     bSuccess = FALSE;
  550.   }
  551.   else
  552.   {
  553.     pClient->m_pWorkerThread->m_bAutoDelete = FALSE; //We are in charge of closing the thread
  554.     pClient->m_pWorkerThread->ResumeThread(); //Resume the thread now that we have set it up correctly
  555.   }
  556.   //increment the thread index, ready for the next call into RecyleThread at a later date
  557.   m_nLifetimeThreadIndex = (m_nLifetimeThreadIndex + 1) % ((int) m_Threads.GetSize());
  558.   return bSuccess;
  559. }