NT4/private/ole32/stg/xact/coord.cxx
2020-09-30 17:12:29 +02:00

1695 lines
41 KiB
C++

//+---------------------------------------------------------------------------
//
// 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 <process.h>
#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;
}