//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1994. // // File: coord.cxx // // Contents: Transaction Coordinator implementation // // Classes: // // Functions: // // History: 03-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- #include "xacthead.cxx" #pragma hdrstop #include #include "coord.hxx" #include "enlist.hxx" #include "xactenum.hxx" #include "xactdisp.hxx" #if 0 //Instantiate so we can make sure that all the methods are // properly declared. //CTransactionCoordinator tcFoo; #endif //BUGBUG: GetLock macro should return a unique error code when it detects // the busy condition, but we don't have one defined yet. #define GetLock() EnterCriticalSection(&_cs); \ if (_dwLockCount != 0) \ { \ if (GetCurrentThreadId() != _dwThread) \ { \ LeaveCriticalSection(&_cs); \ return STG_E_INUSE; \ } \ else \ { \ _dwLockCount++; \ } \ } \ else \ { \ _dwLockCount = 1; \ _dwThread = GetCurrentThreadId(); \ } \ LeaveCriticalSection(&_cs); #define ReleaseLock() EnterCriticalSection(&_cs); \ if (--_dwLockCount == 0) \ { \ _dwThread = 0; \ } \ LeaveCriticalSection(&_cs); //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::CConnectionPoint, public // // Synopsis: Constructor // // Arguments: [type] -- Type supported by connection point // // History: 28-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- CConnectionPoint::CConnectionPoint() { xactDebugOut((DEB_ITRACE, "In CConnectionPoint::CConnectionPoint:%p()\n", this)); _cReferences = 1; _dwCookie = 0; _pxlHead = NULL; xactDebugOut((DEB_ITRACE, "Out CConnectionPoint::CConnectionPoint\n")); } void CConnectionPoint::Init(XACTTYPE type, CTransactionCoordinator *ptc) { _type = type; _ptc = ptc; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::QueryInterface, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CConnectionPoint::QueryInterface(REFIID iid, void **ppvObj) { SCODE sc = S_OK; xactDebugOut((DEB_TRACE, "In CConnectionPoint::QueryInterface:%p()\n", this)); *ppvObj = NULL; if ((IsEqualIID(iid, IID_IUnknown)) || (IsEqualIID(iid, IID_IConnectionPoint))) { *ppvObj = (IConnectionPoint *)this; CConnectionPoint::AddRef(); } else { return E_NOINTERFACE; } xactDebugOut((DEB_TRACE, "Out CConnectionPoint::QueryInterface\n")); return ResultFromScode(sc); } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::AddRef, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CConnectionPoint::AddRef(void) { ULONG ulRet; xactDebugOut((DEB_TRACE, "In CConnectionPoint::AddRef:%p()\n", this)); InterlockedIncrement(&_cReferences); ulRet = _cReferences; xactDebugOut((DEB_TRACE, "Out CConnectionPoint::AddRef\n")); return ulRet; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::Release, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CConnectionPoint::Release(void) { LONG lRet; xactDebugOut((DEB_TRACE, "In CConnectionPoint::Release:%p()\n", this)); xactAssert(_cReferences > 0); lRet = InterlockedDecrement(&_cReferences); if (lRet == 0) { xactAssert((lRet > 0) && "Connection point released too many times."); } else if (lRet < 0) lRet = 0; xactDebugOut((DEB_TRACE, "Out CConnectionPoint::Release\n")); return (ULONG)lRet; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::GetConnectionInterface, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 24-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CConnectionPoint::GetConnectionInterface(IID *pIID) { xactDebugOut((DEB_ITRACE, "In CConnectionPoint::GetConnectionInterface:%p()\n", this)); if (_type == Adjust) { *pIID = IID_ITransactionAdjustEvents; } else if (_type == Veto) { *pIID = IID_ITransactionVetoEvents; } else { *pIID = IID_ITransactionOutcomeEvents; } xactDebugOut((DEB_ITRACE, "Out CConnectionPoint::GetConnectionInterface\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::GetConnectionPointContainer, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 24-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CConnectionPoint::GetConnectionPointContainer( IConnectionPointContainer ** ppCPC) { xactDebugOut((DEB_ITRACE, "In CConnectionPoint::GetConnectionPointContainer:%p()\n", this)); *ppCPC = (IConnectionPointContainer *)_ptc; _ptc->AddRef(); xactDebugOut((DEB_ITRACE, "Out CConnectionPoint::GetConnectionPointContainer\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::Advise, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 24-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CConnectionPoint::Advise(IUnknown *pUnkSink, DWORD *pdwCookie) { SCODE sc; CXactList *pxlTemp = NULL; CXactList **ppxlHead = NULL; void *pv = NULL; xactDebugOut((DEB_ITRACE, "In CConnectionPoint::Advise:%p()\n", this)); ITransactionAdjustEvents *pAdjust; ITransactionVetoEvents *pVeto; ITransactionOutcomeEvents *pOutcome; //BUGBUG: Multithread access xactMem(pxlTemp = new CXactList); //Note: The QueryInterface will give us a reference to hold on to. if (_type == Adjust) { xactChk(pUnkSink->QueryInterface(IID_ITransactionAdjustEvents, &pv)); pxlTemp->SetAdjust((ITransactionAdjustEvents *)pv); } else if (_type == Veto) { xactChk(pUnkSink->QueryInterface(IID_ITransactionVetoEvents, &pv)); pxlTemp->SetVeto((ITransactionVetoEvents *)pv); } else { xactChk(pUnkSink->QueryInterface(IID_ITransactionOutcomeEvents, &pv)); pxlTemp->SetOutcome((ITransactionOutcomeEvents *)pv); } pxlTemp->SetNext(_pxlHead); *pdwCookie = ++_dwCookie; pxlTemp->SetFlags(*pdwCookie); _pxlHead = pxlTemp; xactDebugOut((DEB_ITRACE, "Out CConnectionPoint::Advise\n")); return sc; Err: delete pxlTemp; return sc; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::Unadvise, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 24-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CConnectionPoint::Unadvise(DWORD dwCookie) { CXactList *pxlTemp; CXactList *pxlPrev; xactDebugOut((DEB_ITRACE, "In CConnectionPoint::Unadvise:%p()\n", this)); pxlTemp = _pxlHead; pxlPrev = NULL; while ((pxlTemp != NULL) && (pxlTemp->GetFlags() != dwCookie)) { pxlPrev = pxlTemp; pxlTemp = pxlTemp->GetNext(); } if (pxlTemp != NULL) { //Found the sink. Delete it from the list. if (pxlPrev != NULL) { pxlPrev->SetNext(pxlTemp->GetNext()); } else { _pxlHead = pxlTemp->GetNext(); } if (_type == Adjust) { pxlTemp->GetAdjust()->Release(); } else if (_type == Veto) { pxlTemp->GetVeto()->Release(); } else { pxlTemp->GetOutcome()->Release(); } delete pxlTemp; } else //Client passed in unknown cookie. return E_UNEXPECTED; xactDebugOut((DEB_ITRACE, "Out CConnectionPoint::Unadvise\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::EnumConnections, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 24-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CConnectionPoint::EnumConnections( IEnumConnections **ppEnum) { xactDebugOut((DEB_ITRACE, "In CConnectionPoint::EnumConnections:%p()\n", this)); xactDebugOut((DEB_ITRACE, "Out CConnectionPoint::EnumConnections\n")); return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CConnectionPoint::CloseConnections, public // // Synopsis: Close all connections // // Arguments: none // // Returns: void // // History: 31-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- void CConnectionPoint::CloseConnections(void) { xactDebugOut((DEB_ITRACE, "In CConnectionPoint::CloseConnections:%p()\n", this)); CXactList *pxlTemp = _pxlHead; CXactList *pxlPrev; while (pxlTemp != NULL) { pxlPrev = pxlTemp; if (_type == Adjust) { pxlTemp->GetAdjust()->Release(); } else if (_type == Veto) { pxlTemp->GetVeto()->Release(); } else { pxlTemp->GetOutcome()->Release(); } pxlTemp = pxlTemp->GetNext(); delete pxlPrev; } _pxlHead = NULL; xactDebugOut((DEB_ITRACE, "Out CConnectionPoint::CloseConnections\n")); } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::CTransactionCoordinator, public // // Synopsis: Default constructor // // Returns: Appropriate status code // // History: 05-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- CTransactionCoordinator::CTransactionCoordinator(CTransactionDispenser *ptd, IUnknown *punkOuter, ISOLEVEL isoLevel, ULONG isoFlags, ULONG ulTimeout) { _cReferences = 1; _punkOuter = punkOuter; _ptd = ptd; _ptd->AddRef(); _cpAdjust.Init(Adjust, this); _cpVeto.Init(Veto, this); _cpOutcome.Init(Outcome, this); //BUGBUG: This is a HACK. It works only because // ITransactionCompletionEvents is identical to // ITransactionOutcomeEvents _cpCompletion.Init(Outcome, this); _pxlResources = NULL; _pxlSinglePhase = NULL; _cPreventCommit = 0; _xactInfo.uow = BOID_NULL; //BUGBUG: grfTCSupported should include async when we do async. _xactInfo.grfTCSupported = XACTTC_DONTAUTOABORT | XACTTC_TRYALLRESOURCES; _xactInfo.grfRMSupported = 0; _xactInfo.grfTCSupportedRetaining = _xactInfo.grfTCSupported; _xactInfo.grfRMSupportedRetaining = _xactInfo.grfRMSupported; _xactInfo.isoLevel = isoLevel; _xactInfo.isoFlags = isoFlags; _fValidTransaction = FALSE; _ulTimeout = ulTimeout; InitializeCriticalSection(&_cs); _dwLockCount = 0; _dwThread = 0; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::~CTransactionCoordinator, public // // Synopsis: Destructor // // History: 25-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- CTransactionCoordinator::~CTransactionCoordinator() { CloseTransaction(); _ptd->Defect(this); _ptd->Release(); DeleteCriticalSection(&_cs); } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::Init, public // // Synopsis: Initialize coordinator instance // // Returns: Appropriate status code // // History: 25-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- SCODE CTransactionCoordinator::Init(void) { SCODE sc; xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::Init:%p()\n", this)); sc = CoCreateGuid((GUID *)&(_xactInfo.uow)); if (SUCCEEDED(sc)) { _fValidTransaction = TRUE; } xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::Init\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::QueryInterface, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::QueryInterface(REFIID iid, void **ppvObj) { SCODE sc = S_OK; xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::QueryInterface:%p()\n", this)); *ppvObj = NULL; if ((IsEqualIID(iid, IID_IUnknown)) || (IsEqualIID(iid, IID_ITransaction))) { *ppvObj = (ITransaction *)this; } else if (IsEqualIID(iid, IID_ITransactionNested)) { *ppvObj = (ITransactionNested *)this; } else if (IsEqualIID(iid, IID_ITransactionCoordinator)) { *ppvObj = (ITransactionCoordinator *)this; } else if (IsEqualIID(iid, IID_ITransactionControl)) { *ppvObj = (ITransactionControl *)this; } else if (IsEqualIID(iid, IID_IConnectionPointContainer)) { *ppvObj = (IConnectionPointContainer *)this; } else { return _punkOuter->QueryInterface(iid, ppvObj); } if (SUCCEEDED(sc)) { CTransactionCoordinator::AddRef(); } xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::QueryInterface\n")); return ResultFromScode(sc); } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::AddRef, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CTransactionCoordinator::AddRef(void) { ULONG ulRet; xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::AddRef:%p()\n", this)); InterlockedIncrement(&_cReferences); ulRet = _cReferences; xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::AddRef\n")); return ulRet; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::Release, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CTransactionCoordinator::Release(void) { LONG lRet; xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::Release:%p()\n", this)); xactAssert(_cReferences > 0); lRet = InterlockedDecrement(&_cReferences); if (lRet == 0) { delete this; } else if (lRet < 0) lRet = 0; xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::Release\n")); return (ULONG)lRet; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::CloseTransaction, private // // Synopsis: Close the current transaction, defecting all resources. // // Arguments: None. // // Returns: Appropriate status code // // History: 25-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- SCODE CTransactionCoordinator::CloseTransaction(void) { SCODE sc; xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::CloseTransaction:%p()\n", this)); _fValidTransaction = FALSE; CXactList *pxlTemp = _pxlResources; CXactList *pxlLast; while (pxlTemp != NULL) { pxlLast = pxlTemp; sc = pxlTemp->GetResource()->Defect(FALSE); if (FAILED(sc)) { //BUGBUG: Do what? We don't want to stop defecting. } pxlTemp->GetEnlistment()->Release(); pxlTemp = pxlTemp->GetNext(); delete pxlLast; } _pxlResources = NULL; if (_pxlSinglePhase != NULL) { _pxlSinglePhase->GetTransaction()->Release(); } _pxlSinglePhase = NULL; _cpAdjust.CloseConnections(); _cpVeto.CloseConnections(); _cpOutcome.CloseConnections(); xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::CloseTransaction\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::Phase1Worker, public // // Synopsis: Worker function for phase 1 of a commit call // // Arguments: [psc] -- Pointer to commit struct containing parameters. // // Returns: Appropriate status code // // History: 27-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- SCODE CTransactionCoordinator::Phase1Worker(SCommitStruct *pcs) { SCODE sc; SCODE scErr = S_OK; xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::Phase1Worker:%p()\n", this)); //Note: This function does not release the lock in the success case. // It relies of Phase2Worker to release it. GetLock(); CXactList *pxlTemp = _cpAdjust.GetHead(); if (!IsValid()) { xactErr(Err, XACT_E_NOTRANSACTION); } if (_cPreventCommit != 0) { xactErr(Err, XACT_E_COMMITPREVENTED); } //Update statistics on dispenser to indicate that we're now committing. InterlockedIncrement((LONG *)&(_ptd->GetStats()->cCommitting)); //1) For each sink registered for ITransactionAdjustEvents, call // OnPrePrepareAdjust() while (pxlTemp != NULL) { xactChk(pxlTemp->GetAdjust()->OnPrePrepareAdjust(pcs->fRetaining)); pxlTemp = pxlTemp->GetNext(); } //2) If all succeeded, then for each sink registered for // ITransactionVetoEvents, call OnPrePrepare. pxlTemp = _cpVeto.GetHead(); while (pxlTemp != NULL) { xactChk(pxlTemp->GetVeto()->OnPrePrepare(pcs->fRetaining)); pxlTemp = pxlTemp->GetNext(); } //3) Prepare each resource. pxlTemp = _pxlResources; while (pxlTemp != NULL) { XACTSTAT xactstat = pxlTemp->GetState(); if ((xactstat == XACTSTAT_OPEN) || (xactstat == XACTSTAT_PREPARED)) { sc = pxlTemp->GetResource()->Prepare(pcs->fRetaining, 0, FALSE, //BUGBUG: Get moniker back. // for recovery. NULL, NULL); if (SUCCEEDED(sc)) { pxlTemp->SetState(XACTSTAT_PREPARED); } } else if ((xactstat == XACTSTAT_HEURISTIC_DAMAGE) || (xactstat == XACTSTAT_HEURISTIC_DANGER)) { sc = XACT_E_HEURISTICDAMAGE; } if (FAILED(sc) && !(pcs->grfTC & XACTTC_TRYALLRESOURCES)) { xactChk(sc); } else if (FAILED(sc)) { scErr = sc; } pxlTemp = pxlTemp->GetNext(); } sc = scErr; xactChk(sc); xactChk(CoCreateGuid((GUID *)&(pcs->uowNew))); //All the prepares succeeded. Commit the single phase guy, if there // if one. if (_pxlSinglePhase != NULL) { xactChk(_pxlSinglePhase->GetTransaction()->Commit(pcs->fRetaining, pcs->grfTC, pcs->grfRM)); } xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::Phase1Worker\n")); return sc; Err: if (!(pcs->grfTC & XACTTC_DONTAUTOABORT)) { //The user didn't specify auto-abort, so we abort the transaction. // Note that the retaining semantics are still specified by the // user. CTransactionCoordinator::Abort(NULL, pcs->fRetaining, FALSE); } delete pcs; ReleaseLock(); return sc; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::Phase2Worker, public // // Synopsis: Worker function for phase 2 of a commit // // Arguments: [pcs] -- Pointer to commitstruct containing parameters // // Returns: Appropriate status code // // History: 27-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- SCODE CTransactionCoordinator::Phase2Worker(SCommitStruct *pcs) { //Note: This function does not call GetLock(). It relies on Phase1Worker // to call it. SCODE sc; xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::Phase2Worker:%p()\n", this)); //For each sink registered for OutcomeEvents, call OnCommit CXactList *pxlTemp = _cpOutcome.GetHead(); while (pxlTemp != NULL) { sc = pxlTemp->GetOutcome()->OnCommit(pcs->fRetaining, (pcs->fRetaining) ? &(pcs->uowNew) : NULL, S_OK); pxlTemp = pxlTemp->GetNext(); } pxlTemp = _pxlResources; while (pxlTemp != NULL) { sc = pxlTemp->GetResource()->Commit(pcs->grfRM, (pcs->fRetaining) ? &(pcs->uowNew) : NULL); xactAssert(SUCCEEDED(sc)); pxlTemp->SetState(XACTSTAT_COMMITTED); pxlTemp = pxlTemp->GetNext(); } //For each sink registered for CompletionEvents, call OnCommit //BUGBUG: Only works because OutcomeEvents and CompletionEvents // are identical. pxlTemp = _cpCompletion.GetHead(); while (pxlTemp != NULL) { sc = pxlTemp->GetOutcome()->OnCommit(pcs->fRetaining, (pcs->fRetaining) ? &(pcs->uowNew) : NULL, sc); pxlTemp = pxlTemp->GetNext(); } //Update statistics to indicate that we are no longer committing, but // have successfully committed. InterlockedDecrement((LONG *)&(_ptd->GetStats()->cCommitting)); InterlockedIncrement((LONG *)&(_ptd->GetStats()->cCommitted)); if (!pcs->fRetaining) { CloseTransaction(); } else { pxlTemp = _pxlResources; while (pxlTemp != NULL) { pxlTemp->SetState(XACTSTAT_OPEN); pxlTemp = pxlTemp->GetNext(); } _xactInfo.uow = pcs->uowNew; } xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::Phase2Worker\n")); delete pcs; ReleaseLock(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::Commit, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::Commit(BOOL fRetaining, DWORD grfTC, DWORD grfRM) { SCODE sc = S_OK; xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::Commit:%p()\n", this)); SCommitStruct *pcs; xactMem(pcs = new SCommitStruct); pcs->fRetaining = fRetaining; pcs->grfTC = grfTC; pcs->grfRM = grfRM; if (grfTC & XACTTC_ASYNC) { ULONG h; //Spawn thread. We use _beginthread because the code in the resources // may use the C runtime. h = _beginthread(Phase1WorkerThreadEntryPoint, 0, pcs); if (h == -1) { grfTC = grfTC & ~XACTTC_ASYNC; } else { return XACT_S_ASYNC; } } if (!(grfTC & XACTTC_ASYNC)) { xactChk(Phase1Worker(pcs)); } if (grfTC & XACTTC_SYNC_PHASEONE) { //Spawn thread. ULONG h = _beginthread(Phase2WorkerThreadEntryPoint, 0, pcs); if (h == -1) { grfTC = grfTC & ~XACTTC_SYNC_PHASEONE; } else { return sc; } } if (!(grfTC & XACTTC_SYNC_PHASEONE)) { xactChk(Phase2Worker(pcs)); } xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::Commit\n")); Err: return sc; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::Abort, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::Abort(BOID *pboidReason, BOOL fRetaining, BOOL fAsync) { //BUGBUG: This need to do something different for retaining aborts, // since we can't really support them in docfile. We need to gracefully // handle resources that cannot support fRetaining == TRUE. SCODE sc = S_OK; GetLock(); if (!IsValid()) { ReleaseLock(); return XACT_E_NOTRANSACTION; } //BUGBUG: Handle Async xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::Abort:%p()\n", this)); CXactList *pxlTemp; XACTUOW uow; //Update statistics to show that we're aborting. InterlockedIncrement((LONG *)&(_ptd->GetStats()->cAborting)); if (fRetaining) { sc = CoCreateGuid((GUID *)&uow); if (FAILED(sc)) return sc; } pxlTemp = _pxlResources; while (pxlTemp != NULL) { pxlTemp->GetResource()->Abort( pboidReason, fRetaining, (fRetaining) ? &uow : NULL); pxlTemp->SetState(XACTSTAT_OPEN); pxlTemp = pxlTemp->GetNext(); } pxlTemp = _cpOutcome.GetHead(); while (pxlTemp != NULL) { pxlTemp->GetOutcome()->OnAbort(pboidReason, fRetaining, (fRetaining) ? &uow : NULL, S_OK); pxlTemp = pxlTemp->GetNext(); } //BUGBUG: Only works because Outcome and Completion events are // identical. pxlTemp = _cpCompletion.GetHead(); while (pxlTemp != NULL) { pxlTemp->GetOutcome()->OnAbort(pboidReason, fRetaining, (fRetaining) ? &uow : NULL, S_OK); pxlTemp = pxlTemp->GetNext(); } //Now we're no longer aborting, so update statistics. InterlockedDecrement((LONG *)&(_ptd->GetStats()->cAborting)); InterlockedIncrement((LONG *)&(_ptd->GetStats()->cAborted)); if (!fRetaining) { CloseTransaction(); } _xactInfo.uow = uow; xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::Abort\n")); ReleaseLock(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::GetTransactionInfo, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::GetTransactionInfo(XACTTRANSINFO *pinfo) { xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::GetTransactionInfo:%p()\n", this)); if (!IsValid()) { return XACT_E_NOTRANSACTION; } *pinfo = _xactInfo; xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::GetTransactionInfo\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::GetParent, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::GetParent(REFIID iid, void **ppvParent) { xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::GetParent:%p()\n", this)); xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::GetParent\n")); //We don't currently support nesting this coordinator, so always return // E_FAIL to indicate no parent transaction. return E_FAIL; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::Enlist, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::Enlist(IUnknown *pResource, DWORD grfRMTC, XACTRMGRID *prmgrid, XACTTRANSINFO *pinfo, DWORD *pgrfTCRMENLIST, ITransactionEnlistment **ppEnlist) { xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::Enlist:%p()\n", this)); SCODE sc; CXactList *pxl = NULL; CTransactionEnlistment *pte = NULL; ITransactionResource *ptr = NULL; GetLock(); if (!IsValid()) { xactErr(Err, XACT_E_NOTRANSACTION); } xactChkTo(Err_QI, pResource->QueryInterface(IID_ITransactionResource, (void **)&ptr)); xactMem(pxl = new CXactList); xactMem(pte = new CTransactionEnlistment(pxl, this)); pxl->SetNext(_pxlResources); _pxlResources = pxl; pxl->SetResource(ptr); pxl->SetFlags(grfRMTC); pxl->SetRMGRID(prmgrid); pxl->SetEnlistment(pte); *ppEnlist = pte; //We are always active recovering. *pgrfTCRMENLIST = XACTTCRMENLIST_IAMACTIVE; *pinfo = _xactInfo; xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::Enlist\n")); ReleaseLock(); return S_OK; Err: delete pxl; delete pte; Err_QI: if (ptr != NULL) ptr->Release(); ReleaseLock(); return sc; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::EnlistSinglePhase, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::EnlistSinglePhase( ITransaction *pResource, DWORD grfRMTC, XACTRMGRID *prmgrid, XACTTRANSINFO *pinfo, DWORD *pgrfTCRMENLIST, ITransactionEnlistment **ppEnlist) { xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::EnlistSinglePhase:%p()\n", this)); SCODE sc; CXactList *pxl = NULL; CTransactionEnlistment *pte = NULL; GetLock(); if (!IsValid()) { xactErr(Err, XACT_E_NOTRANSACTION); } if (_pxlSinglePhase != NULL) { xactErr(Err, XACT_E_ALREADYOTHERSINGLEPHASE); } xactMem(pxl = new CXactList); xactMem(pte = new CTransactionEnlistment(pxl, this)); _pxlSinglePhase = pxl; pxl->SetTransaction(pResource); pxl->SetFlags(grfRMTC); pxl->SetRMGRID(prmgrid); pxl->SetEnlistment(pte); *ppEnlist = pte; //We are always active recovering. *pgrfTCRMENLIST = XACTTCRMENLIST_IAMACTIVE; *pinfo = _xactInfo; xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::EnlistSinglePhase\n")); ReleaseLock(); return S_OK; Err: delete pxl; delete pte; ReleaseLock(); return sc; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::EnumResources, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::EnumResources(IEnumXACTRE **ppenum) { SCODE sc; xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::EnumResources:%p()\n", this)); if (!IsValid()) { return XACT_E_NOTRANSACTION; } xactMem(*ppenum = new CResourceEnum(this)); xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::EnumResources\n")); Err: return sc; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::GetStatus, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 15-Aug-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::GetStatus(DWORD *pdwStatus) { xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::GetStatus:%p()\n", this)); xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::GetStatus\n")); return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::SetTimeout, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::SetTimeout(ULONG ulTimeout) { xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::SetTimeout:%p()\n", this)); if (!IsValid()) { return XACT_E_NOTRANSACTION; } _ulTimeout = ulTimeout; xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::SetTimeout\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::PreventCommit, public // // Synopsis: // // Arguments: // // Returns: Appropriate status code // // Modifies: // // History: 05-Jul-95 PhilipLa Created // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::PreventCommit(BOOL fPrevent) { xactDebugOut((DEB_TRACE, "In CTransactionCoordinator::PreventCommit:%p()\n", this)); GetLock(); if (!IsValid()) { ReleaseLock(); return XACT_E_NOTRANSACTION; } _cPreventCommit += (fPrevent) ? 1 : -1; ReleaseLock(); xactDebugOut((DEB_TRACE, "Out CTransactionCoordinator::PreventCommit\n")); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::DefectResource, public // // Synopsis: Internal method used to remove resources from resource // list. // // Arguments: [pxlResource] -- Pointer to resource to defect. // // Returns: Appropriate status code // // History: 26-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- SCODE CTransactionCoordinator::DefectResource(CXactList *pxlResource) { xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::DefectResource:%p()\n", this)); //First find the resource in the list, as well as the previous resource. CXactList *pxlPrev = NULL; GetLock(); CXactList *pxlTemp = _pxlResources; while (pxlTemp != pxlResource) { pxlPrev = pxlTemp; pxlTemp = pxlTemp->GetNext(); } xactAssert(pxlTemp != NULL); //Now remove it from the list. if (pxlPrev == NULL) { _pxlResources = pxlTemp->GetNext(); } else { pxlPrev->SetNext(pxlTemp->GetNext()); } pxlTemp->GetEnlistment()->Release(); delete pxlTemp; xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::DefectResource\n")); ReleaseLock(); return S_OK; } void _cdecl Phase1WorkerThreadEntryPoint(void *pcs) { SCODE sc = ((SCommitStruct *)pcs)->ptc->Phase1Worker((SCommitStruct *)pcs); if (SUCCEEDED(sc)) { ((SCommitStruct *)pcs)->ptc->Phase2Worker((SCommitStruct *)pcs); } } void _cdecl Phase2WorkerThreadEntryPoint(void *pcs) { ((SCommitStruct *)pcs)->ptc->Phase2Worker((SCommitStruct *)pcs); } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::EnumConnectionPoints, public // // Synopsis: Return enumerator on connection points // // Arguments: [ppEnum] -- Return pointer of enumerator // // Returns: Appropriate status code // // History: 28-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::EnumConnectionPoints( IEnumConnectionPoints **ppEnum) { xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::EnumConnectionPoints:%p()\n", this)); xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::EnumConnectionPoints\n")); return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CTransactionCoordinator::FindConnectionPoint, public // // Synopsis: Return a connection point given an IID // // Arguments: [iid] -- IID to return connection point for // [ppCP] -- Return location for pointer // // Returns: Appropriate status code // // History: 28-Jul-95 PhilipLa Created // //---------------------------------------------------------------------------- STDMETHODIMP CTransactionCoordinator::FindConnectionPoint( REFIID iid, IConnectionPoint **ppCP) { xactDebugOut((DEB_ITRACE, "In CTransactionCoordinator::FindConnectionPoint:%p()\n", this)); CConnectionPoint *pcp; if (IsEqualIID(iid, IID_ITransactionAdjustEvents)) { pcp = &_cpAdjust; } else if (IsEqualIID(iid, IID_ITransactionVetoEvents)) { pcp = &_cpVeto; } else if (IsEqualIID(iid, IID_ITransactionOutcomeEvents)) { pcp = &_cpOutcome; } else { *ppCP = NULL; return E_NOINTERFACE; } pcp->AddRef(); *ppCP = pcp; xactDebugOut((DEB_ITRACE, "Out CTransactionCoordinator::FindConnectionPoint\n")); return S_OK; }