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.

592 lines
16 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. XactRT.cpp
  5. Abstract:
  6. This module contains RT code involved with transactions.
  7. Author:
  8. Alexander Dadiomov (alexdad) 19-Jun-96
  9. Revision History:
  10. --*/
  11. #include "stdh.h"
  12. #include "TXDTC.H"
  13. #include "txcoord.h"
  14. #include "xactsink.h"
  15. #include "cs.h"
  16. #include "mqutil.h"
  17. #include "rtprpc.h"
  18. #include "xactmq.h"
  19. #include "xactrt.tmh"
  20. //RT transactions cache: ring buffer of transaction UOWs
  21. #define XACT_RING_BUF_SIZE 16 // size of the transactions ring buffer
  22. static XACTUOW s_uowXactRingBuf[XACT_RING_BUF_SIZE]; // transaction ring buffer
  23. ULONG s_ulXrbFirst = XACT_RING_BUF_SIZE; // First used element in transaction ring buffer
  24. ULONG s_ulXrbLast = XACT_RING_BUF_SIZE; // Last used element in transaction ring buffer
  25. static BOOL g_DtcInit = FALSE;
  26. static ULONG g_StubRmCounter = 0;
  27. static CCriticalSection s_RingBufCS;
  28. // Whereabouts of the controlling DTC for the QM
  29. // For the dependent client it will be non-local
  30. ULONG g_cbQmTmWhereabouts = 0; // length of DTC whereabouts
  31. BYTE *g_pbQmTmWhereabouts = NULL; // DTC whereabouts
  32. static ITransactionExport *g_pExport = NULL; // cached DTC export object
  33. HANDLE g_hMutexDTC = NULL; // Serializes calls to DTC
  34. extern HRESULT DepGetTmWhereabouts(
  35. IN DWORD cbBufSize,
  36. OUT UCHAR *pbWhereabouts,
  37. OUT DWORD *pcbWhereabouts);
  38. /*====================================================
  39. GetMutex
  40. Internal: creates/opens global mutex and waits for it
  41. =====================================================*/
  42. HRESULT GetMutex()
  43. {
  44. if (!g_hMutexDTC)
  45. {
  46. g_hMutexDTC = CreateMutexA(NULL, FALSE, "MSMQ_DTC");
  47. }
  48. if (!g_hMutexDTC)
  49. {
  50. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("CreateMutex failed: %x "), 0));
  51. return MQ_ERROR_DTC_CONNECT;
  52. }
  53. WaitForSingleObject(g_hMutexDTC, 5 * 60 * 1000);
  54. return MQ_OK;
  55. }
  56. //---------------------------------------------------------
  57. // BOOL FindTransaction( XACTUOW *pUow )
  58. //
  59. // Description:
  60. //
  61. // Linear search in the ring buffer; *not* adds
  62. // returns TRUE if xaction was found, FALSE - if not
  63. //---------------------------------------------------------
  64. static BOOL FindTransaction(XACTUOW *pUow)
  65. {
  66. CS lock(s_RingBufCS);
  67. // Look for the UOW in the ring buffer
  68. for (ULONG i = s_ulXrbFirst; i <= s_ulXrbLast && i < XACT_RING_BUF_SIZE; i++)
  69. {
  70. if (memcmp(&s_uowXactRingBuf[i], pUow, sizeof(XACTUOW))==0)
  71. {
  72. return TRUE;
  73. }
  74. }
  75. return FALSE;
  76. }
  77. //---------------------------------------------------------
  78. // BOOL RememberTransaction( XACTUOW *pUow )
  79. //
  80. // Description:
  81. //
  82. // Linear search in the ring buffer; adds there if not found;
  83. // returns TRUE if xaction was found, FALSE - if it was added
  84. //---------------------------------------------------------
  85. static BOOL RememberTransaction(XACTUOW *pUow)
  86. {
  87. CS lock(s_RingBufCS);
  88. // Look for the UOW in the ring buffer
  89. for (ULONG i = s_ulXrbFirst; i <= s_ulXrbLast && i < XACT_RING_BUF_SIZE; i++)
  90. {
  91. if (memcmp(&s_uowXactRingBuf[i], pUow, sizeof(XACTUOW))==0)
  92. {
  93. return TRUE;
  94. }
  95. }
  96. // No check for ring buffer overflow, because it is not dangerous (maximum RT will go to QM)
  97. // adding transaction to the ring buffer
  98. if (s_ulXrbFirst == XACT_RING_BUF_SIZE)
  99. {
  100. // Ring buffer is empty
  101. s_ulXrbFirst = s_ulXrbLast = 0;
  102. memcpy(&s_uowXactRingBuf[s_ulXrbFirst], pUow, sizeof(XACTUOW));
  103. }
  104. else
  105. {
  106. s_ulXrbLast = (s_ulXrbLast == XACT_RING_BUF_SIZE-1 ? 0 : s_ulXrbLast+1);
  107. memcpy(&s_uowXactRingBuf[s_ulXrbLast], pUow, sizeof(XACTUOW));
  108. }
  109. return FALSE;
  110. }
  111. //---------------------------------------------------------
  112. // HRESULT RTpGetExportObject
  113. //
  114. // Description:
  115. //
  116. // Creates and caches the DTC export object
  117. //---------------------------------------------------------
  118. HRESULT RTpGetExportObject(IUnknown *punkDtc,
  119. ULONG cbTmWhereabouts,
  120. BYTE *pbTmWhereabouts)
  121. {
  122. HRESULT hr = MQ_OK;
  123. R<ITransactionExportFactory> pTxExpFac = NULL;
  124. if (g_pExport)
  125. {
  126. g_pExport->Release();
  127. g_pExport = NULL;
  128. }
  129. // Get the DTC's ITransactionExportFactory interface
  130. hr = punkDtc->QueryInterface (IID_ITransactionExportFactory, (void **)(&pTxExpFac.ref()));
  131. if (FAILED(hr))
  132. {
  133. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("QueryInterface failed: %x "), hr));
  134. return hr;
  135. }
  136. // Create Export object
  137. hr = pTxExpFac->Create (cbTmWhereabouts, pbTmWhereabouts, &g_pExport);
  138. if (FAILED(hr))
  139. {
  140. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("Create Export Object failed: %x "), hr));
  141. return hr;
  142. }
  143. return(MQ_OK);
  144. }
  145. //---------------------------------------------------------
  146. // HRESULT RTpBuildTransactionCookie
  147. //
  148. // Description:
  149. //
  150. // Builds transaction Cookie
  151. //---------------------------------------------------------
  152. HRESULT RTpBuildTransactionCookie(ITransaction *pTrans,
  153. ULONG *pcbCookie,
  154. BYTE **ppbCookie)
  155. {
  156. HRESULT hr = MQ_OK;
  157. ULONG cbUsed;
  158. R<IUnknown> punkTx = NULL;
  159. *pcbCookie = 0;
  160. *ppbCookie = NULL;
  161. // Get transaction's Unknown
  162. hr = pTrans->QueryInterface (IID_IUnknown, (void **)(&punkTx.ref()));
  163. if (FAILED(hr))
  164. {
  165. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("QueryInterface failed: %x "), hr));
  166. return hr;
  167. }
  168. // Get transaction cookie size
  169. hr = g_pExport->Export (punkTx.get(), pcbCookie);
  170. if (FAILED(hr) || *pcbCookie == 0)
  171. {
  172. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("Export failed: %x "), hr));
  173. return hr;
  174. }
  175. // Allocate memory for transaction Cookie
  176. try
  177. {
  178. *ppbCookie = new BYTE[*pcbCookie];
  179. }
  180. catch(const bad_alloc&)
  181. {
  182. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("Allocation failed: %x "), hr));
  183. return MQ_ERROR_INSUFFICIENT_RESOURCES;
  184. }
  185. // Get transaction Cookie itself
  186. hr = g_pExport->GetTransactionCookie (punkTx.get(), *pcbCookie, *ppbCookie, &cbUsed);
  187. if (FAILED(hr))
  188. {
  189. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("GetTransactionCookie failed: %x "), hr));
  190. return hr;
  191. }
  192. return(MQ_OK);
  193. }
  194. //---------------------------------------------------------
  195. // HRESULT RTXactGetDTC
  196. //
  197. // Description:
  198. //
  199. // Obtains DTC transaction manager. Defers to mqutil
  200. //
  201. // Outputs:
  202. // ppunkDTC pointers to DTC transaction manager
  203. //---------------------------------------------------------
  204. DLL_EXPORT HRESULT RTXactGetDTC(IUnknown **ppunkDTC)
  205. {
  206. HRESULT hr = MQ_ERROR;
  207. __try
  208. {
  209. GetMutex(); // Isolate export creation from others
  210. hr = XactGetDTC(ppunkDTC, NULL, NULL);//, g_fDependentClient);
  211. }
  212. __finally
  213. {
  214. ReleaseMutex(g_hMutexDTC);
  215. }
  216. return (SUCCEEDED(hr) ? MQ_OK : hr);
  217. }
  218. //---------------------------------------------------------
  219. // HRESULT RTpProvideTransactionEnlist
  220. //
  221. // Description:
  222. //
  223. // Provides that QM is enlisted in this transaction,
  224. // checks the transaction state
  225. //---------------------------------------------------------
  226. HRESULT RTpProvideTransactionEnlist(ITransaction *pTrans, XACTUOW *pUow)
  227. {
  228. HRESULT hr = MQ_OK;
  229. IUnknown *punkDtc = NULL;
  230. IMSMQTransaction *pIntXact = NULL;
  231. ULONG cbTmWhereabouts;
  232. BYTE *pbTmWhereabouts = NULL;
  233. ULONG cbCookie;
  234. BYTE *pbCookie = NULL;
  235. XACTTRANSINFO xinfo;
  236. BOOL fMutexTaken = FALSE;
  237. __try
  238. {
  239. //
  240. // Get the transaction info. UOW resides there.
  241. //
  242. hr = pTrans->GetTransactionInfo(&xinfo);
  243. if (FAILED(hr))
  244. {
  245. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("GetTransactionInfo failed: %x "), hr));
  246. __leave;
  247. }
  248. // Put pointer to UOW in the output parameter
  249. CopyMemory(pUow, &xinfo.uow, sizeof(XACTUOW));
  250. //
  251. // Is it internal transaction?
  252. //
  253. pTrans->QueryInterface (IID_IMSMQTransaction, (void **)(&pIntXact));
  254. if (pIntXact)
  255. {
  256. // Internal transactions
  257. //------------------------
  258. hr = pIntXact->EnlistTransaction(pUow);
  259. if (FAILED(hr))
  260. {
  261. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("EnlistTransaction failed: %x "), hr));
  262. }
  263. }
  264. else
  265. {
  266. // External transactions
  267. //------------------------
  268. // Look for the transaction in the cache
  269. //
  270. if (FindTransaction(pUow)) // this xaction is known already; QM must have been enlisted
  271. {
  272. hr = MQ_OK;
  273. __leave;
  274. }
  275. // Get global mutex to isolate enlistment
  276. //
  277. GetMutex(); // Isolate export creation from others
  278. fMutexTaken = TRUE;
  279. //
  280. // Get the DTC IUnknown and TM whereabouts
  281. //
  282. hr = XactGetDTC(&punkDtc, &cbTmWhereabouts, &pbTmWhereabouts);//, g_fDependentClient);
  283. if (FAILED(hr))
  284. {
  285. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("XactGetDTC failed: %x "), hr));
  286. __leave;
  287. }
  288. // XactGetDTC could return success code of 1 if it reconnected to DTC
  289. if (hr == 1)
  290. {
  291. // No Release: DTC object is not alive anymore
  292. g_pExport = NULL;
  293. }
  294. // Get the QM's controlling DTC whereabouts
  295. //
  296. if (!g_pbQmTmWhereabouts)
  297. {
  298. g_cbQmTmWhereabouts = 128;
  299. g_pbQmTmWhereabouts = new BYTE[128];
  300. DWORD cbNeeded;
  301. hr = DepGetTmWhereabouts(g_cbQmTmWhereabouts, g_pbQmTmWhereabouts, &cbNeeded);
  302. if (hr == MQ_ERROR_USER_BUFFER_TOO_SMALL)
  303. {
  304. delete [] g_pbQmTmWhereabouts;
  305. g_cbQmTmWhereabouts = cbNeeded;
  306. g_pbQmTmWhereabouts = new BYTE[cbNeeded];
  307. hr = DepGetTmWhereabouts(g_cbQmTmWhereabouts, g_pbQmTmWhereabouts, &cbNeeded);
  308. }
  309. if (FAILED(hr))
  310. {
  311. delete [] g_pbQmTmWhereabouts;
  312. g_cbQmTmWhereabouts = 0;
  313. g_pbQmTmWhereabouts = NULL;
  314. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("DepGetTmWhereabouts failed: %x "), hr));
  315. __leave;
  316. }
  317. else
  318. {
  319. g_cbQmTmWhereabouts = cbNeeded;
  320. }
  321. }
  322. //
  323. // Get and cache Export object
  324. //
  325. if (g_pExport == NULL)
  326. {
  327. hr = RTpGetExportObject(
  328. punkDtc,
  329. g_cbQmTmWhereabouts,
  330. g_pbQmTmWhereabouts);
  331. if (FAILED(hr))
  332. {
  333. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("RTpGetExportObject failed: %x "), hr));
  334. __leave;
  335. }
  336. }
  337. //
  338. // Prepare the transaction Cookie
  339. //
  340. hr = RTpBuildTransactionCookie(
  341. pTrans,
  342. &cbCookie,
  343. &pbCookie);
  344. if (FAILED(hr))
  345. {
  346. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("RTpBuildTransactionCookie failed: %x "), hr));
  347. __leave;
  348. }
  349. //
  350. // RPC call to QM for enlistment
  351. //
  352. __try
  353. {
  354. INIT_RPC_HANDLE ;
  355. if(tls_hBindRpc == 0)
  356. return MQ_ERROR_SERVICE_NOT_AVAILABLE;
  357. hr = QMEnlistTransaction(tls_hBindRpc, pUow, cbCookie, pbCookie);
  358. }
  359. __except(EXCEPTION_EXECUTE_HANDLER)
  360. {
  361. DWORD rc = GetExceptionCode();
  362. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("RTpProvideTransactionEnlist failed: RPC code=%x "), rc));
  363. DBG_USED(rc);
  364. hr = MQ_ERROR_SERVICE_NOT_AVAILABLE;
  365. }
  366. //Now that transaction is actually enlisted we remember it in ring buffer
  367. if (SUCCEEDED(hr))
  368. {
  369. RememberTransaction(pUow);
  370. }
  371. else
  372. {
  373. DBGMSG((DBGMOD_XACT, DBGLVL_ERROR, TEXT("QMEnlistTransaction failed: %x "), hr));
  374. }
  375. }
  376. if (FAILED(hr)) {
  377. __leave;
  378. }
  379. hr = MQ_OK;
  380. }
  381. __finally
  382. {
  383. if (SUCCEEDED(hr) && AbnormalTermination())
  384. hr = MQ_ERROR;
  385. #ifdef _DEBUG
  386. DWORD cRef = 0;
  387. if (punkDtc)
  388. cRef = punkDtc->Release();
  389. #else
  390. if (punkDtc)
  391. punkDtc->Release();
  392. #endif
  393. if (pIntXact)
  394. pIntXact->Release();
  395. if (pbCookie)
  396. delete pbCookie;
  397. if (fMutexTaken)
  398. ReleaseMutex(g_hMutexDTC);
  399. }
  400. return(hr);
  401. }
  402. //---------------------------------------------------------
  403. // HRESULT InitStubRm
  404. //
  405. // Description:
  406. //
  407. // Initializes stub RM manager - now needed only for performance managements
  408. //
  409. //---------------------------------------------------------
  410. IResourceManager *g_pIResMgr = 0;
  411. HRESULT InitStubRm()
  412. {
  413. HRESULT hRc;
  414. IResourceManagerFactory *pIRmFactory = 0;
  415. CStubIResourceManagerSink *pIResMgrSink = 0;
  416. IUnknown *punkDTC = 0;
  417. UUID guid;
  418. //CS lock(s_RingBufCS);
  419. DBGMSG((DBGMOD_XACT, DBGLVL_WARNING, TEXT("InitStubRM called!")));
  420. if (g_DtcInit)
  421. {
  422. return MQ_OK;
  423. }
  424. //
  425. // (1) Establish contact with the MS DTC transaction manager.
  426. // First the application obtains the IUnknown interface to the DTC TM.
  427. //
  428. hRc = XactGetDTC(&punkDTC, NULL, NULL);//, g_fDependentClient);
  429. if (FAILED(hRc)) {
  430. return hRc;
  431. }
  432. // Get the resource manager factory from the IUnknown
  433. hRc = punkDTC->QueryInterface(IID_IResourceManagerFactory,(LPVOID *) &pIRmFactory);
  434. punkDTC->Release();
  435. if (S_OK != hRc)
  436. {
  437. return hRc;
  438. }
  439. // (2) Create and instance of the resource manager interface. A
  440. // pointer to this interface is retrned to the client appliction
  441. // through the pIResMgr member variable.
  442. //
  443. // Create a resource manager sink and create an instance of the
  444. // resource manager through the resource manager factory.
  445. pIResMgrSink = new CStubIResourceManagerSink; //stub implementation
  446. if ( 0 == pIResMgrSink )
  447. {
  448. pIRmFactory->Release();
  449. return E_FAIL;
  450. }
  451. //pIResMgrSink->AddRef();
  452. // Create a new guid for each resource manager.
  453. hRc = UuidCreate (&guid);
  454. if ( S_OK != hRc)
  455. {
  456. return E_FAIL;
  457. }
  458. // Prepare stub RM name (ANSI)
  459. CHAR szStubBaseName[MAX_REG_DEFAULT_LEN];
  460. READ_REG_STRING(wszStubBaseName, FALCON_RM_STUB_NAME_REGNAME, FALCON_DEFAULT_STUB_RM_NAME ) ;
  461. size_t res = wcstombs(szStubBaseName, wszStubBaseName, sizeof(szStubBaseName));
  462. ASSERT(res != (size_t)(-1));
  463. DBG_USED(res);
  464. CHAR szStubRmName[60];
  465. sprintf(szStubRmName, "%s%d", szStubBaseName, g_StubRmCounter++);
  466. // Create instance of the resource manager interface.
  467. hRc = pIRmFactory->Create (&guid,
  468. szStubRmName,
  469. (IResourceManagerSink *) pIResMgrSink,
  470. &g_pIResMgr );
  471. //pIRmFactory->Release();
  472. if (S_OK != hRc)
  473. {
  474. return hRc;
  475. }
  476. //g_pIResMgr->AddRef(); // we want to keep it. BUGBUG: When will we release it ?
  477. g_DtcInit = TRUE;
  478. return S_OK;
  479. }
  480. //---------------------------------------------------------
  481. // void RTpInitXactRingBuf()
  482. //
  483. // Description:
  484. //
  485. // Initiates the ring buffer data
  486. //---------------------------------------------------------
  487. void RTpInitXactRingBuf()
  488. {
  489. CS lock(s_RingBufCS);
  490. s_ulXrbFirst = XACT_RING_BUF_SIZE;
  491. s_ulXrbLast = XACT_RING_BUF_SIZE;
  492. g_DtcInit = FALSE;
  493. }