1 /* Module: msdtc_enlist.cpp
2 *
3 * Description:
4 * This module contains routines related to
5 * the enlistment in MSDTC.
6 *
7 *-------
8 */
9
10 #ifdef _HANDLE_ENLIST_IN_DTC_
11
12 #undef _MEMORY_DEBUG_
13 #ifndef _WIN32_WINNT
14 #define _WIN32_WINNT 0x0400
15 #endif /* _WIN32_WINNT */
16
17 #define WIN32_LEAN_AND_MEAN
18 #include <oleTx2xa.h>
19 #include <XOLEHLP.h>
20 /*#include <Txdtc.h>*/
21 #define _PGDTC_FUNCS_IMPORT_
22 #include "connexp.h"
23
24 /*#define _SLEEP_FOR_TEST_*/
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <process.h>
29 #include <map>
30 #ifndef WIN32
31 #include <errno.h>
32 #endif /* WIN32 */
33
34 #include <sql.h>
35 #define _MYLOG_FUNCS_IMPORT_
36 #include "mylog.h"
37 #define _PGENLIST_FUNCS_IMPLEMENT_
38 #include "pgenlist.h"
39 #include "xalibname.h"
40
41 #ifdef WIN32
42 #ifndef snprintf
43 #define snprintf _snprintf
44 #endif /* snprintf */
45 #endif /* WIN32 */
46
47 /* Define a type for defining a constant string expression */
48 #ifndef CSTR
49 #define CSTR static const char * const
50 #endif /* CSTR */
51
52 EXTERN_C {
53 HINSTANCE s_hModule; /* Saved module handle. */
54 }
55 /* This is where the Driver Manager attaches to this Driver */
56 BOOL WINAPI
57 DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
58 {
59 switch (ul_reason_for_call)
60 {
61 case DLL_PROCESS_ATTACH:
62 s_hModule = (HINSTANCE) hInst; /* Save for dialog boxes */
63 break;
64 case DLL_PROCESS_DETACH:
65 mylog("DETACHING pgenlist\n");
66 break;
67 }
68 return TRUE;
69 }
70
71 /*
72 * A comment About locks used in this module
73 *
74 * the locks should be acquired with stronger to weaker order.
75 *
76 * 1:ELOCK -- the strongest per IAsyncPG object lock
77 * When the *isolated* or *dtcconn* member of an IAsyncPG object
78 * is changed, this lock should be held.
79 * While an IAsyncPG object accesses a psqlodbc connection,
80 * this lock should be held.
81 *
82 * 2:[CONN_CS] -- per psqlodbc connection lock
83 * This lock would be held for a pretty long time while accessing
84 * the psqlodbc connection assigned to an IAsyncPG object. You
85 * can use the connecion safely by holding a ELOCK for the
86 * IAsyncPG object because the assignment is ensured to be
87 * fixed while the ELOCK is held.
88 *
89 * 3:LIFELOCK -- a global lock to ensure the lives of IAsyncPG objects
90 * While this lock is held, IAsyncPG objects would never die.
91 *
92 * 4:SLOCK -- the short term per IAsyncPG object lock
93 * When any member of an IAsyncPG object is changed, this lock
94 * should be held.
95 */
96
97 // #define _LOCK_DEBUG_
98 static class INIT_CRIT
99 {
100 public:
101 CRITICAL_SECTION life_cs; /* for asdum member of ConnectionClass */
INIT_CRIT()102 INIT_CRIT() {
103 InitializeCriticalSection(&life_cs);
104 }
~INIT_CRIT()105 ~INIT_CRIT() {
106 DeleteCriticalSection(&life_cs);
107 }
108 } init_crit;
109 #define LIFELOCK_ACQUIRE EnterCriticalSection(&init_crit.life_cs)
110 #define LIFELOCK_RELEASE LeaveCriticalSection(&init_crit.life_cs)
111
112 /*
113 * Some helper macros about connection handling.
114 */
115 #define CONN_CS_ACQUIRE(conn) PgDtc_lock_cntrl((conn), TRUE, FALSE)
116 #define TRY_CONN_CS_ACQUIRE(conn) PgDtc_lock_cntrl((conn), TRUE, TRUE)
117 #define CONN_CS_RELEASE(conn) PgDtc_lock_cntrl((conn), FALSE, FALSE)
118
119 #define CONN_IS_IN_TRANS(conn) PgDtc_get_property((conn), inTrans)
120
121
XidToText(const XID & xid,char * rtext)122 static const char *XidToText(const XID &xid, char *rtext)
123 {
124 int glen = xid.gtrid_length, blen = xid.bqual_length;
125 int i, j;
126
127 for (i = 0, j = 0; i < glen; i++, j += 2)
128 sprintf(rtext + j, "%02x", (unsigned char) xid.data[i]);
129 strcat(rtext, "-"); j++;
130 for (; i < glen + blen; i++, j += 2)
131 sprintf(rtext + j, "%02x", (unsigned char) xid.data[i]);
132 return rtext;
133 }
134
135 static LONG g_cComponents = 0;
136 static LONG g_cServerLocks = 0;
137
138 //
139 // �ȉ���ITransactionResourceAsync�I�u�W�F�N�g�͔C�ӂ̃X���b�h����
140 // ���R�ɃA�N�Z�X�\�Ȃ悤�Ɏ�������B�eRequest�̌��ʂ�Ԃ����߂�
141 // �g�p����ITransactionEnlistmentAsync�C���^�[�t�F�C�X�����̂悤��
142 // ��������Ă���i�Ǝv����A���L�Q�Ɓj�̂ŌĂяo����COM�̃A�p�[
143 // �g�����g���ӎ�����(CoMarshalInterThreadInterfaceInStream/CoGetIn
144 // terfaceAndReleaseStream���g�p����j�K�v�͂Ȃ��B
145 // ����DLL���Ŏg�p����ITransactionResourceAsync��ITransactionEnlist
146 // mentAsync�̃C���^�[�t�F�C�X�|�C���^�[�͔C�ӂ̃X���b�h���璼�ڎg�p
147 // ���邱�Ƃ��ł���B
148 //
149
150 // OLE Transactions Standard
151 //
152 // OLE Transactions is the Microsoft interface standard for transaction
153 // management. Applications use OLE Transactions-compliant interfaces to
154 // initiate, commit, abort, and inquire about transactions. Resource
155 // managers use OLE Transactions-compliant interfaces to enlist in
156 // transactions, to propagate transactions to other resource managers,
157 // to propagate transactions from process to process or from system to
158 // system, and to participate in the two-phase commit protocol.
159 //
160 // The Microsoft DTC system implements most OLE Transactions-compliant
161 // objects, interfaces, and methods. Resource managers that wish to use
162 // OLE Transactions must implement some OLE Transactions-compliant objects,
163 // interfaces, and methods.
164 //
165 // The OLE Transactions specification is based on COM but it differs in the
166 // following respects:
167 //
168 // OLE Transactions objects cannot be created using the COM CoCreate APIs.
169 // References to OLE Transactions objects are always direct. Therefore,
170 // no proxies or stubs are created for inter-apartment, inter-process,
171 // or inter-node calls and OLE Transactions references cannot be marshaled
172 // using standard COM marshaling.
173 // All references to OLE Transactions objects and their sinks are completely
174 // free threaded and cannot rely upon COM concurrency control models.
175 // For example, you cannot pass a reference to an IResourceManagerSink
176 // interface on a single-threaded apartment and expect the callback to occur
177 // only on the same single-threaded apartment.
178
179 class IAsyncPG : public ITransactionResourceAsync
180 {
181 private:
182 IDtcToXaHelperSinglePipe *helper;
183 DWORD RMCookie;
184 void *dtcconn;
185 LONG refcnt;
186 CRITICAL_SECTION as_spin; // to make this object Both
187 CRITICAL_SECTION as_exec; // to make this object Both
188 XID xid;
189 bool isolated;
190 bool prepared;
191 bool done;
192 bool abort;
193 HANDLE eThread[3];
194 bool eFin[3];
195 bool requestAccepted;
196 HRESULT prepare_result;
197 HRESULT commit_result;
198 #ifdef _LOCK_DEBUG_
199 int spin_cnt;
200 int cs_cnt;
201 #endif /* _LOCK_DEBUG_ */
202
203 public:
204 enum {
205 PrepareExec = 0
206 ,CommitExec
207 ,AbortExec
208 };
209
210 ITransactionEnlistmentAsync *enlist;
211
212 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject);
213 ULONG STDMETHODCALLTYPE AddRef(void);
214 ULONG STDMETHODCALLTYPE Release(void);
215
216 HRESULT STDMETHODCALLTYPE PrepareRequest(BOOL fRetaining,
217 DWORD grfRM,
218 BOOL fWantMoniker,
219 BOOL fSinglePhase);
220 HRESULT STDMETHODCALLTYPE CommitRequest(DWORD grfRM, XACTUOW * pNewUOW);
221 HRESULT STDMETHODCALLTYPE AbortRequest(BOID * pboidReason,
222 BOOL fRetaining,
223 XACTUOW * pNewUOW);
224 HRESULT STDMETHODCALLTYPE TMDown(void);
225
226 IAsyncPG();
SetHelper(IDtcToXaHelperSinglePipe * pHelper,DWORD dwRMCookie)227 void SetHelper(IDtcToXaHelperSinglePipe *pHelper, DWORD dwRMCookie) {helper = pHelper; RMCookie = dwRMCookie;}
228
229 HRESULT RequestExec(DWORD type, HRESULT res);
230 HRESULT ReleaseConnection(void);
SetConnection(void * sconn)231 void SetConnection(void *sconn) {SLOCK_ACQUIRE(); dtcconn = sconn; SLOCK_RELEASE();}
SetXid(const XID * ixid)232 void SetXid(const XID *ixid) {SLOCK_ACQUIRE(); xid = *ixid; SLOCK_RELEASE();}
233 void *separateXAConn(bool spinAcquired, bool continueConnection);
234 bool CloseThread(DWORD type);
235 private:
236 ~IAsyncPG();
SLOCK_ACQUIRE()237 void SLOCK_ACQUIRE() {EnterCriticalSection(&as_spin);}
SLOCK_RELEASE()238 void SLOCK_RELEASE() {LeaveCriticalSection(&as_spin);}
ELOCK_ACQUIRE()239 void ELOCK_ACQUIRE() {EnterCriticalSection(&as_exec);}
ELOCK_RELEASE()240 void ELOCK_RELEASE() {LeaveCriticalSection(&as_exec);}
241 void *getLockedXAConn(void);
242 void *generateXAConn(bool spinAcquired);
243 void *isolateXAConn(bool spinAcquired, bool continueConnection);
SetPrepareResult(HRESULT res)244 void SetPrepareResult(HRESULT res) {SLOCK_ACQUIRE(); prepared = true; prepare_result = res; SLOCK_RELEASE();}
245 void SetDone(HRESULT);
246 void Wait_pThread(bool slock_hold);
247 void Wait_cThread(bool slock_hold, bool once);
248 };
249
250
IAsyncPG(void)251 IAsyncPG::IAsyncPG(void) : helper(NULL), RMCookie(0), enlist(NULL), dtcconn(NULL), refcnt(1), isolated(false), done(false), abort(false), prepared(false), requestAccepted(false)
252 {
253 InterlockedIncrement(&g_cComponents);
254 InitializeCriticalSection(&as_spin);
255 InitializeCriticalSection(&as_exec);
256 eThread[0] = eThread[1] = eThread[2] = NULL;
257 eFin[0] = eFin[1] = eFin[2] = false;
258 memset(&xid, 0, sizeof(xid));
259 #ifdef _LOCK_DEBUG_
260 spin_cnt = 0;
261 cs_cnt = 0;
262 #endif /* _LOCK_DEBUG_ */
263 }
264
265 //
266 // invoked from *delete*.
267 // When entered ELOCK -> LIFELOCK -> SLOCK are held
268 // and they are released.
269 //
~IAsyncPG(void)270 IAsyncPG::~IAsyncPG(void)
271 {
272 void *fconn = NULL;
273
274 if (dtcconn)
275 {
276 if (isolated)
277 fconn = dtcconn;
278 PgDtc_set_async(dtcconn, NULL);
279 dtcconn = NULL;
280 }
281 SLOCK_RELEASE();
282 LIFELOCK_RELEASE;
283 if (fconn)
284 {
285 mylog("IAsyncPG Destructor is freeing the connection\n");
286 PgDtc_free_connect(fconn);
287 }
288 DeleteCriticalSection(&as_spin);
289 ELOCK_RELEASE();
290 DeleteCriticalSection(&as_exec);
291 InterlockedDecrement(&g_cComponents);
292 }
QueryInterface(REFIID riid,void ** ppvObject)293 HRESULT STDMETHODCALLTYPE IAsyncPG::QueryInterface(REFIID riid, void ** ppvObject)
294 {
295 mylog("%p QueryInterface called\n", this);
296 if (riid == IID_IUnknown || riid == IID_ITransactionResourceAsync)
297 {
298 *ppvObject = this;
299 AddRef();
300 return S_OK;
301 }
302 *ppvObject = NULL;
303 return E_NOINTERFACE;
304 }
305 //
306 // acquire/releases SLOCK.
307 //
AddRef(void)308 ULONG STDMETHODCALLTYPE IAsyncPG::AddRef(void)
309 {
310 mylog("%p->AddRef called\n", this);
311 SLOCK_ACQUIRE();
312 refcnt++;
313 SLOCK_RELEASE();
314 return refcnt;
315 }
316 //
317 // acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
318 //
Release(void)319 ULONG STDMETHODCALLTYPE IAsyncPG::Release(void)
320 {
321 mylog("%p->Release called refcnt=%d\n", this, refcnt);
322 SLOCK_ACQUIRE();
323 refcnt--;
324 if (refcnt <= 0)
325 {
326 SLOCK_RELEASE();
327 ELOCK_ACQUIRE();
328 LIFELOCK_ACQUIRE;
329 SLOCK_ACQUIRE();
330 if (refcnt <=0)
331 {
332 const int refcnt_copy = refcnt;
333 mylog("delete %p\n", this);
334 delete this;
335 return refcnt_copy;
336 }
337 else
338 {
339 SLOCK_RELEASE();
340 LIFELOCK_RELEASE;
341 ELOCK_RELEASE();
342 }
343 }
344 else
345 SLOCK_RELEASE();
346 return refcnt;
347 }
348
349 //
350 // Acquire/release SLOCK.
351 //
Wait_pThread(bool slock_hold)352 void IAsyncPG::Wait_pThread(bool slock_hold)
353 {
354 mylog("Wait_pThread %d in\n", slock_hold);
355 HANDLE wThread;
356 int wait_idx = PrepareExec;
357 DWORD ret;
358
359 if (!slock_hold)
360 SLOCK_ACQUIRE();
361 while (NULL != (wThread = eThread[wait_idx]) && !eFin[wait_idx])
362 {
363 SLOCK_RELEASE();
364 ret = WaitForSingleObject(wThread, 2000);
365 SLOCK_ACQUIRE();
366 if (WAIT_TIMEOUT != ret)
367 eFin[wait_idx] = true;
368 }
369 if (!slock_hold)
370 SLOCK_RELEASE();
371 mylog("Wait_pThread out\n");
372 }
373
374 //
375 // Acquire/releases SLOCK.
376 //
Wait_cThread(bool slock_hold,bool once)377 void IAsyncPG::Wait_cThread(bool slock_hold, bool once)
378 {
379 HANDLE wThread;
380 int wait_idx;
381 DWORD ret;
382
383 mylog("Wait_cThread %d,%d in\n", slock_hold, once);
384 if (!slock_hold)
385 SLOCK_ACQUIRE();
386 if (NULL != eThread[CommitExec])
387 wait_idx = CommitExec;
388 else
389 wait_idx = AbortExec;
390 while (NULL != (wThread = eThread[wait_idx]) && !eFin[wait_idx])
391 {
392 SLOCK_RELEASE();
393 ret = WaitForSingleObject(wThread, 2000);
394 SLOCK_ACQUIRE();
395 if (WAIT_TIMEOUT != ret)
396 eFin[wait_idx] = true;
397 else if (once)
398 break;
399 }
400 if (!slock_hold)
401 SLOCK_RELEASE();
402 mylog("Wait_cThread out\n");
403 }
404
405 /* Processing Prepare/Commit Request */
406 typedef
407 struct RequestPara {
408 DWORD type;
409 LPVOID lpr;
410 HRESULT res;
411 } RequestPara;
412
413 //
414 // Acquire/releases LIFELOCK -> SLOCK.
415 // may acquire/release ELOCK.
416 //
SetDone(HRESULT res)417 void IAsyncPG::SetDone(HRESULT res)
418 {
419 LIFELOCK_ACQUIRE;
420 SLOCK_ACQUIRE();
421 done = true;
422 if (E_FAIL == res ||
423 E_UNEXPECTED == res)
424 abort = true;
425 requestAccepted = true;
426 commit_result = res;
427 if (dtcconn)
428 {
429 PgDtc_set_async(dtcconn, NULL);
430 if (isolated)
431 {
432 SLOCK_RELEASE();
433 LIFELOCK_RELEASE;
434 ELOCK_ACQUIRE();
435 if (dtcconn)
436 {
437 mylog("Freeing isolated connection=%p\n", dtcconn);
438 PgDtc_free_connect(dtcconn);
439 SetConnection(NULL);
440 }
441 ELOCK_RELEASE();
442 }
443 else
444 {
445 dtcconn = NULL;
446 SLOCK_RELEASE();
447 LIFELOCK_RELEASE;
448 }
449 }
450 else
451 {
452 SLOCK_RELEASE();
453 LIFELOCK_RELEASE;
454 }
455 }
456
457 //
458 // Acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
459 //
generateXAConn(bool spinAcquired)460 void *IAsyncPG::generateXAConn(bool spinAcquired)
461 {
462 mylog("generateXAConn isolated=%d dtcconn=%p\n", isolated, dtcconn);
463 if (!spinAcquired)
464 SLOCK_ACQUIRE();
465 if (isolated || done)
466 {
467 SLOCK_RELEASE();
468 return dtcconn;
469 }
470 SLOCK_RELEASE();
471 ELOCK_ACQUIRE();
472 LIFELOCK_ACQUIRE;
473 SLOCK_ACQUIRE();
474 if (dtcconn && !isolated && !done && prepared)
475 {
476 void *sconn = dtcconn;
477
478 dtcconn = PgDtc_isolate(sconn, useAnotherRoom);
479 isolated = true;
480 SLOCK_RELEASE();
481 LIFELOCK_RELEASE;
482 // PgDtc_connect(dtcconn); may be called in getLockedXAConn
483 }
484 else
485 {
486 SLOCK_RELEASE();
487 LIFELOCK_RELEASE;
488 }
489 ELOCK_RELEASE();
490 return dtcconn;
491 }
492
493 //
494 // Acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
495 //
isolateXAConn(bool spinAcquired,bool continueConnection)496 void *IAsyncPG::isolateXAConn(bool spinAcquired, bool continueConnection)
497 {
498 void *sconn;
499
500 mylog("isolateXAConn isolated=%d dtcconn=%p\n", isolated, dtcconn);
501 if (!spinAcquired)
502 SLOCK_ACQUIRE();
503 if (isolated || done || NULL == dtcconn)
504 {
505 SLOCK_RELEASE();
506 return dtcconn;
507 }
508 SLOCK_RELEASE();
509 ELOCK_ACQUIRE();
510 LIFELOCK_ACQUIRE;
511 SLOCK_ACQUIRE();
512 if (isolated || done || NULL == dtcconn)
513 {
514 SLOCK_RELEASE();
515 LIFELOCK_RELEASE;
516 ELOCK_RELEASE();
517 return dtcconn;
518 }
519 sconn = dtcconn;
520
521 dtcconn = PgDtc_isolate(sconn, continueConnection ? 0 : disposingConnection);
522
523 isolated = true;
524 SLOCK_RELEASE();
525 LIFELOCK_RELEASE;
526 if (continueConnection)
527 {
528 PgDtc_connect(sconn);
529 }
530 ELOCK_RELEASE();
531 return dtcconn;
532 }
533
534 //
535 // Acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
536 //
separateXAConn(bool spinAcquired,bool continueConnection)537 void *IAsyncPG::separateXAConn(bool spinAcquired, bool continueConnection)
538 {
539 mylog("%s isolated=%d dtcconn=%p\n", __FUNCTION__, isolated, dtcconn);
540 if (!spinAcquired)
541 SLOCK_ACQUIRE();
542 if (prepared)
543 return generateXAConn(true);
544 else
545 return isolateXAConn(true, continueConnection);
546 }
547
548 //
549 // [when entered]
550 // ELOCK is held.
551 //
552 // Acquire/releases SLOCK.
553 // Try to acquire CONN_CS also.
554 //
555 // [on exit]
556 // ELOCK is kept held.
557 // If the return connection != NULL
558 // the CONN_CS lock for the connection is held.
559 //
getLockedXAConn()560 void *IAsyncPG::getLockedXAConn()
561 {
562 SLOCK_ACQUIRE();
563 while (!done && !isolated && NULL != dtcconn)
564 {
565 /*
566 * Note that COMMIT/ROLLBACK PREPARED command should be
567 * issued outside the transaction.
568 */
569 if (!prepared || !CONN_IS_IN_TRANS(dtcconn))
570 {
571 if (TRY_CONN_CS_ACQUIRE(dtcconn))
572 {
573 if (prepared && CONN_IS_IN_TRANS(dtcconn))
574 {
575 CONN_CS_RELEASE(dtcconn);
576 }
577 else
578 break;
579 }
580 }
581 separateXAConn(true, true);
582 SLOCK_ACQUIRE(); // SLOCK was released by separateXAConn()
583 }
584 SLOCK_RELEASE();
585 if (isolated && NULL != dtcconn)
586 {
587 CONN_CS_ACQUIRE(dtcconn);
588 if (!PgDtc_get_property(dtcconn, connected))
589 PgDtc_connect(dtcconn);
590 }
591 return dtcconn;
592 }
593
594 //
595 // Acquire/release ELOCK -> SLOCK.
596 //
RequestExec(DWORD type,HRESULT res)597 HRESULT IAsyncPG::RequestExec(DWORD type, HRESULT res)
598 {
599 HRESULT ret;
600 bool bReleaseEnlist = false;
601 void *econn;
602 char pgxid[258];
603
604 mylog("%p->RequestExec type=%d conn=%p\n", this, type, dtcconn);
605 XidToText(xid, pgxid);
606 #ifdef _SLEEP_FOR_TEST_
607 /*Sleep(2000);*/
608 #endif /* _SLEEP_FOR_TEST_ */
609 ELOCK_ACQUIRE();
610 switch (type)
611 {
612 case PrepareExec:
613 if (done || NULL == dtcconn)
614 {
615 res = E_UNEXPECTED;
616 break;
617 }
618 if (econn = getLockedXAConn(), NULL != econn)
619 {
620 PgDtc_set_property(econn, inprogress, (void *) 1);
621 if (E_FAIL == res)
622 PgDtc_one_phase_operation(econn, ABORT_GLOBAL_TRANSACTION);
623 else if (XACT_S_SINGLEPHASE == res)
624 {
625 if (!PgDtc_one_phase_operation(econn, ONE_PHASE_COMMIT))
626 res = E_FAIL;
627 }
628 else
629 {
630 if (!PgDtc_two_phase_operation(econn, PREPARE_TRANSACTION, pgxid))
631 res = E_FAIL;
632 }
633 PgDtc_set_property(econn, inprogress, (void *) 0);
634 CONN_CS_RELEASE(econn);
635 }
636 if (S_OK != res)
637 {
638 SetDone(res);
639 bReleaseEnlist = true;
640 }
641 ret = enlist->PrepareRequestDone(res, NULL, NULL);
642 SetPrepareResult(res);
643 break;
644 case CommitExec:
645 Wait_pThread(false);
646 if (E_FAIL != res)
647 {
648 econn = getLockedXAConn();
649 if (econn)
650 {
651 PgDtc_set_property(econn, inprogress, (void *) 1);
652 if (!PgDtc_two_phase_operation(econn, COMMIT_PREPARED, pgxid))
653 res = E_FAIL;
654 PgDtc_set_property(econn, inprogress, (void *) 0);
655 CONN_CS_RELEASE(econn);
656 }
657 }
658 SetDone(res);
659 ret = enlist->CommitRequestDone(res);
660 bReleaseEnlist = true;
661 break;
662 case AbortExec:
663 Wait_pThread(false);
664 if (prepared && !done)
665 {
666 econn = getLockedXAConn();
667 if (econn)
668 {
669 PgDtc_set_property(econn, inprogress, (void *) 1);
670 if (!PgDtc_two_phase_operation(econn, ROLLBACK_PREPARED, pgxid))
671 res = E_FAIL;
672 PgDtc_set_property(econn, inprogress, (void *) 0);
673 CONN_CS_RELEASE(econn);
674 }
675 }
676 SetDone(res);
677 ret = enlist->AbortRequestDone(res);
678 bReleaseEnlist = true;
679 break;
680 default:
681 ret = -1;
682 }
683 if (bReleaseEnlist)
684 {
685 helper->ReleaseRMCookie(RMCookie, TRUE);
686 enlist->Release();
687 }
688 ELOCK_RELEASE();
689 mylog("%p->Done ret=%d\n", this, ret);
690 return ret;
691 }
692
693 //
694 // Acquire/releses SLOCK
695 // or [ELOCK -> LIFELOCK -> ] SLOCK.
696 //
ReleaseConnection(void)697 HRESULT IAsyncPG::ReleaseConnection(void)
698 {
699 mylog("%p->ReleaseConnection\n", this);
700
701 SLOCK_ACQUIRE();
702 if (isolated || NULL == dtcconn)
703 {
704 SLOCK_RELEASE();
705 return SQL_SUCCESS;
706 }
707 Wait_pThread(true);
708 if (NULL != eThread[CommitExec] || NULL != eThread[AbortExec] || requestAccepted)
709 {
710 if (!done)
711 Wait_cThread(true, true);
712 }
713 if (!isolated && !done && dtcconn && PgDtc_get_property(dtcconn, connected))
714 {
715 isolateXAConn(true, false);
716 }
717 else
718 SLOCK_RELEASE();
719 mylog("%p->ReleaseConnection exit\n", this);
720 return SQL_SUCCESS;
721 }
722
723 EXTERN_C static unsigned WINAPI DtcRequestExec(LPVOID para);
724 EXTERN_C static void __cdecl ClosePrepareThread(LPVOID para);
725 EXTERN_C static void __cdecl CloseCommitThread(LPVOID para);
726 EXTERN_C static void __cdecl CloseAbortThread(LPVOID para);
727
728 //
729 // Acquire/release [ELOCK -> ] SLOCK.
730 //
PrepareRequest(BOOL fRetaining,DWORD grfRM,BOOL fWantMoniker,BOOL fSinglePhase)731 HRESULT STDMETHODCALLTYPE IAsyncPG::PrepareRequest(BOOL fRetaining, DWORD grfRM,
732 BOOL fWantMoniker, BOOL fSinglePhase)
733 {
734 HRESULT ret, res;
735 RequestPara *reqp;
736 const DWORD reqtype = PrepareExec;
737
738 mylog("%p PrepareRequest called grhRM=%d enl=%p\n", this, grfRM, enlist);
739 SLOCK_ACQUIRE();
740 if (dtcconn && 0 != PgDtc_get_property(dtcconn, errorNumber))
741 res = ret = E_FAIL;
742 else
743 {
744 ret = S_OK;
745 if (fSinglePhase)
746 {
747 res = XACT_S_SINGLEPHASE;
748 mylog("XACT is singlePhase\n");
749 }
750 else
751 res = S_OK;
752 }
753 SLOCK_RELEASE();
754 ELOCK_ACQUIRE();
755 #ifdef _SLEEP_FOR_TEST_
756 Sleep(2000);
757 #endif /* _SLEEP_FOR_TEST_ */
758 reqp = new RequestPara;
759 reqp->type = reqtype;
760 reqp->lpr = (LPVOID) this;
761 reqp->res = res;
762 #define DONT_CALL_RETURN_FROM_HERE ???
763 AddRef();
764 HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, DtcRequestExec, reqp, 0, NULL);
765 if (NULL == hThread)
766 {
767 delete(reqp);
768 ret = E_FAIL;
769 }
770 else
771 {
772 SLOCK_ACQUIRE();
773 eThread[reqtype] = hThread;
774 SLOCK_RELEASE();
775 /*
776 * We call here _beginthread not _beginthreadex
777 * so as not to call CloseHandle() to clean up
778 * the thread.
779 */
780 _beginthread(ClosePrepareThread, 0, (void *) this);
781 }
782 ELOCK_RELEASE();
783 Release();
784 #undef return
785 return ret;
786 }
787 //
788 // Acquire/release [ELOCK -> ] SLOCK.
789 //
CommitRequest(DWORD grfRM,XACTUOW * pNewUOW)790 HRESULT STDMETHODCALLTYPE IAsyncPG::CommitRequest(DWORD grfRM, XACTUOW * pNewUOW)
791 {
792 HRESULT res = S_OK, ret = S_OK;
793 RequestPara *reqp;
794 const DWORD reqtype = CommitExec;
795
796 mylog("%p CommitRequest called grfRM=%d enl=%p\n", this, grfRM, enlist);
797
798 SLOCK_ACQUIRE();
799 if (!prepared || done)
800 ret = E_UNEXPECTED;
801 else if (S_OK != prepare_result)
802 ret = E_UNEXPECTED;
803 SLOCK_RELEASE();
804 if (S_OK != ret)
805 return ret;
806 #define DONT_CALL_RETURN_FROM_HERE ???
807 AddRef();
808 ELOCK_ACQUIRE();
809 #ifdef _SLEEP_FOR_TEST_
810 Sleep(1000);
811 #endif /* _SLEEP_FOR_TEST_ */
812 reqp = new RequestPara;
813 reqp->type = reqtype;
814 reqp->lpr = (LPVOID) this;
815 reqp->res = res;
816 enlist->AddRef();
817 HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, DtcRequestExec, reqp, 0, NULL);
818 if (NULL == hThread)
819 {
820 delete(reqp);
821 enlist->Release();
822 ret = E_FAIL;
823 }
824 else
825 {
826 SLOCK_ACQUIRE();
827 eThread[reqtype] = hThread;
828 SLOCK_RELEASE();
829 /*
830 * We call here _beginthread not _beginthreadex
831 * so as not to call CloseHandle() to clean up
832 * the thread.
833 */
834 _beginthread(CloseCommitThread, 0, (void *) this);
835 }
836 mylog("CommitRequest ret=%d\n", ret);
837 requestAccepted = true;
838 ELOCK_RELEASE();
839 Release();
840 #undef return
841 return ret;
842 }
843 //
844 // Acquire/release [ELOCK -> ] SLOCK.
845 //
AbortRequest(BOID * pboidReason,BOOL fRetaining,XACTUOW * pNewUOW)846 HRESULT STDMETHODCALLTYPE IAsyncPG::AbortRequest(BOID * pboidReason, BOOL fRetaining,
847 XACTUOW * pNewUOW)
848 {
849 HRESULT res = S_OK, ret = S_OK;
850 RequestPara *reqp;
851 const DWORD reqtype = AbortExec;
852
853 mylog("%p AbortRequest called\n", this);
854 SLOCK_ACQUIRE();
855 if (done)
856 ret = E_UNEXPECTED;
857 else if (prepared && S_OK != prepare_result)
858 ret = E_UNEXPECTED;
859 SLOCK_RELEASE();
860 if (S_OK != ret)
861 return ret;
862 #define return DONT_CALL_RETURN_FROM_HERE ???
863 AddRef();
864 ELOCK_ACQUIRE();
865 if (!prepared && dtcconn)
866 {
867 PgDtc_set_property(dtcconn, inprogress, (void *) 1);
868 PgDtc_one_phase_operation(dtcconn, ONE_PHASE_ROLLBACK);
869 PgDtc_set_property(dtcconn, inprogress, (void *) 0);
870 }
871 reqp = new RequestPara;
872 reqp->type = reqtype;
873 reqp->lpr = (LPVOID) this;
874 reqp->res = res;
875 enlist->AddRef();
876 HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, DtcRequestExec, reqp, 0, NULL);
877 if (NULL == hThread)
878 {
879 delete(reqp);
880 enlist->Release();
881 ret = E_FAIL;
882 }
883 else
884 {
885 SLOCK_ACQUIRE();
886 eThread[reqtype] = hThread;
887 SLOCK_RELEASE();
888 /*
889 * We call here _beginthread not _beginthreadex
890 * so as not to call CloseHandle() to clean up
891 * the thread.
892 */
893 _beginthread(CloseAbortThread, 0, (void *) this);
894 }
895 mylog("AbortRequest ret=%d\n", ret);
896 requestAccepted = true;
897 ELOCK_RELEASE();
898 Release();
899 #undef return
900 return ret;
901 }
TMDown(void)902 HRESULT STDMETHODCALLTYPE IAsyncPG::TMDown(void)
903 {
904 mylog("%p TMDown called\n", this);
905 return S_OK;
906 }
907
CloseThread(DWORD type)908 bool IAsyncPG::CloseThread(DWORD type)
909 {
910 CSTR func = "CloseThread";
911 HANDLE th;
912 DWORD ret, excode = S_OK;
913 bool rls_async = false;
914
915 mylog("%s for %p thread=%d\n", func, this, eThread[type]);
916 if (th = eThread[type], NULL == th || eFin[type])
917 return false;
918 ret = WaitForSingleObject(th, INFINITE);
919 if (WAIT_OBJECT_0 == ret)
920 {
921 switch (type)
922 {
923 case IAsyncPG::AbortExec:
924 case IAsyncPG::CommitExec:
925 rls_async = true;
926 break;
927 default:
928 GetExitCodeThread(th, &excode);
929 if (S_OK != excode)
930 rls_async = true;
931 }
932 SLOCK_ACQUIRE();
933 eThread[type] = NULL;
934 eFin[type] = true;
935 SLOCK_RELEASE();
936 CloseHandle(th);
937 }
938 mylog("%s ret=%d\n", func, ret);
939 return rls_async;
940 }
941
ClosePrepareThread(LPVOID para)942 EXTERN_C static void __cdecl ClosePrepareThread(LPVOID para)
943 {
944 CSTR func = "ClosePrepareThread";
945 IAsyncPG *async = (IAsyncPG *) para;
946 bool release;
947
948 mylog("%s for %p", func, async);
949 if (release = async->CloseThread(IAsyncPG::PrepareExec), release)
950 async->Release();
951 mylog("%s release=%d\n", func, release);
952 }
953
CloseCommitThread(LPVOID para)954 EXTERN_C static void __cdecl CloseCommitThread(LPVOID para)
955 {
956 CSTR func = "CloseCommitThread";
957 IAsyncPG *async = (IAsyncPG *) para;
958 bool release;
959
960 mylog("%s for %p", func, async);
961 if (release = async->CloseThread(IAsyncPG::CommitExec), release)
962 async->Release();
963 mylog("%s release=%d\n", func, release);
964 }
965
CloseAbortThread(LPVOID para)966 EXTERN_C static void __cdecl CloseAbortThread(LPVOID para)
967 {
968 CSTR func = "CloseAbortThread";
969 IAsyncPG *async = (IAsyncPG *) para;
970 bool release;
971
972 mylog("%s for %p", func, async);
973 if (release = async->CloseThread(IAsyncPG::AbortExec), release)
974 async->Release();
975 mylog("%s release=%d\n", func, release);
976 }
977
DtcRequestExec(LPVOID para)978 EXTERN_C static unsigned WINAPI DtcRequestExec(LPVOID para)
979 {
980 RequestPara *reqp = (RequestPara *) para;
981 DWORD type = reqp->type;
982 IAsyncPG *async = (IAsyncPG *) reqp->lpr;
983 HRESULT res = reqp->res, ret;
984
985 mylog("DtcRequestExec type=%d", reqp->type);
986 delete(reqp);
987 ret = async->RequestExec(type, res);
988 mylog(" Done ret=%d\n", ret);
989 return ret;
990 }
991
992 CSTR regKey = "SOFTWARE\\Microsoft\\MSDTC\\XADLL";
993
regkeyCheck(const char * xalibname,const char * xalibpath)994 static int regkeyCheck(const char *xalibname, const char *xalibpath)
995 {
996 int retcode = 0;
997 LONG ret;
998 HKEY sKey;
999 DWORD rSize;
1000
1001 ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, regKey, 0, KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_64KEY, &sKey);
1002 switch (ret)
1003 {
1004 case ERROR_SUCCESS:
1005 break;
1006 case ERROR_FILE_NOT_FOUND:
1007 ret = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, regKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &sKey, NULL);
1008 mylog("%s:CreateKeyEx ret=%d\n", __FUNCTION__, ret);
1009 break;
1010 default:
1011 mylog("%s:OpenKeyEx ret=%d\n", __FUNCTION__, ret);
1012 }
1013 if (ERROR_SUCCESS != ret)
1014 return -1;
1015 else
1016 {
1017 char keyval[1024];
1018
1019 rSize = sizeof(keyval);
1020 switch (ret = ::RegQueryValueEx(sKey, xalibname, NULL, NULL, (LPBYTE) keyval, &rSize))
1021 {
1022 case ERROR_SUCCESS:
1023 if (rSize > 0)
1024 {
1025 if (0 == _stricmp(keyval, xalibpath))
1026 break;
1027 mylog("%s:XADLL value %s is different from %s\n", __FUNCTION__, keyval, xalibpath);
1028 if (IsWow64())
1029 {
1030 mylog("%s:avoid RegSetValue operation from wow64 process\n", __FUNCTION__);
1031 break;
1032 }
1033 }
1034 case ERROR_FILE_NOT_FOUND:
1035 mylog("%s:Setting value %s\n", __FUNCTION__, xalibpath);
1036 ret = ::RegSetValueEx(sKey, xalibname, 0, REG_SZ, (CONST BYTE *) xalibpath, (DWORD) strlen(xalibpath) + 1);
1037 if (ERROR_SUCCESS == ret)
1038 retcode = 1;
1039 else
1040 {
1041 retcode = -1;
1042 mylog("%s:SetValuEx ret=%d\n", __FUNCTION__, ret);
1043 }
1044 break;
1045 default:
1046 retcode = -1;
1047 mylog("%s:QueryValuEx ret=%d\n", __FUNCTION__, ret);
1048 break;
1049 }
1050 ::RegCloseKey(sKey);
1051 }
1052 return retcode;
1053 }
1054
EnlistInDtc_1pipe(void * conn,ITransaction * pTra,ITransactionDispenser * pDtc,int method)1055 RETCODE static EnlistInDtc_1pipe(void *conn, ITransaction *pTra, ITransactionDispenser *pDtc, int method)
1056 {
1057 CSTR func = "EnlistInDtc_1pipe";
1058 static IDtcToXaHelperSinglePipe *pHelper = NULL;
1059 ITransactionResourceAsync *pRes = NULL;
1060 IAsyncPG *asdum;
1061 HRESULT res;
1062 DWORD dwRMCookie;
1063 XID xid;
1064 const char *xalibname = GetXaLibName();
1065 const char *xalibpath = GetXaLibPath();
1066
1067 int recovLvl;
1068 char errmsg[256];
1069 char reason[128];
1070
1071 if (!pHelper)
1072 {
1073 res = pDtc->QueryInterface(IID_IDtcToXaHelperSinglePipe, (void **) &pHelper);
1074 if (res != S_OK || !pHelper)
1075 {
1076 mylog("DtcToXaHelperSingelPipe get error %d\n", res);
1077 pHelper = NULL;
1078 return SQL_ERROR;
1079 }
1080 }
1081 res = (NULL != (asdum = new IAsyncPG)) ? S_OK : E_FAIL;
1082 if (S_OK != res)
1083 {
1084 mylog("CoCreateInstance error %d\n", res);
1085 return SQL_ERROR;
1086 }
1087
1088 recovLvl = PgDtc_is_recovery_available(conn, reason, sizeof(reason));
1089 switch (method)
1090 {
1091 case DTC_CHECK_BEFORE_LINK:
1092 if (0 == recovLvl)
1093 {
1094 snprintf(errmsg, sizeof(errmsg), "%s is unavailable in distributed transactions", reason);
1095 PgDtc_set_error(conn, errmsg, func);
1096 return SQL_ERROR;
1097 }
1098 }
1099 /*mylog("dllname=%s dsn=%s\n", xalibname, conn->connInfo.dsn); res = 0;*/
1100 char dtcname[1024];
1101 PgDtc_create_connect_string(conn, dtcname, sizeof(dtcname));
1102
1103 bool confirmedRegkey = false, confirmingLink = false, xarmerr = false;
1104 char error_header[64];
1105 while (true)
1106 {
1107 res = pHelper->XARMCreate(dtcname, (char *) xalibname, &dwRMCookie);
1108
1109 mylog("XARMcreate error code=%x (%d %d)\n", res, confirmedRegkey, confirmingLink);
1110 xarmerr = true;
1111 if (!confirmingLink)
1112 snprintf(error_header, sizeof(error_header), "XARMcreate error code=%x", res);
1113 switch (res)
1114 {
1115 case S_OK:
1116 if (confirmingLink)
1117 {
1118 switch (recovLvl)
1119 {
1120 case 0:
1121 snprintf(errmsg, sizeof(errmsg), "%s:%s is currently unavailable in distributed transactions", error_header, reason);
1122 break;
1123 case -1:
1124 snprintf(errmsg, sizeof(errmsg), "%s:Possibly you connect to the database whose authentication method is %s or ident", error_header, reason);
1125 break;
1126 case 1:
1127 snprintf(errmsg, sizeof(errmsg), "%s:Are you trying to connect to the database whose authentication method is ident?", error_header);
1128 break;
1129 }
1130 }
1131 else
1132 xarmerr = false;
1133 break;
1134 case XACT_E_XA_TX_DISABLED:
1135 snprintf(errmsg, sizeof(errmsg), "%s:Please enable XA transaction in MSDTC security configuration", error_header);
1136 break;
1137 case XACT_E_TMNOTAVAILABLE:
1138 snprintf(errmsg, sizeof(errmsg), "%s:Please start Distributed Transaction Coordinator service", error_header);
1139 break;
1140 case E_FAIL:
1141 if (!confirmedRegkey)
1142 {
1143 int retcode = regkeyCheck(xalibname, xalibpath);
1144 confirmedRegkey = true;
1145 if (retcode > 0)
1146 continue;
1147 }
1148 switch (method)
1149 {
1150 case DTC_CHECK_RM_CONNECTION:
1151 if (!confirmingLink)
1152 {
1153 confirmingLink = true;
1154 strcat(dtcname, ";" KEYWORD_DTC_CHECK "=0");
1155 continue;
1156 }
1157 default:
1158 snprintf(errmsg, sizeof(errmsg), "%s:Failed to link with DTC service. Please look at the log of Event Viewer etc.", error_header);
1159 }
1160 break;
1161 case XACT_E_CONNECTION_DOWN:
1162 snprintf(errmsg, sizeof(errmsg), "%s:Lost connection with DTC transaction manager\nMSDTC has some trouble?", error_header);
1163 break;
1164 default:
1165 snprintf(errmsg, sizeof(errmsg), "%s\n", error_header);
1166 break;
1167 }
1168 break;
1169 }
1170 if (xarmerr)
1171 {
1172 PgDtc_set_error(conn, errmsg, func);
1173 return SQL_ERROR;
1174 }
1175
1176 res = pHelper->ConvertTridToXID((DWORD *) pTra, dwRMCookie, &xid);
1177 if (res != S_OK)
1178 {
1179 mylog("ConvertTridToXid error %d\n", res);
1180 return SQL_ERROR;
1181 }
1182 {
1183 char pgxid[258];
1184 XidToText(xid, pgxid);
1185 mylog("ConvertTridToXID -> %s\n", pgxid);
1186 }
1187 asdum->SetXid(&xid);
1188 /* Create an IAsyncPG instance by myself */
1189 /* DLLGetClassObject(GUID_IAsyncPG, IID_ITransactionResourceAsync, (void **) &asdum); */
1190
1191 asdum->SetHelper(pHelper, dwRMCookie);
1192 res = pHelper->EnlistWithRM(dwRMCookie, pTra, asdum, &asdum->enlist);
1193 if (res != S_OK)
1194 {
1195 mylog("EnlistWithRM error %d\n", res);
1196 pHelper->ReleaseRMCookie(dwRMCookie, TRUE);
1197 return SQL_ERROR;
1198 }
1199
1200 mylog("asdum=%p start transaction\n", asdum);
1201 asdum->SetConnection(conn);
1202 LIFELOCK_ACQUIRE;
1203 PgDtc_set_async(conn, asdum);
1204 LIFELOCK_RELEASE;
1205
1206 return SQL_SUCCESS;
1207 }
1208
1209
1210 EXTERN_C RETCODE
IsolateDtcConn(void * conn,BOOL continueConnection)1211 IsolateDtcConn(void *conn, BOOL continueConnection)
1212 {
1213 IAsyncPG *async;
1214
1215 LIFELOCK_ACQUIRE;
1216 if (async = (IAsyncPG *) PgDtc_get_async(conn), NULL != async)
1217 {
1218 if (PgDtc_get_property(conn, idleInGlobalTransaction))
1219 {
1220 async->AddRef();
1221 LIFELOCK_RELEASE;
1222 async->separateXAConn(false, continueConnection ? true : false);
1223 async->Release();
1224 }
1225 else
1226 LIFELOCK_RELEASE;
1227 }
1228 else
1229 LIFELOCK_RELEASE;
1230 return SQL_SUCCESS;
1231 }
1232
1233
getITransactionDispenser(DWORD grfOptions,HRESULT * hres)1234 static ITransactionDispenser *getITransactionDispenser(DWORD grfOptions, HRESULT *hres)
1235 {
1236 static ITransactionDispenser *pDtc = NULL;
1237 HRESULT res = S_OK;
1238
1239 if (!pDtc)
1240 {
1241 res = DtcGetTransactionManagerEx(NULL, NULL, IID_ITransactionDispenser,
1242
1243 grfOptions, NULL, (void **) &pDtc);
1244 if (FAILED(res))
1245 {
1246 mylog("DtcGetTransactionManager error %x\n", res);
1247 pDtc = NULL;
1248 }
1249 }
1250 if (hres)
1251 *hres = res;
1252
1253 return pDtc;
1254 }
1255
GetTransactionObject(HRESULT * hres)1256 EXTERN_C void *GetTransactionObject(HRESULT *hres)
1257 {
1258
1259 ITransaction *pTra = NULL;
1260 ITransactionDispenser *pDtc = NULL;
1261
1262 if (pDtc = getITransactionDispenser(OLE_TM_FLAG_NONE, hres), NULL == pDtc)
1263 return pTra;
1264 HRESULT res = pDtc->BeginTransaction(NULL, ISOLATIONLEVEL_READCOMMITTED,
1265 0, NULL, &pTra);
1266 switch (res)
1267 {
1268 case S_OK:
1269 break;
1270 default:
1271 pTra = NULL;
1272 }
1273 if (hres)
1274 *hres = res;
1275 return pTra;
1276 }
1277
ReleaseTransactionObject(void * pObj)1278 EXTERN_C void ReleaseTransactionObject(void *pObj)
1279 {
1280 ITransaction *pTra = (ITransaction *) pObj;
1281
1282 if (!pTra) return;
1283 pTra->Release();
1284 }
1285
EnlistInDtc(void * conn,void * pTra,int method)1286 EXTERN_C RETCODE EnlistInDtc(void *conn, void *pTra, int method)
1287 {
1288 ITransactionDispenser *pDtc = NULL;
1289 RETCODE ret;
1290
1291 if (!pTra)
1292 {
1293 IAsyncPG *asdum = (IAsyncPG *) PgDtc_get_async(conn);
1294 PgDtc_set_property(conn, enlisted, (void *) 0);
1295 return SQL_SUCCESS;
1296 }
1297 if (CONN_IS_IN_TRANS(conn))
1298 {
1299 PgDtc_one_phase_operation(conn, SHUTDOWN_LOCAL_TRANSACTION);
1300 }
1301 HRESULT hres;
1302 pDtc = getITransactionDispenser(OLE_TM_FLAG_NODEMANDSTART, &hres);
1303 if (!pDtc)
1304 {
1305 char errmsg[128];
1306 snprintf(errmsg, sizeof(errmsg), "enlistment error:DtcGetTransactionManager error code=%x", hres);
1307 PgDtc_set_error(conn, errmsg, __FUNCTION__);
1308 return SQL_ERROR;
1309 }
1310 ret = EnlistInDtc_1pipe(conn, (ITransaction *) pTra, pDtc, method);
1311 if (SQL_SUCCEEDED(ret))
1312 PgDtc_set_property(conn, enlisted, (void *) 1);
1313 return ret;
1314 }
1315
DtcOnDisconnect(void * conn)1316 EXTERN_C RETCODE DtcOnDisconnect(void *conn)
1317 {
1318 mylog("DtcOnDisconnect\n");
1319 LIFELOCK_ACQUIRE;
1320 IAsyncPG *asdum = (IAsyncPG *) PgDtc_get_async(conn);
1321 if (asdum)
1322 {
1323 asdum->AddRef();
1324 LIFELOCK_RELEASE;
1325 asdum->ReleaseConnection();
1326 asdum->Release();
1327 }
1328 else
1329 LIFELOCK_RELEASE;
1330 return SQL_SUCCESS;
1331 }
1332
1333 #endif /* _HANDLE_ENLIST_IN_DTC_ */
1334