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.

512 lines
11 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. Abstract:
  5. History:
  6. --*/
  7. #include "precomp.h"
  8. #include <assert.h>
  9. #include <comutl.h>
  10. #include <wbemcli.h>
  11. #include "msgsvc.h"
  12. /**************************************************************************
  13. CMsgServiceRecord - hold sink given to the msg service on Add().
  14. ***************************************************************************/
  15. class CMsgServiceRecord : public OVERLAPPED
  16. {
  17. CCritSec m_cs;
  18. long m_cRefs;
  19. CWbemPtr<IWmiMessageReceiverSink> m_pSink;
  20. public:
  21. CMsgServiceRecord() : m_cRefs(0) { }
  22. void SetSink( IWmiMessageReceiverSink* pSink )
  23. {
  24. CInCritSec ics(&m_cs);
  25. m_pSink = pSink;
  26. }
  27. void AddRef()
  28. {
  29. InterlockedIncrement( &m_cRefs );
  30. }
  31. void Release()
  32. {
  33. if ( InterlockedDecrement( &m_cRefs ) == 0 )
  34. {
  35. delete this;
  36. }
  37. }
  38. HRESULT Receive()
  39. {
  40. CInCritSec ics(&m_cs);
  41. if ( m_pSink == NULL )
  42. {
  43. return WBEM_E_SHUTTING_DOWN;
  44. }
  45. return m_pSink->Receive( this );
  46. }
  47. HRESULT Notify()
  48. {
  49. CInCritSec ics(&m_cs);
  50. if ( m_pSink == NULL )
  51. {
  52. return WBEM_E_SHUTTING_DOWN;
  53. }
  54. return m_pSink->Notify( this );
  55. }
  56. };
  57. /*****************************************************************************
  58. CMsgService
  59. ******************************************************************************/
  60. ULONG CMsgService::SyncServiceFunc( void* pCtx )
  61. {
  62. HRESULT hr;
  63. CMsgServiceRecord* pRecord = (CMsgServiceRecord*)pCtx;
  64. do
  65. {
  66. hr = pRecord->Receive();
  67. } while( SUCCEEDED(hr) );
  68. //
  69. // Since the record will no longer be serviced, give up our ref
  70. // count on it.
  71. //
  72. pRecord->Release();
  73. return hr;
  74. }
  75. ULONG CMsgService::AsyncServiceFunc( void* pCtx )
  76. {
  77. HRESULT hr;
  78. CMsgServiceRecord* pRecord;
  79. CMsgService* pSvc = (CMsgService*)pCtx;
  80. do
  81. {
  82. hr = pSvc->AsyncWaitForCompletion( INFINITE, &pRecord );
  83. if ( FAILED(hr) )
  84. {
  85. //
  86. // exit loop. hr will describe whether it was normal or not.
  87. //
  88. break;
  89. }
  90. if ( hr == S_OK )
  91. {
  92. //
  93. // hr can be S_FALSE as well. this occurrs when the
  94. // first submit is performed. In this case, we don't do
  95. // the notify.
  96. //
  97. hr = pRecord->Notify();
  98. }
  99. if ( SUCCEEDED(hr) )
  100. {
  101. hr = pSvc->AsyncReceive( pRecord );
  102. }
  103. if ( FAILED(hr) )
  104. {
  105. //
  106. // Since the record will no longer be serviced, give up our ref
  107. // count on it.
  108. //
  109. pRecord->Release();
  110. }
  111. } while ( 1 );
  112. return hr;
  113. }
  114. /*********************************************************************
  115. CMsgService
  116. **********************************************************************/
  117. CMsgService::CMsgService( CLifeControl* pControl )
  118. : m_XService( this ), CUnkInternal( pControl ),
  119. m_hThread( INVALID_HANDLE_VALUE ), m_cSvcRefs( 0 ), m_bAsyncInit( FALSE )
  120. {
  121. }
  122. void* CMsgService::GetInterface( REFIID riid )
  123. {
  124. if ( riid == IID_IWmiMessageService )
  125. {
  126. return &m_XService;
  127. }
  128. return NULL;
  129. }
  130. CMsgService::~CMsgService()
  131. {
  132. if ( m_bAsyncInit )
  133. {
  134. //
  135. // wait for async thread to complete. TODO: print error here if
  136. // WaitForSingleObject times out.
  137. //
  138. WaitForSingleObject( m_hThread, 5000 );
  139. CloseHandle( m_hThread );
  140. }
  141. }
  142. HRESULT CMsgService::EnsureService( BOOL bAsync )
  143. {
  144. HRESULT hr;
  145. if ( !bAsync )
  146. {
  147. return S_OK;
  148. }
  149. CInCritSec ics( &m_cs );
  150. if ( m_bAsyncInit )
  151. {
  152. return S_OK;
  153. }
  154. assert( m_hThread == INVALID_HANDLE_VALUE );
  155. //
  156. // must make sure that all async initialization is performed
  157. // before starting the async thread(s).
  158. //
  159. hr = AsyncInitialize();
  160. if ( FAILED(hr) )
  161. {
  162. return hr;
  163. }
  164. m_hThread = CreateThread( NULL,
  165. 0,
  166. AsyncServiceFunc,
  167. this,
  168. 0,
  169. NULL );
  170. if ( m_hThread == INVALID_HANDLE_VALUE )
  171. {
  172. return HRESULT_FROM_WIN32( GetLastError() );
  173. }
  174. m_bAsyncInit = TRUE;
  175. return S_OK;
  176. }
  177. HRESULT CMsgService::Remove( void* pHdl )
  178. {
  179. CMsgServiceRecord* pRecord = (CMsgServiceRecord*)pHdl;
  180. //
  181. // setting the sink to null will ensure that no callbacks
  182. // will occur.
  183. //
  184. pRecord->SetSink( NULL );
  185. //
  186. // the client will not be using the record anymore so release its ref.
  187. //
  188. pRecord->Release();
  189. return S_OK;
  190. }
  191. HRESULT CMsgService::Add( CMsgServiceRecord* pRecord,
  192. HANDLE hFileOverlapped,
  193. DWORD dwFlags )
  194. {
  195. HRESULT hr;
  196. hr = EnsureService( TRUE );
  197. if ( FAILED(hr) )
  198. {
  199. return hr;
  200. }
  201. hr = AsyncAddOverlappedFile( hFileOverlapped, pRecord );
  202. if ( FAILED(hr) )
  203. {
  204. return hr;
  205. }
  206. return WBEM_S_NO_ERROR;
  207. }
  208. HRESULT CMsgService::Add( CMsgServiceRecord* pRec, DWORD dwFlags )
  209. {
  210. HRESULT hr;
  211. hr = EnsureService( FALSE );
  212. HANDLE hThread = CreateThread( NULL, 0, SyncServiceFunc, pRec, 0, NULL );
  213. if ( hThread == INVALID_HANDLE_VALUE )
  214. {
  215. return HRESULT_FROM_WIN32( GetLastError() );
  216. }
  217. return WBEM_S_NO_ERROR;
  218. }
  219. HRESULT CMsgService::XService::Add( IWmiMessageReceiverSink* pSink,
  220. HANDLE* phFileOverlapped,
  221. DWORD dwFlags,
  222. void** ppHdl )
  223. {
  224. ENTER_API_CALL
  225. HRESULT hr;
  226. *ppHdl = NULL;
  227. //
  228. // create the msg service record for this sink.
  229. //
  230. CWbemPtr<CMsgServiceRecord> pRecord = new CMsgServiceRecord;
  231. if ( pRecord == NULL )
  232. {
  233. return WBEM_E_OUT_OF_MEMORY;
  234. }
  235. pRecord->SetSink( pSink );
  236. //
  237. // initialize for async or sync operation
  238. //
  239. if ( phFileOverlapped )
  240. {
  241. hr = m_pObject->Add( pRecord, *phFileOverlapped, dwFlags );
  242. }
  243. else
  244. {
  245. hr = m_pObject->Add( pRecord, dwFlags );
  246. }
  247. if ( FAILED(hr) )
  248. {
  249. return hr;
  250. }
  251. //
  252. // the msg service keeps a ref count now on the record until its sure
  253. // that it is no longer being serviced.
  254. //
  255. pRecord->AddRef();
  256. //
  257. // caller now owns a ref as well. This will be released in Remove().
  258. //
  259. pRecord->AddRef();
  260. *ppHdl = pRecord;
  261. return WBEM_S_NO_ERROR;
  262. EXIT_API_CALL
  263. }
  264. HRESULT CMsgService::XService::Remove( void* pHdl )
  265. {
  266. ENTER_API_CALL
  267. return m_pObject->Remove( pHdl );
  268. EXIT_API_CALL
  269. }
  270. /*************************************************************************
  271. CMessageServiceNT
  272. **************************************************************************/
  273. #define SHUTDOWN_COMPLETION_KEY 0xfffffffe
  274. #define INITRECV_COMPLETION_KEY 0xfffffffd
  275. CMsgServiceNT::CMsgServiceNT( CLifeControl* pControl )
  276. : CMsgService( pControl ), m_hPort( INVALID_HANDLE_VALUE )
  277. {
  278. }
  279. CMsgServiceNT::~CMsgServiceNT()
  280. {
  281. if ( m_hPort != INVALID_HANDLE_VALUE )
  282. {
  283. CloseHandle( m_hPort );
  284. }
  285. }
  286. HRESULT CMsgServiceNT::AsyncAddOverlappedFile( HANDLE hOverlappedFile,
  287. CMsgServiceRecord* pRec )
  288. {
  289. //
  290. // add the file handle that was given to us to the completion port.
  291. // when the receiver closes this file handle, it will be removed from
  292. // the completion port automatically.
  293. //
  294. HANDLE hPort = CreateIoCompletionPort( hOverlappedFile, m_hPort, 0, 0 );
  295. if ( hPort == INVALID_HANDLE_VALUE )
  296. {
  297. return HRESULT_FROM_WIN32( GetLastError() );
  298. }
  299. assert( hPort == m_hPort );
  300. //
  301. // now perform the first receive on the record. We cannot do it on this
  302. // thread because overlapped i/o cancels requests if the thread that
  303. // issued them is brought down before the i/o completes. To work around
  304. // this, we post a request to the completion port and wait for it
  305. // to be received.
  306. //
  307. if ( !PostQueuedCompletionStatus( m_hPort,
  308. 0,
  309. INITRECV_COMPLETION_KEY,
  310. pRec ) )
  311. {
  312. return HRESULT_FROM_WIN32( GetLastError() );
  313. }
  314. return S_OK;
  315. }
  316. //
  317. // assumes already locked.
  318. //
  319. HRESULT CMsgServiceNT::AsyncInitialize()
  320. {
  321. if ( m_hPort != INVALID_HANDLE_VALUE )
  322. {
  323. return S_OK;
  324. }
  325. m_hPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE,
  326. NULL,
  327. NULL,
  328. 0 );
  329. if ( m_hPort == INVALID_HANDLE_VALUE )
  330. {
  331. return HRESULT_FROM_WIN32( GetLastError() );
  332. }
  333. return S_OK;
  334. }
  335. HRESULT CMsgServiceNT::AsyncShutdown( DWORD cThreads )
  336. {
  337. //
  338. // this method has the responsibility breaking the async thread(s) out
  339. // of their svc loop.
  340. //
  341. assert( m_hPort != INVALID_HANDLE_VALUE );
  342. for( DWORD i=0; i < cThreads; i++ )
  343. {
  344. PostQueuedCompletionStatus( m_hPort, 0, SHUTDOWN_COMPLETION_KEY, NULL);
  345. }
  346. return S_OK;
  347. }
  348. HRESULT CMsgServiceNT::AsyncReceive( CMsgServiceRecord* pRecord )
  349. {
  350. ZeroMemory( pRecord, sizeof(OVERLAPPED) );
  351. return pRecord->Receive();
  352. }
  353. HRESULT CMsgServiceNT::AsyncWaitForCompletion( DWORD dwTimeout,
  354. CMsgServiceRecord** ppRecord)
  355. {
  356. BOOL bRes;
  357. ULONG dwBytesTransferred;
  358. ULONG_PTR dwCompletionKey;
  359. LPOVERLAPPED lpOverlapped;
  360. *ppRecord = NULL;
  361. bRes = GetQueuedCompletionStatus( m_hPort,
  362. &dwBytesTransferred,
  363. &dwCompletionKey,
  364. &lpOverlapped,
  365. dwTimeout );
  366. if ( bRes )
  367. {
  368. if ( dwCompletionKey == SHUTDOWN_COMPLETION_KEY )
  369. {
  370. return WBEM_E_SHUTTING_DOWN;
  371. }
  372. }
  373. else if ( lpOverlapped == NULL )
  374. {
  375. //
  376. // usually happens when the operation times out. HR will tell caller
  377. // if this is the case.
  378. //
  379. return HRESULT_FROM_WIN32( GetLastError() );
  380. }
  381. //
  382. // if we're here, then this means that we've sucessfully dequeued a
  383. // completion packet. However, the i/o operation may have failed
  384. // ( bRes is FALSE ). In this case the overlapped structure will
  385. // contain the needed error information.
  386. //
  387. *ppRecord = (CMsgServiceRecord*)lpOverlapped;
  388. //
  389. // we must also handle the case where this is an initial receive
  390. // completion. This happens when a receiver is first added. Since
  391. // we can't issue a receive on the adding thread, we must do it on our
  392. // worker threads. In this case, we return S_FALSE to signal to the
  393. // Async handling routine that there was no prior submit and a notify
  394. // should NOT be formed.
  395. //
  396. return dwCompletionKey != INITRECV_COMPLETION_KEY ? S_OK : S_FALSE;
  397. }