Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1194 lines
34 KiB

  1. //
  2. // MODULE: TOPICSHOP.CPP
  3. //
  4. // PURPOSE: Provide a means of "publishing" troubleshooter topics. This is where a
  5. // working thread goes to obtain a CTopic to use.
  6. //
  7. // COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
  8. //
  9. // AUTHOR: Joe Mabel
  10. //
  11. // ORIGINAL DATE: 9-10-98
  12. //
  13. // NOTES:
  14. //
  15. // Version Date By Comments
  16. //--------------------------------------------------------------------
  17. // V3.0 09-10-98 JM
  18. //
  19. #pragma warning(disable:4786)
  20. #include "stdafx.h"
  21. #include <algorithm>
  22. #include "TopicShop.h"
  23. #include "event.h"
  24. #include "apiwraps.h"
  25. #include "CharConv.h"
  26. #include "bn.h"
  27. #include "propnames.h"
  28. #ifdef LOCAL_TROUBLESHOOTER
  29. #include "CHMFileReader.h"
  30. #endif
  31. //////////////////////////////////////////////////////////////////////
  32. // CTopicInCatalog
  33. //////////////////////////////////////////////////////////////////////
  34. CTopicInCatalog::CTopicInCatalog(const CTopicInfo & topicinfo) :
  35. m_topicinfo(topicinfo),
  36. m_bTopicInfoMayNotBeCurrent(false),
  37. m_bInited(false),
  38. m_countLoad(CCounterLocation::eIdTopicLoad, topicinfo.GetNetworkName()),
  39. m_countLoadOK(CCounterLocation::eIdTopicLoadOK, topicinfo.GetNetworkName()),
  40. m_countEvent(CCounterLocation::eIdTopicEvent, topicinfo.GetNetworkName()),
  41. m_countHit(CCounterLocation::eIdTopicHit, topicinfo.GetNetworkName()),
  42. m_countHitNewCookie(CCounterLocation::eIdTopicHitNewCookie, topicinfo.GetNetworkName()),
  43. m_countHitOldCookie(CCounterLocation::eIdTopicHitOldCookie, topicinfo.GetNetworkName())
  44. {
  45. ::InitializeCriticalSection( &m_csTopicinfo);
  46. m_hev = ::CreateEvent(
  47. NULL,
  48. TRUE, // any number of (working) threads may be released on signal
  49. FALSE, // initially non-signalled
  50. NULL);
  51. if (! m_hev)
  52. {
  53. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  54. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  55. SrcLoc.GetSrcFileLineStr(),
  56. _T(""),
  57. _T(""),
  58. EV_GTS_ERROR_EVENT );
  59. // Simulate a bad alloc exception in this case.
  60. // This exception will be caught by the caller if the new call has been
  61. // properly wrapped in a try...catch() block. Only known caller is
  62. // CTopicShop::AddTopic() which handles this properly.
  63. throw bad_alloc();
  64. }
  65. m_countEvent.Increment();
  66. }
  67. CTopicInCatalog::~CTopicInCatalog()
  68. {
  69. if (m_hev)
  70. ::CloseHandle(m_hev);
  71. ::DeleteCriticalSection( &m_csTopicinfo);
  72. }
  73. CTopicInfo CTopicInCatalog::GetTopicInfo() const
  74. {
  75. ::EnterCriticalSection(&m_csTopicinfo);
  76. CTopicInfo ret(m_topicinfo);
  77. ::LeaveCriticalSection(&m_csTopicinfo);
  78. return ret;
  79. }
  80. void CTopicInCatalog::SetTopicInfo(const CTopicInfo &topicinfo)
  81. {
  82. ::EnterCriticalSection(&m_csTopicinfo);
  83. m_topicinfo = topicinfo;
  84. m_bTopicInfoMayNotBeCurrent = true;
  85. ::LeaveCriticalSection(&m_csTopicinfo);
  86. }
  87. // Just let this object know to increment the hit count
  88. void CTopicInCatalog::CountHit(bool bNewCookie)
  89. {
  90. m_countHit.Increment();
  91. if (bNewCookie)
  92. m_countHitNewCookie.Increment();
  93. else
  94. m_countHitOldCookie.Increment();
  95. }
  96. // Obtain a CP_TOPIC as a pointer to the topic, if that topic is already built.
  97. // As long as a CP_TOPIC remains undeleted, the associated CTopic is guaranteed to
  98. // remain undeleted.
  99. // Warning: this function will return with a null topic if topic is not yet built.
  100. // Must test for null with CP_TOPIC::IsNull(). Can't test a smart pointer for null
  101. // with ==.
  102. CP_TOPIC & CTopicInCatalog::GetTopicNoWait(CP_TOPIC &cpTopic) const
  103. {
  104. cpTopic = m_cpTopic;
  105. return cpTopic;
  106. }
  107. // Obtain a CP_TOPIC as a pointer to the topic.
  108. // Wait as necessary for that topic to be built.
  109. // Warning: this function will return with a null topic if topic cannot be built.
  110. // As long as a CP_TOPIC remains undeleted, the associated CTopic is guaranteed to
  111. // remain undeleted.
  112. // Warning: this function may have to wait for TopicInCatalog.m_cpTopic to be built.
  113. CP_TOPIC & CTopicInCatalog::GetTopic(CP_TOPIC &cpTopic) const
  114. {
  115. if (!m_bInited)
  116. {
  117. // Wait for a set period, if failure then log error msg and wait infinite.
  118. WAIT_INFINITE( m_hev );
  119. }
  120. return GetTopicNoWait(cpTopic);
  121. }
  122. // to be called by the TopicBuilderTask thread
  123. void CTopicInCatalog::Init(const CTopic* pTopic)
  124. {
  125. m_countLoad.Increment();
  126. if(pTopic)
  127. {
  128. m_cpTopic = pTopic;
  129. m_countLoadOK.Increment();
  130. }
  131. if(pTopic || m_cpTopic.IsNull())
  132. m_bInited = true;
  133. ::SetEvent(m_hev);
  134. }
  135. // Just let this object know to increment the count of changes detected.
  136. void CTopicInCatalog::CountChange()
  137. {
  138. m_countEvent.Increment();
  139. }
  140. CTopicInCatalog::TopicStatus CTopicInCatalog::GetTopicStatus() const
  141. {
  142. if (!m_bInited)
  143. return eNotInited;
  144. else if(m_cpTopic.IsNull())
  145. return eFail;
  146. else
  147. return eOK;
  148. }
  149. bool CTopicInCatalog::GetTopicInfoMayNotBeCurrent() const
  150. {
  151. ::EnterCriticalSection(&m_csTopicinfo);
  152. bool bRet= m_bTopicInfoMayNotBeCurrent;
  153. ::LeaveCriticalSection(&m_csTopicinfo);
  154. return bRet;
  155. }
  156. void CTopicInCatalog::TopicInfoIsCurrent()
  157. {
  158. ::EnterCriticalSection(&m_csTopicinfo);
  159. m_bTopicInfoMayNotBeCurrent = false;
  160. ::LeaveCriticalSection(&m_csTopicinfo);
  161. }
  162. //////////////////////////////////////////////////////////////////////
  163. // CTemplateInCatalog
  164. //////////////////////////////////////////////////////////////////////
  165. CTemplateInCatalog::CTemplateInCatalog( const CString & strTemplate ) :
  166. m_strTemplate( strTemplate ),
  167. m_countLoad(CCounterLocation::eIdTopicLoad, strTemplate),
  168. m_countLoadOK(CCounterLocation::eIdTopicLoadOK, strTemplate),
  169. m_countEvent(CCounterLocation::eIdTopicEvent, strTemplate),
  170. m_countHit(CCounterLocation::eIdTopicHit, strTemplate),
  171. m_bInited( false )
  172. {
  173. m_hev = ::CreateEvent(
  174. NULL,
  175. TRUE, // any number of (working) threads may be released on signal
  176. FALSE, // initially non-signalled
  177. NULL);
  178. if (! m_hev)
  179. {
  180. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  181. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  182. SrcLoc.GetSrcFileLineStr(),
  183. _T(""),
  184. _T(""),
  185. EV_GTS_ERROR_EVENT );
  186. // Simulate a bad alloc exception in this case.
  187. // This exception will be caught by the caller if the new call has been
  188. // properly wrapped in a try...catch() block.
  189. throw bad_alloc();
  190. }
  191. m_countEvent.Increment();
  192. }
  193. CTemplateInCatalog::~CTemplateInCatalog()
  194. {
  195. if (m_hev)
  196. ::CloseHandle(m_hev);
  197. }
  198. const CString & CTemplateInCatalog::GetTemplateInfo() const
  199. {
  200. return m_strTemplate;
  201. }
  202. // Just let this object know to increment the hit count
  203. void CTemplateInCatalog::CountHit( bool bNewCookie )
  204. {
  205. m_countHit.Increment();
  206. }
  207. // Obtain a CP_TEMPLATE as a pointer to the template, if that template is already built.
  208. // As long as a CP_TEMPLATE remains undeleted, the associated CAPGTSHTIReader is guaranteed to
  209. // remain undeleted.
  210. // Warning: this function will return with a null template if template is not yet built.
  211. // Must test for null with CP_TEMPLATE::IsNull(). Can't test a smart pointer for null
  212. // with ==.
  213. CP_TEMPLATE & CTemplateInCatalog::GetTemplateNoWait( CP_TEMPLATE &cpTemplate ) const
  214. {
  215. cpTemplate= m_cpTemplate;
  216. return cpTemplate;
  217. }
  218. // Obtain a CP_TEMPLATE as a pointer to the template.
  219. // Wait as necessary for that template to be built.
  220. // Warning: this function will return with a null template if template cannot be built.
  221. // As long as a CP_TEMPLATE remains undeleted, the associated CAPGTSHTIReader is guaranteed to
  222. // remain undeleted.
  223. // Warning: this function may have to wait for TopicInCatalog.m_cpTemplate to be built.
  224. CP_TEMPLATE & CTemplateInCatalog::GetTemplate( CP_TEMPLATE &cpTemplate ) const
  225. {
  226. if (!m_bInited)
  227. {
  228. // Wait for a set period, if failure then log error msg and wait infinite.
  229. WAIT_INFINITE( m_hev );
  230. }
  231. return GetTemplateNoWait( cpTemplate );
  232. }
  233. // to be called by the TopicBuilderTask thread
  234. void CTemplateInCatalog::Init( const CAPGTSHTIReader* pTemplate )
  235. {
  236. m_countLoad.Increment();
  237. if (pTemplate)
  238. {
  239. m_cpTemplate= pTemplate;
  240. m_countLoadOK.Increment();
  241. }
  242. if (pTemplate || m_cpTemplate.IsNull())
  243. m_bInited = true;
  244. ::SetEvent(m_hev);
  245. }
  246. // Just let this object know to increment the count of changes detected.
  247. void CTemplateInCatalog::CountChange()
  248. {
  249. m_countEvent.Increment();
  250. }
  251. // Just let this object know to increment the count of failures detected.
  252. void CTemplateInCatalog::CountFailed()
  253. {
  254. // The load failed so increment the count of attempted loads.
  255. m_countLoad.Increment();
  256. }
  257. CTemplateInCatalog::TemplateStatus CTemplateInCatalog::GetTemplateStatus() const
  258. {
  259. if (!m_bInited)
  260. return eNotInited;
  261. else if(m_cpTemplate.IsNull())
  262. return eFail;
  263. else
  264. return eOK;
  265. }
  266. DWORD CTemplateInCatalog::CountOfFailedLoads() const
  267. {
  268. return( m_countLoad.GetTotal() - m_countLoadOK.GetTotal() );
  269. }
  270. /////////////////////////////////////////////////////////////////////
  271. // CTopicShop::CTopicBuildQueue
  272. // This class does the bulk of its work on a separate thread.
  273. // The thread is created in the constructor by starting static function
  274. // CTopicShop::CTopicBuildQueue::TopicBuilderTask.
  275. // That function, in turn does its work by calling private members of this class that
  276. // are specific to use on the TopicBuilderTask thread.
  277. // When this goes out of scope, its own destructor calls ShutDown to stop the thread,
  278. // waits for the thread to shut.
  279. // The following method is available for other threads communicating with that thread:
  280. // CTopicShop::CTopicBuildQueue::RequestBuild
  281. //////////////////////////////////////////////////////////////////////
  282. CTopicShop::CTopicBuildQueue::CTopicBuildQueue( CTopicCatalog & TopicCatalog,
  283. CTemplateCatalog & TemplateCatalog)
  284. : m_TopicCatalog (TopicCatalog),
  285. m_TemplateCatalog( TemplateCatalog ),
  286. m_eCurrentlyBuilding(eUnknown),
  287. m_bShuttingDown (false),
  288. m_dwErr(0),
  289. m_ThreadStatus(eBeforeInit),
  290. m_time(0)
  291. {
  292. enum {eHevBuildReq, eHevShut, eThread, eOK} Progress = eHevBuildReq;
  293. SetThreadStatus(eBeforeInit);
  294. m_hevBuildRequested = ::CreateEvent(
  295. NULL,
  296. FALSE, // release one thread (the TopicBuilderTask) on signal
  297. FALSE, // initially non-signalled
  298. NULL);
  299. if (m_hevBuildRequested)
  300. {
  301. Progress = eHevShut;
  302. m_hevThreadIsShut = ::CreateEvent(
  303. NULL,
  304. FALSE, // release one thread (this one) on signal
  305. FALSE, // initially non-signalled
  306. NULL);
  307. if (m_hevThreadIsShut)
  308. {
  309. Progress = eThread;
  310. DWORD dwThreadID; // No need to hold onto dwThreadID in member variable.
  311. // All Win32 functions take the handle m_hThread instead.
  312. // The one reason you'd ever want to know this ID is for
  313. // debugging
  314. // Note that there is no corresponding ::CloseHandle(m_hThread).
  315. // That is because the thread goes out of existence on the implicit
  316. // ::ExitThread() when TopicBuilderTask returns. See documentation of
  317. // ::CreateThread for further details JM 10/22/98
  318. m_hThread = ::CreateThread( NULL,
  319. 0,
  320. (LPTHREAD_START_ROUTINE)TopicBuilderTask,
  321. this,
  322. 0,
  323. &dwThreadID);
  324. if (m_hThread)
  325. Progress = eOK;
  326. }
  327. }
  328. if (Progress != eOK)
  329. {
  330. m_dwErr = GetLastError();
  331. CString str;
  332. str.Format(_T("%d"), m_dwErr);
  333. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  334. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  335. SrcLoc.GetSrcFileLineStr(),
  336. (Progress == eHevBuildReq) ?_T("Can't create \"request build\" event")
  337. : (Progress == eHevShut) ? _T("Can't create \"shut\" event")
  338. : _T("Can't create thread"),
  339. str,
  340. EV_GTS_ERROR_TOPICBUILDERTHREAD );
  341. SetThreadStatus(eFail);
  342. if (m_hevBuildRequested)
  343. ::CloseHandle(m_hevBuildRequested);
  344. if (m_hevThreadIsShut)
  345. ::CloseHandle(m_hevThreadIsShut);
  346. }
  347. }
  348. CTopicShop::CTopicBuildQueue::~CTopicBuildQueue()
  349. {
  350. ShutDown();
  351. if (m_hevBuildRequested)
  352. ::CloseHandle(m_hevBuildRequested);
  353. if (m_hevThreadIsShut)
  354. ::CloseHandle(m_hevThreadIsShut);
  355. }
  356. void CTopicShop::CTopicBuildQueue::SetThreadStatus(ThreadStatus ts)
  357. {
  358. LOCKOBJECT();
  359. m_ThreadStatus = ts;
  360. time(&m_time);
  361. UNLOCKOBJECT();
  362. }
  363. DWORD CTopicShop::CTopicBuildQueue::GetStatus(ThreadStatus &ts, DWORD & seconds) const
  364. {
  365. time_t timeNow;
  366. LOCKOBJECT();
  367. ts = m_ThreadStatus;
  368. time(&timeNow);
  369. seconds = timeNow - m_time;
  370. UNLOCKOBJECT();
  371. return m_dwErr;
  372. }
  373. // report status of topics in m_TopicCatalog
  374. // OUTPUT Total: number of topics
  375. // OUTPUT NoInit: number of uninitialized topics (never built)
  376. // OUTPUT Fail: number of topics we tried to build, but could never build
  377. // INPUT parrstrFail NULL == don't care to get this output
  378. // OUTPUT *parrstrFail: names of the topics that couldn't be built
  379. void CTopicShop::CTopicBuildQueue::GetTopicsStatus(
  380. DWORD &Total, DWORD &NoInit, DWORD &Fail, vector<CString>*parrstrFail) const
  381. {
  382. LOCKOBJECT();
  383. Total = m_TopicCatalog.size();
  384. NoInit = 0;
  385. Fail = 0;
  386. if (parrstrFail)
  387. parrstrFail->clear();
  388. for (CTopicCatalog::const_iterator it = m_TopicCatalog.begin(); it != m_TopicCatalog.end(); ++it)
  389. {
  390. CTopicInCatalog::TopicStatus status = it->second->GetTopicStatus();
  391. switch (status)
  392. {
  393. case CTopicInCatalog::eNotInited:
  394. ++NoInit;
  395. break;
  396. case CTopicInCatalog::eFail:
  397. ++Fail;
  398. if (parrstrFail)
  399. {
  400. try
  401. {
  402. parrstrFail->push_back(it->second->GetTopicInfo().GetNetworkName());
  403. }
  404. catch (exception& x)
  405. {
  406. CString str;
  407. // Note STL exception in event log.
  408. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  409. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  410. SrcLoc.GetSrcFileLineStr(),
  411. CCharConversion::ConvertACharToString(x.what(), str),
  412. _T(""),
  413. EV_GTS_STL_EXCEPTION );
  414. }
  415. }
  416. break;
  417. default:
  418. break;
  419. }
  420. }
  421. UNLOCKOBJECT();
  422. }
  423. // report status of template in m_TemplateCatalog
  424. // INPUT parrstrFail NULL == don't care to get this output
  425. // OUTPUT *parrstrFail: names of the topics that couldn't be built
  426. // INPUT parrcntFail NULL == don't care to get this output
  427. // OUTPUT *parrcntFail: count of failures of the topics that couldn't be built.
  428. // one to one correspondence with parrstrFail.
  429. void CTopicShop::CTopicBuildQueue::GetTemplatesStatus(
  430. vector<CString>*parrstrFail, vector<DWORD>*parrcntFail ) const
  431. {
  432. LOCKOBJECT();
  433. if (parrstrFail)
  434. parrstrFail->clear();
  435. if (parrcntFail)
  436. parrcntFail->clear();
  437. for (CTemplateCatalog::const_iterator it = m_TemplateCatalog.begin(); it != m_TemplateCatalog.end(); ++it)
  438. {
  439. if (it->second->GetTemplateStatus() == CTemplateInCatalog::eFail)
  440. {
  441. if (parrstrFail)
  442. {
  443. // Currently we only care about failures and their related count.
  444. try
  445. {
  446. parrstrFail->push_back(it->second->GetTemplateInfo());
  447. if (parrcntFail)
  448. parrcntFail->push_back( it->second->CountOfFailedLoads() );
  449. }
  450. catch (exception& x)
  451. {
  452. CString str;
  453. // Note STL exception in event log.
  454. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  455. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  456. SrcLoc.GetSrcFileLineStr(),
  457. CCharConversion::ConvertACharToString(x.what(), str),
  458. _T(""),
  459. EV_GTS_STL_EXCEPTION );
  460. }
  461. }
  462. }
  463. }
  464. UNLOCKOBJECT();
  465. }
  466. // For use by this class and derived classes destructors.
  467. void CTopicShop::CTopicBuildQueue::ShutDown()
  468. {
  469. LOCKOBJECT();
  470. if (m_bShuttingDown)
  471. {
  472. // We have already shut down the topic builder thread, simply exit.
  473. UNLOCKOBJECT();
  474. return;
  475. }
  476. m_bShuttingDown = true;
  477. if (m_hThread)
  478. {
  479. DWORD RetVal;
  480. ::SetEvent(m_hevBuildRequested);
  481. UNLOCKOBJECT();
  482. // Wait for a set period, if failure then log error msg and wait infinite.
  483. RetVal= WAIT_INFINITE( m_hevThreadIsShut );
  484. }
  485. else
  486. UNLOCKOBJECT();
  487. }
  488. // For general use (not part of TopicBuilderTask thread) code.
  489. // Ask for a topic to be built (or rebuilt).
  490. // INPUT strTopic - name of topic OR HTI TEMPLATE
  491. // INPUT bPriority - If bPriority is true, move it ahead of any topics/templates
  492. // for which this has not been called with bPriority true. At a gien priority level,
  493. // toics always come before templates.
  494. // INPUT eCat - indicates whether strTopic is a topic or an HTI template
  495. // This is an asynchronous request that will eventually be fulfilled by TopicBuilderTask thread
  496. void CTopicShop::CTopicBuildQueue::RequestBuild(const CString &strTopic, bool bPriority,
  497. CatalogCategory eCat )
  498. {
  499. // Verify that this is a valid category.
  500. if (eCat != eTopic && eCat != eTemplate)
  501. return;
  502. // Make a lower-case version of the topic name.
  503. CString strTopicLC = strTopic;
  504. strTopicLC.MakeLower();
  505. vector<CString> & Priority = (eCat == eTopic) ?
  506. m_PriorityBuild :
  507. m_PriorityBuildTemplates;
  508. vector<CString> & NonPriority = (eCat == eTopic) ?
  509. m_NonPriorityBuild :
  510. m_NonPriorityBuildTemplates;
  511. LOCKOBJECT();
  512. if ((strTopicLC != m_CurrentlyBuilding) || (eCat != m_eCurrentlyBuilding))
  513. {
  514. vector<CString>::iterator it = find(Priority.begin(), Priority.end(), strTopicLC);
  515. if (it == Priority.end())
  516. {
  517. try
  518. {
  519. it = find(NonPriority.begin(), NonPriority.end(), strTopicLC);
  520. if (bPriority)
  521. {
  522. if (it != NonPriority.end())
  523. {
  524. // it's in the non-priority list. Get it out of there.
  525. NonPriority.erase(it);
  526. }
  527. // Add it to the priority list
  528. Priority.push_back(strTopicLC);
  529. }
  530. else if (it == NonPriority.end())
  531. {
  532. // Add it to the non-priority list
  533. NonPriority.push_back(strTopicLC);
  534. }
  535. // else it's already listed
  536. }
  537. catch (exception& x)
  538. {
  539. CString str;
  540. // Note STL exception in event log.
  541. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  542. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  543. SrcLoc.GetSrcFileLineStr(),
  544. CCharConversion::ConvertACharToString(x.what(), str),
  545. _T(""),
  546. EV_GTS_STL_EXCEPTION );
  547. }
  548. }
  549. // else it's already a priority, we can't do more
  550. }
  551. // else it's already building, we can't do more
  552. ::SetEvent(m_hevBuildRequested);
  553. UNLOCKOBJECT();
  554. }
  555. // For use by the TopicBuilderTask thread. Should only be called when nothing is
  556. // currently building. Caller is responsible to build only one at a time.
  557. // OUTPUT strTopic - name of topic OR HTI TEMPLATE
  558. // OUTPUT eCat - indicates whether strTopic is a topic or an HTI template
  559. // false return indicates invalid request.
  560. // non-empty string output strTopic indicates what is currently building
  561. // empty string output should never happen
  562. // true return indicates valid request:
  563. // non-empty string output strTopic indicates what to build
  564. // empty string output strTopic indicates nothing more to build
  565. // Note that this function has the side effect of changing the _thread_ priority.
  566. bool CTopicShop::CTopicBuildQueue::GetNextToBuild( CString &strTopic, CatalogCategory &eCat )
  567. {
  568. vector<CString>::iterator it;
  569. LOCKOBJECT();
  570. bool bOK = m_CurrentlyBuilding.IsEmpty();
  571. if (bOK)
  572. {
  573. if (!m_PriorityBuild.empty())
  574. {
  575. // We have priority topics to build.
  576. it = m_PriorityBuild.begin();
  577. m_CurrentlyBuilding = *it;
  578. m_eCurrentlyBuilding= eTopic;
  579. m_PriorityBuild.erase(it);
  580. // If there are more priority builds waiting behind this, boost priority
  581. // above normal so we get to them ASAP. Otherwise, normal priority.
  582. ::SetThreadPriority(GetCurrentThread(),
  583. m_PriorityBuild.empty() ? THREAD_PRIORITY_NORMAL : THREAD_PRIORITY_ABOVE_NORMAL);
  584. }
  585. else if (!m_PriorityBuildTemplates.empty())
  586. {
  587. // We have priority alternate templates to build.
  588. it = m_PriorityBuildTemplates.begin();
  589. m_CurrentlyBuilding = *it;
  590. m_eCurrentlyBuilding= eTemplate;
  591. m_PriorityBuildTemplates.erase(it);
  592. // If there are more priority builds waiting behind this, boost priority
  593. // above normal so we get to them ASAP. Otherwise, normal priority.
  594. ::SetThreadPriority(GetCurrentThread(),
  595. m_PriorityBuildTemplates.empty() ? THREAD_PRIORITY_NORMAL : THREAD_PRIORITY_ABOVE_NORMAL);
  596. }
  597. else if (!m_NonPriorityBuild.empty())
  598. {
  599. // We have non-priority topics to build.
  600. it = m_NonPriorityBuild.begin();
  601. m_CurrentlyBuilding = *it;
  602. m_eCurrentlyBuilding= eTopic;
  603. m_NonPriorityBuild.erase(it);
  604. // This is initialization, no one is in a hurry for it,
  605. // let's not burden the system unduly.
  606. ::SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_BELOW_NORMAL);
  607. }
  608. else if (!m_NonPriorityBuildTemplates.empty())
  609. {
  610. // We have non-priority alternate templates to build.
  611. it = m_NonPriorityBuildTemplates.begin();
  612. m_CurrentlyBuilding = *it;
  613. m_eCurrentlyBuilding= eTemplate;
  614. m_NonPriorityBuildTemplates.erase(it);
  615. // This is initialization, no one is in a hurry for it,
  616. // let's not burden the system unduly.
  617. ::SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_BELOW_NORMAL);
  618. }
  619. else
  620. ::SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);
  621. }
  622. strTopic = m_CurrentlyBuilding;
  623. eCat= m_eCurrentlyBuilding;
  624. UNLOCKOBJECT();
  625. return bOK;
  626. }
  627. // Acknowledge that we have finished building the topic previously obtained with GetNextToBuild
  628. // This should be called before GetNextToBuild is called again.
  629. void CTopicShop::CTopicBuildQueue::BuildComplete()
  630. {
  631. LOCKOBJECT();
  632. m_CurrentlyBuilding = _T("");
  633. m_eCurrentlyBuilding= eUnknown;
  634. UNLOCKOBJECT();
  635. }
  636. // For use by the TopicBuilderTask thread.
  637. // Must be called on TopicBuilderTask thread. Handles all work of building & publishing
  638. // topics driven by the queue contents
  639. void CTopicShop::CTopicBuildQueue::Build()
  640. {
  641. CString strTopic;
  642. CatalogCategory eCat;
  643. while (true)
  644. {
  645. LOCKOBJECT();
  646. SetThreadStatus(eRun);
  647. if (m_bShuttingDown)
  648. {
  649. UNLOCKOBJECT();
  650. break;
  651. }
  652. GetNextToBuild( strTopic, eCat );
  653. if (strTopic.IsEmpty())
  654. {
  655. ::ResetEvent(m_hevBuildRequested);
  656. UNLOCKOBJECT();
  657. SetThreadStatus(eWait);
  658. ::WaitForSingleObject(m_hevBuildRequested, INFINITE);
  659. continue;
  660. }
  661. else
  662. UNLOCKOBJECT();
  663. if (eCat == eTopic)
  664. {
  665. // at this point we have a topic name. Get access to topic info.
  666. CTopicCatalog::const_iterator it = m_TopicCatalog.find(strTopic);
  667. if (it == m_TopicCatalog.end())
  668. {
  669. // Asked to initialize a topic that doesn't have a catalog entry.
  670. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  671. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  672. SrcLoc.GetSrcFileLineStr(),
  673. _T("Asked to build"),
  674. strTopic,
  675. EV_GTS_UNRECOGNIZED_TOPIC );
  676. }
  677. else
  678. {
  679. CTopicInCatalog & TopicInCatalog = *(it->second);
  680. const CTopicInfo topicinfo (TopicInCatalog.GetTopicInfo());
  681. try
  682. {
  683. // must create this with new so we can manage it under a reference count regime
  684. CTopic *ptopic = new CTopic (topicinfo.GetDscFilePath()
  685. ,topicinfo.GetHtiFilePath()
  686. ,topicinfo.GetBesFilePath()
  687. ,topicinfo.GetTscFilePath() );
  688. if (ptopic->Read())
  689. TopicInCatalog.Init(ptopic);
  690. else
  691. {
  692. // Release memory.
  693. delete ptopic;
  694. TopicInCatalog.Init(NULL);
  695. }
  696. TopicInCatalog.TopicInfoIsCurrent();
  697. }
  698. catch (bad_alloc&)
  699. {
  700. // Note memory failure in event log.
  701. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  702. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  703. SrcLoc.GetSrcFileLineStr(),
  704. _T(""), _T(""), EV_GTS_CANT_ALLOC );
  705. }
  706. }
  707. }
  708. else if (eCat == eTemplate)
  709. {
  710. // Determine whether the passed in template is in the catalog.
  711. CTemplateCatalog::const_iterator it = m_TemplateCatalog.find(strTopic);
  712. if (it == m_TemplateCatalog.end())
  713. {
  714. // Asked to initialize a template that doesn't have a catalog entry.
  715. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  716. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  717. SrcLoc.GetSrcFileLineStr(),
  718. _T("Asked to build"),
  719. strTopic,
  720. EV_GTS_UNRECOGNIZED_TEMPLATE );
  721. }
  722. else
  723. {
  724. CTemplateInCatalog & TemplateInCatalog = *(it->second);
  725. const CString & strTemplateName = TemplateInCatalog.GetTemplateInfo();
  726. try
  727. {
  728. // must create this with new so we can manage it under a reference count regime
  729. CAPGTSHTIReader *pTemplate;
  730. pTemplate= new CAPGTSHTIReader( CPhysicalFileReader::makeReader( strTemplateName ) );
  731. if (pTemplate->Read())
  732. TemplateInCatalog.Init( pTemplate );
  733. else
  734. {
  735. // Release memory.
  736. delete pTemplate;
  737. TemplateInCatalog.Init( NULL );
  738. }
  739. }
  740. catch (bad_alloc&)
  741. {
  742. // Note memory failure in event log.
  743. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  744. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  745. SrcLoc.GetSrcFileLineStr(),
  746. _T(""), _T(""), EV_GTS_CANT_ALLOC );
  747. }
  748. }
  749. }
  750. BuildComplete();
  751. }
  752. SetThreadStatus(eExiting);
  753. }
  754. // For use by the TopicBuilderTask thread.
  755. void CTopicShop::CTopicBuildQueue::AckShutDown()
  756. {
  757. LOCKOBJECT();
  758. ::SetEvent(m_hevThreadIsShut);
  759. UNLOCKOBJECT();
  760. }
  761. // Main routine of a thread responsible for building and publishing CTopic objects.
  762. // INPUT lpParams
  763. // Always returns 0.
  764. /* static */ UINT WINAPI CTopicShop::CTopicBuildQueue::TopicBuilderTask(LPVOID lpParams)
  765. {
  766. reinterpret_cast<CTopicBuildQueue*>(lpParams)->Build();
  767. reinterpret_cast<CTopicBuildQueue*>(lpParams)->AckShutDown();
  768. return 0;
  769. }
  770. //////////////////////////////////////////////////////////////////////
  771. // CTopicShop::ThreadStatus
  772. //////////////////////////////////////////////////////////////////////
  773. /* static */ CString CTopicShop::ThreadStatusText(ThreadStatus ts)
  774. {
  775. switch(ts)
  776. {
  777. case eBeforeInit: return _T("Before Init");
  778. case eFail: return _T("Fail");
  779. case eWait: return _T("Wait");
  780. case eRun: return _T("Run");
  781. case eExiting: return _T("Exiting");
  782. default: return _T("");
  783. }
  784. }
  785. //////////////////////////////////////////////////////////////////////
  786. // CTopicShop
  787. // The only functions which need to lock this class are those which modify TopicCatalog.
  788. // TopicBuildQueue has its own protection.
  789. //////////////////////////////////////////////////////////////////////
  790. CTopicShop::CTopicShop() :
  791. m_TopicBuildQueue( m_TopicCatalog, m_TemplateCatalog ),
  792. m_hevShopIsOpen(NULL)
  793. {
  794. m_hevShopIsOpen = ::CreateEvent(
  795. NULL,
  796. TRUE, // any number of (working) threads may be released on signal
  797. FALSE, // initially non-signalled
  798. NULL);
  799. if (! m_hevShopIsOpen)
  800. {
  801. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  802. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  803. SrcLoc.GetSrcFileLineStr(),
  804. _T(""),
  805. _T(""),
  806. EV_GTS_ERROR_EVENT );
  807. // Simulate a bad alloc exception in this case.
  808. // This constructor is only called within the ctor of CDBLoadConfiguration
  809. // and the allocation of that object is wrapped within a try...catch() block.
  810. throw bad_alloc();
  811. }
  812. }
  813. CTopicShop::~CTopicShop()
  814. {
  815. // Terminate the topic builder thread prior to cleaning up the topics.
  816. m_TopicBuildQueue.ShutDown();
  817. if (m_hevShopIsOpen)
  818. ::CloseHandle(m_hevShopIsOpen);
  819. // Clean up the topics.
  820. for (CTopicCatalog::const_iterator it = m_TopicCatalog.begin(); it != m_TopicCatalog.end(); ++it)
  821. {
  822. delete it->second;
  823. }
  824. // Clean up the templates.
  825. for (CTemplateCatalog::const_iterator itu = m_TemplateCatalog.begin(); itu != m_TemplateCatalog.end(); ++itu)
  826. {
  827. delete itu->second;
  828. }
  829. }
  830. // Add a topic to the catalog. It must eventually be built by TopicBuilderTask thread.
  831. // If topic is already in list identically, no effect.
  832. void CTopicShop::AddTopic(const CTopicInfo & topicinfo)
  833. {
  834. // our keys into the catalog should be all lower case. This code is fine, because
  835. // CTopicInfo::GetNetworkName() is guaranteed to return lower case.
  836. CString strNetworkName = topicinfo.GetNetworkName();
  837. LOCKOBJECT();
  838. CTopicCatalog::const_iterator it = m_TopicCatalog.find(strNetworkName);
  839. if (it == m_TopicCatalog.end())
  840. {
  841. try
  842. {
  843. m_TopicCatalog[strNetworkName] = new CTopicInCatalog(topicinfo);
  844. }
  845. catch (bad_alloc&)
  846. {
  847. // Note memory failure in event log.
  848. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  849. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  850. SrcLoc.GetSrcFileLineStr(),
  851. _T(""), _T(""), EV_GTS_CANT_ALLOC );
  852. }
  853. }
  854. else if (! (topicinfo == it->second->GetTopicInfo()))
  855. {
  856. it->second->SetTopicInfo(topicinfo);
  857. m_TopicBuildQueue.RequestBuild(strNetworkName, false, CTopicBuildQueue::eTopic);
  858. }
  859. UNLOCKOBJECT();
  860. }
  861. // Add a template to the catalog. It must eventually be built by TopicBuilderTask thread.
  862. // If template is already in list, no effect.
  863. void CTopicShop::AddTemplate( const CString & strTemplateName )
  864. {
  865. LOCKOBJECT();
  866. if (m_TemplateCatalog.find( strTemplateName ) == m_TemplateCatalog.end())
  867. {
  868. try
  869. {
  870. m_TemplateCatalog[ strTemplateName ] = new CTemplateInCatalog( strTemplateName );
  871. }
  872. catch (bad_alloc&)
  873. {
  874. // Note memory failure in event log.
  875. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  876. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  877. SrcLoc.GetSrcFileLineStr(),
  878. _T(""), _T(""), EV_GTS_CANT_ALLOC );
  879. }
  880. }
  881. UNLOCKOBJECT();
  882. }
  883. // if shop is not already open, open it.
  884. void CTopicShop::OpenShop()
  885. {
  886. ::SetEvent(m_hevShopIsOpen);
  887. }
  888. // Request that a topic be built (or rebuilt)
  889. // Typically called in response to either the system detecting a change to the topic
  890. // files or from an operator saying "act as if a change has been detected".
  891. // INPUT strTopic names the topic to build.
  892. // if pbAlreadyInCatalog is input non-null, then *pbAlreadyInCatalog returns whether
  893. // the topic was already known to the system.
  894. void CTopicShop::BuildTopic(const CString & strTopic, bool *pbAlreadyInCatalog /*= NULL*/)
  895. {
  896. if (pbAlreadyInCatalog)
  897. *pbAlreadyInCatalog = false; // initialize
  898. CTopicInCatalog * pTopic = GetCatalogEntryPtr(strTopic);
  899. if (pTopic)
  900. {
  901. pTopic->CountChange();
  902. if (pbAlreadyInCatalog)
  903. *pbAlreadyInCatalog = true;
  904. }
  905. m_TopicBuildQueue.RequestBuild( strTopic, false, CTopicBuildQueue::eTopic );
  906. }
  907. // Request that a template be built (or rebuilt)
  908. // Typically called in response to the system detecting a change to the template files.
  909. void CTopicShop::BuildTemplate( const CString & strTemplate )
  910. {
  911. CTemplateInCatalog * pTemplate = GetTemplateCatalogEntryPtr( strTemplate );
  912. if (pTemplate)
  913. pTemplate->CountChange();
  914. m_TopicBuildQueue.RequestBuild( strTemplate, false, CTopicBuildQueue::eTemplate );
  915. }
  916. CTopicInCatalog * CTopicShop::GetCatalogEntryPtr(const CString & strTopic) const
  917. {
  918. // Wait for a set period, if failure then log error msg and wait infinite.
  919. WAIT_INFINITE( m_hevShopIsOpen );
  920. CTopicCatalog::const_iterator it= m_TopicCatalog.find(strTopic);
  921. if (it == m_TopicCatalog.end())
  922. return NULL;
  923. else
  924. return it->second;
  925. }
  926. CTemplateInCatalog * CTopicShop::GetTemplateCatalogEntryPtr(const CString & strTemplate ) const
  927. {
  928. // Wait for a set period, if failure then log error msg and wait infinite.
  929. WAIT_INFINITE( m_hevShopIsOpen );
  930. CTemplateCatalog::const_iterator it= m_TemplateCatalog.find( strTemplate );
  931. if (it == m_TemplateCatalog.end())
  932. return NULL;
  933. else
  934. return it->second;
  935. }
  936. // Call this function to obtain a CP_TOPIC as a pointer to the topic (identified by
  937. // strTopic) that you want to operate on. As long as the CP_TOPIC remains undeleted,
  938. // the associated CTopic is guaranteed to remain undeleted.
  939. // this function must not lock CTopicShop, because it can wait a long time.
  940. CP_TOPIC & CTopicShop::GetTopic(const CString & strTopic, CP_TOPIC &cpTopic, bool bNewCookie)
  941. {
  942. CTopicInCatalog *pTopicInCatalog = GetCatalogEntryPtr(strTopic);
  943. if (! pTopicInCatalog)
  944. cpTopic = NULL;
  945. else
  946. {
  947. pTopicInCatalog->CountHit(bNewCookie);
  948. pTopicInCatalog->GetTopicNoWait(cpTopic);
  949. if (cpTopic.IsNull())
  950. {
  951. m_TopicBuildQueue.RequestBuild( strTopic, true, CTopicBuildQueue::eTopic );
  952. pTopicInCatalog->GetTopic(cpTopic);
  953. }
  954. }
  955. return cpTopic;
  956. }
  957. // Call this function to obtain a CP_TEMPLATE as a pointer to the template (identified by
  958. // strTemplate) that you want to operate on. As long as the CP_TEMPLATE remains undeleted,
  959. // the associated CAPGTSHTIReader is guaranteed to remain undeleted.
  960. // this function must not lock CTopicShop, because it can wait a long time.
  961. CP_TEMPLATE & CTopicShop::GetTemplate(const CString & strTemplate, CP_TEMPLATE &cpTemplate, bool bNewCookie)
  962. {
  963. CTemplateInCatalog *pTemplateInCatalog = GetTemplateCatalogEntryPtr(strTemplate);
  964. if (! pTemplateInCatalog)
  965. cpTemplate = NULL;
  966. else
  967. {
  968. pTemplateInCatalog->CountHit(bNewCookie);
  969. pTemplateInCatalog->GetTemplateNoWait( cpTemplate );
  970. if (cpTemplate.IsNull())
  971. {
  972. m_TopicBuildQueue.RequestBuild( strTemplate, true, CTopicBuildQueue::eTemplate );
  973. pTemplateInCatalog->GetTemplate( cpTemplate );
  974. }
  975. }
  976. return cpTemplate;
  977. }
  978. void CTopicShop::GetListOfTopicNames(vector<CString>&arrstrTopic) const
  979. {
  980. arrstrTopic.clear();
  981. LOCKOBJECT();
  982. try
  983. {
  984. for (CTopicCatalog::const_iterator it = m_TopicCatalog.begin(); it != m_TopicCatalog.end(); ++it)
  985. {
  986. arrstrTopic.push_back(it->second->GetTopicInfo().GetNetworkName());
  987. }
  988. }
  989. catch (exception& x)
  990. {
  991. CString str;
  992. // Note STL exception in event log.
  993. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  994. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  995. SrcLoc.GetSrcFileLineStr(),
  996. CCharConversion::ConvertACharToString(x.what(), str),
  997. _T(""),
  998. EV_GTS_STL_EXCEPTION );
  999. }
  1000. UNLOCKOBJECT();
  1001. }
  1002. // Rebuild all topics from source files
  1003. void CTopicShop::RebuildAll()
  1004. {
  1005. LOCKOBJECT();
  1006. for (CTopicCatalog::const_iterator it = m_TopicCatalog.begin(); it != m_TopicCatalog.end(); ++it)
  1007. {
  1008. BuildTopic(it->second->GetTopicInfo().GetNetworkName());
  1009. }
  1010. for (CTemplateCatalog::const_iterator itu = m_TemplateCatalog.begin(); itu != m_TemplateCatalog.end(); ++itu)
  1011. {
  1012. BuildTemplate( itu->first );
  1013. }
  1014. UNLOCKOBJECT();
  1015. }
  1016. // Get status information on the topic builder thread
  1017. DWORD CTopicShop::GetThreadStatus(ThreadStatus &ts, DWORD & seconds) const
  1018. {
  1019. return m_TopicBuildQueue.GetStatus(ts, seconds);
  1020. }
  1021. // see CTopicShop::CTopicBuildQueue::GetTopicsStatus for documentation.
  1022. void CTopicShop::GetTopicsStatus(
  1023. DWORD &Total, DWORD &NoInit, DWORD &Fail, vector<CString>*parrstrFail) const
  1024. {
  1025. m_TopicBuildQueue.GetTopicsStatus(Total, NoInit, Fail, parrstrFail);
  1026. }
  1027. // see CTopicShop::CTopicBuildQueue::GetTemplatesStatus for documentation.
  1028. void CTopicShop::GetTemplatesStatus( vector<CString>*parrstrFail, vector<DWORD>*parrcntFail ) const
  1029. {
  1030. m_TopicBuildQueue.GetTemplatesStatus( parrstrFail, parrcntFail);
  1031. }
  1032. CTopicInCatalog* CTopicShop::GetCatalogEntry(const CString& strTopic) const
  1033. {
  1034. CTopicInCatalog* ret = NULL;
  1035. LOCKOBJECT();
  1036. CTopicCatalog::const_iterator it = m_TopicCatalog.find(strTopic);
  1037. if (it != m_TopicCatalog.end())
  1038. ret = it->second;
  1039. UNLOCKOBJECT();
  1040. return ret;
  1041. }
  1042. bool CTopicShop::RetTemplateInCatalogStatus( const CString& strTemplate, bool& bValid ) const
  1043. {
  1044. bool bIsPresent= false;
  1045. bValid= false;
  1046. LOCKOBJECT();
  1047. CTemplateCatalog::const_iterator it = m_TemplateCatalog.find( strTemplate );
  1048. if (it != m_TemplateCatalog.end())
  1049. {
  1050. CTemplateInCatalog* pTmp;
  1051. bIsPresent= true;
  1052. pTmp= it->second;
  1053. switch (pTmp->GetTemplateStatus())
  1054. {
  1055. case CTemplateInCatalog::eOK:
  1056. bValid= true;
  1057. break;
  1058. case CTemplateInCatalog::eFail:
  1059. // Template has failed to load so we will not try to reload it,
  1060. // but we need to increment the attempted load counter.
  1061. pTmp->CountFailed();
  1062. break;
  1063. default: ;
  1064. }
  1065. }
  1066. UNLOCKOBJECT();
  1067. return( bIsPresent );
  1068. }