Leaked source code of windows server 2003
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.

514 lines
12 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. CloseHandle( hThread );
  218. return WBEM_S_NO_ERROR;
  219. }
  220. HRESULT CMsgService::XService::Add( IWmiMessageReceiverSink* pSink,
  221. HANDLE* phFileOverlapped,
  222. DWORD dwFlags,
  223. void** ppHdl )
  224. {
  225. ENTER_API_CALL
  226. HRESULT hr;
  227. *ppHdl = NULL;
  228. //
  229. // create the msg service record for this sink.
  230. //
  231. CWbemPtr<CMsgServiceRecord> pRecord = new CMsgServiceRecord;
  232. if ( pRecord == NULL )
  233. {
  234. return WBEM_E_OUT_OF_MEMORY;
  235. }
  236. pRecord->SetSink( pSink );
  237. //
  238. // initialize for async or sync operation
  239. //
  240. if ( phFileOverlapped )
  241. {
  242. hr = m_pObject->Add( pRecord, *phFileOverlapped, dwFlags );
  243. }
  244. else
  245. {
  246. hr = m_pObject->Add( pRecord, dwFlags );
  247. }
  248. if ( FAILED(hr) )
  249. {
  250. return hr;
  251. }
  252. //
  253. // the msg service keeps a ref count now on the record until its sure
  254. // that it is no longer being serviced.
  255. //
  256. pRecord->AddRef();
  257. //
  258. // caller now owns a ref as well. This will be released in Remove().
  259. //
  260. pRecord->AddRef();
  261. *ppHdl = pRecord;
  262. return WBEM_S_NO_ERROR;
  263. EXIT_API_CALL
  264. }
  265. HRESULT CMsgService::XService::Remove( void* pHdl )
  266. {
  267. ENTER_API_CALL
  268. return m_pObject->Remove( pHdl );
  269. EXIT_API_CALL
  270. }
  271. /*************************************************************************
  272. CMessageServiceNT
  273. **************************************************************************/
  274. #define SHUTDOWN_COMPLETION_KEY 0xfffffffe
  275. #define INITRECV_COMPLETION_KEY 0xfffffffd
  276. CMsgServiceNT::CMsgServiceNT( CLifeControl* pControl )
  277. : CMsgService( pControl ), m_hPort( INVALID_HANDLE_VALUE )
  278. {
  279. }
  280. CMsgServiceNT::~CMsgServiceNT()
  281. {
  282. if ( m_hPort != INVALID_HANDLE_VALUE )
  283. {
  284. CloseHandle( m_hPort );
  285. }
  286. }
  287. HRESULT CMsgServiceNT::AsyncAddOverlappedFile( HANDLE hOverlappedFile,
  288. CMsgServiceRecord* pRec )
  289. {
  290. //
  291. // add the file handle that was given to us to the completion port.
  292. // when the receiver closes this file handle, it will be removed from
  293. // the completion port automatically.
  294. //
  295. HANDLE hPort = CreateIoCompletionPort( hOverlappedFile, m_hPort, 0, 0 );
  296. if ( hPort == INVALID_HANDLE_VALUE )
  297. {
  298. return HRESULT_FROM_WIN32( GetLastError() );
  299. }
  300. assert( hPort == m_hPort );
  301. //
  302. // now perform the first receive on the record. We cannot do it on this
  303. // thread because overlapped i/o cancels requests if the thread that
  304. // issued them is brought down before the i/o completes. To work around
  305. // this, we post a request to the completion port and wait for it
  306. // to be received.
  307. //
  308. if ( !PostQueuedCompletionStatus( m_hPort,
  309. 0,
  310. INITRECV_COMPLETION_KEY,
  311. pRec ) )
  312. {
  313. return HRESULT_FROM_WIN32( GetLastError() );
  314. }
  315. return S_OK;
  316. }
  317. //
  318. // assumes already locked.
  319. //
  320. HRESULT CMsgServiceNT::AsyncInitialize()
  321. {
  322. if ( m_hPort != INVALID_HANDLE_VALUE )
  323. {
  324. return S_OK;
  325. }
  326. m_hPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE,
  327. NULL,
  328. NULL,
  329. 0 );
  330. if ( m_hPort == INVALID_HANDLE_VALUE )
  331. {
  332. return HRESULT_FROM_WIN32( GetLastError() );
  333. }
  334. return S_OK;
  335. }
  336. HRESULT CMsgServiceNT::AsyncShutdown( DWORD cThreads )
  337. {
  338. //
  339. // this method has the responsibility breaking the async thread(s) out
  340. // of their svc loop.
  341. //
  342. assert( m_hPort != INVALID_HANDLE_VALUE );
  343. for( DWORD i=0; i < cThreads; i++ )
  344. {
  345. PostQueuedCompletionStatus( m_hPort, 0, SHUTDOWN_COMPLETION_KEY, NULL);
  346. }
  347. return S_OK;
  348. }
  349. HRESULT CMsgServiceNT::AsyncReceive( CMsgServiceRecord* pRecord )
  350. {
  351. ZeroMemory( pRecord, sizeof(OVERLAPPED) );
  352. return pRecord->Receive();
  353. }
  354. HRESULT CMsgServiceNT::AsyncWaitForCompletion( DWORD dwTimeout,
  355. CMsgServiceRecord** ppRecord)
  356. {
  357. BOOL bRes;
  358. ULONG dwBytesTransferred;
  359. ULONG_PTR dwCompletionKey;
  360. LPOVERLAPPED lpOverlapped;
  361. *ppRecord = NULL;
  362. bRes = GetQueuedCompletionStatus( m_hPort,
  363. &dwBytesTransferred,
  364. &dwCompletionKey,
  365. &lpOverlapped,
  366. dwTimeout );
  367. if ( bRes )
  368. {
  369. if ( dwCompletionKey == SHUTDOWN_COMPLETION_KEY )
  370. {
  371. return WBEM_E_SHUTTING_DOWN;
  372. }
  373. }
  374. else if ( lpOverlapped == NULL )
  375. {
  376. //
  377. // usually happens when the operation times out. HR will tell caller
  378. // if this is the case.
  379. //
  380. return HRESULT_FROM_WIN32( GetLastError() );
  381. }
  382. //
  383. // if we're here, then this means that we've sucessfully dequeued a
  384. // completion packet. However, the i/o operation may have failed
  385. // ( bRes is FALSE ). In this case the overlapped structure will
  386. // contain the needed error information.
  387. //
  388. *ppRecord = (CMsgServiceRecord*)lpOverlapped;
  389. //
  390. // we must also handle the case where this is an initial receive
  391. // completion. This happens when a receiver is first added. Since
  392. // we can't issue a receive on the adding thread, we must do it on our
  393. // worker threads. In this case, we return S_FALSE to signal to the
  394. // Async handling routine that there was no prior submit and a notify
  395. // should NOT be formed.
  396. //
  397. return dwCompletionKey != INITRECV_COMPLETION_KEY ? S_OK : S_FALSE;
  398. }