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.

433 lines
11 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: E V E N T Q . C P P
  7. //
  8. // Contents: Event Queue for managing synchonization of external events.
  9. //
  10. // Notes:
  11. //
  12. // Author: ckotze 29 Nov 2000
  13. //
  14. //----------------------------------------------------------------------------
  15. #include "pch.h"
  16. #pragma hdrstop
  17. #include "ncmisc.h"
  18. #include "eventq.h"
  19. #include "conman.h"
  20. //+---------------------------------------------------------------------------
  21. //
  22. // Function: Constructor for CEventQueue
  23. //
  24. // Purpose: Creates the various synchronization objects required for the
  25. // Queue
  26. // Arguments:
  27. // HANDLE hServiceShutdown [in]
  28. // Event to set when shutting down queue.
  29. //
  30. //
  31. // Returns: nothing.
  32. //
  33. // Author: ckotze 30 Nov 2000
  34. //
  35. // Notes:
  36. //
  37. //
  38. //
  39. CEventQueue::CEventQueue(HANDLE hServiceShutdown) : m_hServiceShutdown(0), m_pFireEvents(NULL), m_hWait(0), m_fRefreshAllInQueue(FALSE)
  40. {
  41. TraceFileFunc(ttidEvents);
  42. NTSTATUS Status;
  43. try
  44. {
  45. if (!DuplicateHandle(GetCurrentProcess(), hServiceShutdown, GetCurrentProcess(), &m_hServiceShutdown, NULL, FALSE, DUPLICATE_SAME_ACCESS))
  46. {
  47. TraceTag(ttidEvents, "Couldn't Duplicate handle!");
  48. throw E_OUTOFMEMORY;
  49. }
  50. HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  51. m_pFireEvents = new CEvent(hEvent);
  52. Status = RtlRegisterWait(&m_hWait, hEvent, (WAITORTIMERCALLBACKFUNC) DispatchEvents, NULL, INFINITE, WT_EXECUTEDEFAULT);
  53. if (!NT_SUCCESS(Status))
  54. {
  55. throw E_OUTOFMEMORY;
  56. }
  57. TraceTag(ttidEvents, "RtlRegisterWait Succeeded");
  58. InitializeCriticalSection(&m_csQueue);
  59. }
  60. catch (...)
  61. {
  62. if (m_hWait && NT_SUCCESS(Status))
  63. {
  64. RtlDeregisterWaitEx(m_hWait, INVALID_HANDLE_VALUE);
  65. }
  66. // ISSUE: If CreateEvent succeeds and new CEvent fails, we're not freeing the hEvent.
  67. if (m_pFireEvents)
  68. {
  69. delete m_pFireEvents;
  70. }
  71. if (m_hServiceShutdown)
  72. {
  73. CloseHandle(m_hServiceShutdown);
  74. }
  75. throw;
  76. }
  77. }
  78. //+---------------------------------------------------------------------------
  79. //
  80. // Function: Destructor for CEventQueue
  81. //
  82. // Purpose: Empties the queue and frees all existing items in the queue.
  83. //
  84. // Arguments:
  85. //
  86. //
  87. //
  88. //
  89. // Returns: nothing.
  90. //
  91. // Author: ckotze 30 Nov 2000
  92. //
  93. // Notes:
  94. //
  95. //
  96. //
  97. CEventQueue::~CEventQueue()
  98. {
  99. NTSTATUS Status;
  100. while(!m_eqWorkItems.empty())
  101. {
  102. USERWORKITEM UserWorkItem;
  103. UserWorkItem = m_eqWorkItems.front();
  104. m_eqWorkItems.pop_front();
  105. if (UserWorkItem.EventMgr == EVENTMGR_CONMAN)
  106. {
  107. CONMAN_EVENT* pConmanEvent = reinterpret_cast<CONMAN_EVENT*>(UserWorkItem.Event);
  108. FreeConmanEvent(pConmanEvent);
  109. }
  110. }
  111. DeleteCriticalSection(&m_csQueue);
  112. Status = RtlDeregisterWaitEx(m_hWait, INVALID_HANDLE_VALUE);
  113. delete m_pFireEvents;
  114. CloseHandle(m_hServiceShutdown);
  115. }
  116. //+---------------------------------------------------------------------------
  117. //
  118. // Function: EnqueueEvent
  119. //
  120. // Purpose: Stores the new event in the Event Queue
  121. //
  122. // Arguments:
  123. // Function - The pointer to the function to be called when firing the
  124. // event
  125. // Event - The Event information
  126. // Flags - Unused, just for compatibility with QueueUserWorkItem calls
  127. //
  128. // Returns: HRESULT
  129. // S_OK - Event has been added and Event code is
  130. // already dispatching events
  131. // S_FALSE - Event has been added to Queue, but a
  132. // thread needs to be scheduled to fire
  133. // the events
  134. // E_OUTOFMEMORY - Unable to add the event to the Queue.
  135. //
  136. // Author: ckotze 30 Nov 2000
  137. //
  138. // Notes: Locks and Unlocks the critical section only while working
  139. // with the queue
  140. //
  141. //
  142. HRESULT CEventQueue::EnqueueEvent(IN LPTHREAD_START_ROUTINE Function, IN PVOID pEvent, IN const EVENT_MANAGER EventMgr)
  143. {
  144. TraceFileFunc(ttidEvents);
  145. CExceptionSafeLock esLock(&m_csQueue);
  146. USERWORKITEM UserWorkItem;
  147. HRESULT hr = S_OK;
  148. if (!Function)
  149. {
  150. return E_POINTER;
  151. }
  152. if (!pEvent)
  153. {
  154. return E_POINTER;
  155. }
  156. UserWorkItem.Function = Function;
  157. UserWorkItem.Event = pEvent;
  158. UserWorkItem.EventMgr = EventMgr;
  159. if (EVENTMGR_CONMAN == EventMgr)
  160. {
  161. CONMAN_EVENT* pConmanEvent = reinterpret_cast<CONMAN_EVENT*>(pEvent);
  162. if (REFRESH_ALL == pConmanEvent->Type)
  163. {
  164. if (!m_fRefreshAllInQueue)
  165. {
  166. m_fRefreshAllInQueue = TRUE;
  167. }
  168. else
  169. {
  170. FreeConmanEvent(pConmanEvent);
  171. return S_OK;
  172. }
  173. }
  174. }
  175. #ifdef DBG
  176. char pchErrorText[MAX_PATH];
  177. Assert(UserWorkItem.EventMgr);
  178. if (EVENTMGR_CONMAN == UserWorkItem.EventMgr)
  179. {
  180. CONMAN_EVENT* pConmanEvent = reinterpret_cast<CONMAN_EVENT*>(pEvent);
  181. TraceTag(ttidEvents, "EnqueueEvent received Event: %s (currently %d in queue). Event Manager: CONMAN", DbgEvents(pConmanEvent->Type), m_eqWorkItems.size());
  182. sprintf(pchErrorText, "Invalid Type %d specified in Event structure\r\n", pConmanEvent->Type);
  183. AssertSz(IsValidEventType(UserWorkItem.EventMgr, pConmanEvent->Type), pchErrorText);
  184. }
  185. else
  186. {
  187. sprintf(pchErrorText, "Invalid Event Manager %d specified in Event structure\r\n", EventMgr);
  188. AssertSz(FALSE, pchErrorText);
  189. }
  190. #endif
  191. try
  192. {
  193. m_eqWorkItems.push_back(UserWorkItem);
  194. m_pFireEvents->SetEvent();
  195. }
  196. catch (...)
  197. {
  198. hr = E_OUTOFMEMORY;
  199. }
  200. return hr;
  201. }
  202. //+---------------------------------------------------------------------------
  203. //
  204. // Function: DequeueEvent
  205. //
  206. // Purpose: Retrieves the next event in the Event Queue
  207. //
  208. // Arguments:
  209. // Function - The pointer to the function to be called when firing the
  210. // event
  211. // Event - The Event information
  212. // Flags - Unused, just for compatibility with QueueUserWorkItem calls
  213. //
  214. // Returns: HRESULT
  215. //
  216. // Author: ckotze 30 Nov 2000
  217. //
  218. // Notes: Locks and Unlocks the critical section only while working
  219. // with the queue
  220. //
  221. //
  222. HRESULT CEventQueue::DequeueEvent(OUT LPTHREAD_START_ROUTINE& Function, OUT PVOID& pEvent, OUT EVENT_MANAGER& EventMgr)
  223. {
  224. TraceFileFunc(ttidEvents);
  225. CExceptionSafeLock esLock(&m_csQueue);
  226. USERWORKITEM UserWorkItem;
  227. DWORD dwSize = m_eqWorkItems.size();
  228. if (!dwSize)
  229. {
  230. AssertSz(FALSE, "Calling DequeueEvent with 0 items in Queue!!!");
  231. return E_UNEXPECTED;
  232. }
  233. UserWorkItem = m_eqWorkItems.front();
  234. m_eqWorkItems.pop_front();
  235. Function = UserWorkItem.Function;
  236. pEvent = UserWorkItem.Event;
  237. EventMgr = UserWorkItem.EventMgr;
  238. if (EVENTMGR_CONMAN == EventMgr)
  239. {
  240. CONMAN_EVENT* pConmanEvent = reinterpret_cast<CONMAN_EVENT*>(pEvent);
  241. if (REFRESH_ALL == pConmanEvent->Type)
  242. {
  243. m_fRefreshAllInQueue = FALSE;
  244. }
  245. }
  246. #ifdef DBG
  247. char pchErrorText[MAX_PATH];
  248. Assert(EventMgr);
  249. if (EVENTMGR_CONMAN == EventMgr)
  250. {
  251. CONMAN_EVENT* pConmanEvent = reinterpret_cast<CONMAN_EVENT*>(pEvent);
  252. TraceTag(ttidEvents, "DequeueEvent retrieved Event: %s (%d left in queue). Event Manager: CONMAN", DbgEvents(pConmanEvent->Type), m_eqWorkItems.size());
  253. sprintf(pchErrorText, "Invalid Type %d specified in Event structure\r\nItems in Queue: %d\r\n", pConmanEvent->Type, dwSize);
  254. AssertSz(IsValidEventType(EventMgr, pConmanEvent->Type), pchErrorText);
  255. }
  256. else
  257. {
  258. sprintf(pchErrorText, "Invalid Event Manager %d specified in Event structure\r\n", EventMgr);
  259. AssertSz(FALSE, pchErrorText);
  260. }
  261. #endif
  262. return S_OK;
  263. }
  264. //+---------------------------------------------------------------------------
  265. //
  266. // Function: WaitForExit
  267. //
  268. // Purpose: Waits for the queue to exit.
  269. //
  270. // Arguments:
  271. // (none)
  272. //
  273. // Returns: WAIT_OBJECT_0 or failure code.
  274. //
  275. // Author: ckotze 28 Apr 2001
  276. //
  277. // Notes:
  278. //
  279. //
  280. DWORD CEventQueue::WaitForExit()
  281. {
  282. return WaitForSingleObject(m_hServiceShutdown, INFINITE);
  283. }
  284. //+---------------------------------------------------------------------------
  285. //
  286. // Function: size
  287. //
  288. // Purpose: Returns the Number of items in the queue
  289. //
  290. // Arguments:
  291. // (none)
  292. //
  293. // Returns: Number of items in the queue
  294. //
  295. // Author: ckotze 30 Nov 2000
  296. //
  297. // Notes:
  298. //
  299. //
  300. size_t CEventQueue::size()
  301. {
  302. CExceptionSafeLock esLock(&m_csQueue);
  303. TraceFileFunc(ttidEvents);
  304. size_t tempsize;
  305. tempsize = m_eqWorkItems.size();
  306. return tempsize;
  307. }
  308. //+---------------------------------------------------------------------------
  309. //
  310. // Function: AtomCheckSizeAndResetEvent
  311. //
  312. // Purpose: Make sure we know when we're supposed to exit, lock during the
  313. // operation.
  314. // Arguments:
  315. // (none)
  316. //
  317. // Returns: TRUE if should exit thread. FALSE if more events in queue, or
  318. // service is not shutting down.
  319. // Author: ckotze 04 March 2001
  320. //
  321. // Notes:
  322. //
  323. //
  324. BOOL CEventQueue::AtomCheckSizeAndResetEvent(const BOOL fDispatchEvents)
  325. {
  326. CExceptionSafeLock esLock(&m_csQueue);
  327. BOOL fRet = TRUE;
  328. TraceTag(ttidEvents, "Checking for Exit Conditions, Events in queue: %d, Service Shutting Down: %s", size(), (fDispatchEvents) ? "FALSE" : "TRUE");
  329. if (m_eqWorkItems.empty() || !fDispatchEvents)
  330. {
  331. fRet = FALSE;
  332. if (fDispatchEvents)
  333. {
  334. m_pFireEvents->ResetEvent();
  335. }
  336. else
  337. {
  338. SetEvent(m_hServiceShutdown);
  339. }
  340. }
  341. return fRet;
  342. }
  343. // CEvent is a Hybrid between Automatic and Manual reset events.
  344. // It is automatically reset, but we control when it is set so it
  345. // doesn't spawn threads while set, except for the first one.
  346. CEvent::CEvent(HANDLE hEvent)
  347. {
  348. m_hEvent = hEvent;
  349. m_bSignaled = FALSE;
  350. }
  351. CEvent::~CEvent()
  352. {
  353. CloseHandle(m_hEvent);
  354. }
  355. HRESULT CEvent::SetEvent()
  356. {
  357. HRESULT hr = S_OK;
  358. if (!m_bSignaled)
  359. {
  360. if (!::SetEvent(m_hEvent))
  361. {
  362. hr = HrFromLastWin32Error();
  363. }
  364. else
  365. {
  366. m_bSignaled = TRUE;
  367. }
  368. }
  369. return hr;
  370. }
  371. HRESULT CEvent::ResetEvent()
  372. {
  373. HRESULT hr = S_OK;
  374. Assert(m_bSignaled);
  375. m_bSignaled = FALSE;
  376. return hr;
  377. }