|
|
//----------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2000
//
// File: rownotfy.cxx
//
// Contents: Rowset notification connection points
//
// Classes: CRowsetNotification
// CRowsetAsynchNotification
//
// History: 16 Feb 1998 AlanW Created from conpt.cxx
//
//----------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include <rownotfy.hxx>
#include <query.hxx>
#include "tabledbg.hxx"
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//////////////// CRowsetAsynchNotification methods ///////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::CRowsetAsynchNotification, public
//
// Synopsis: Constructor for connection point container class.
//
// Arguments: [query] -- query object with notify info
// [pRowset] -- Rowset pointer
// [ErrorObject] -- OLE-DB error object
// [fWatch] -- TRUE if watch notification to be done
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
CRowsetAsynchNotification::CRowsetAsynchNotification( PQuery & query, ULONG hCursor, IRowset * pRowset, CCIOleDBError & ErrorObject, BOOL fWatch) : _query(query), _hCursor(hCursor), CRowsetNotification( ), _AsynchConnectionPoint ( IID_IDBAsynchNotify ), _WatchConnectionPoint ( IID_IRowsetWatchNotify ), _fDoWatch (fWatch), _fPopulationComplete (FALSE), _cAdvise( 0 ), _pRowset(pRowset), _threadNotify(0), _threadNotifyId( 0 ) { _AsynchConnectionPoint.SetContrUnk( (IUnknown *)this ); if (_fDoWatch) { _WatchConnectionPoint.SetContrUnk( (IUnknown *)this ); } } //CRowsetAsynchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::~CRowsetAsynchNotification, public
//
// Synopsis: Destructor for rowset watch notification class
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
CRowsetAsynchNotification::~CRowsetAsynchNotification() { Win4Assert( _cRefs == 0 && _pContainer == 0 ); Win4Assert( _cAdvise == 0 );
if ( 0 != _pContainer ) StopNotifications();
} //~CRowsetAsynchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::AddRef, public
//
// Synopsis: Increments aggregated object ref. count.
//
// Returns: ULONG
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRowsetAsynchNotification::AddRef() { return InterlockedIncrement( (long *) &_cRefs ); } //AddRef
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::Release, public
//
// Synopsis: Decrements aggregated obj. ref. count, deletes on final release.
//
// Returns: ULONG
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRowsetAsynchNotification::Release() { long cRefs = InterlockedDecrement((long *) &_cRefs);
tbDebugOut(( DEB_NOTIFY, "conpt: release, new crefs: %lx\n", _cRefs ));
// If no references, make sure container doesn't know about me anymore
if ( 0 == cRefs ) { Win4Assert( 0 == _pContainer );
if ( 0 != _pContainer ) { // must have gotten here through excess client release
StopNotifications(); }
delete this; }
return cRefs; } //Release
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::StopNotifications
//
// Synopsis: Shuts down the notification thread (if any)
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::StopNotifications() { if ( GetCurrentThreadId() == _threadNotifyId ) { Win4Assert( !"Notification thread used illegally" ); return; }
_EndNotifyThread();
Disconnect(); _AsynchConnectionPoint.Disconnect( );
if (_fDoWatch) _WatchConnectionPoint.Disconnect( ); }
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_EndNotifyThread
//
// Synopsis: Shuts down the notification thread (if any)
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::_EndNotifyThread() { //
// Is there a thread out there to close?
//
if (0 != _threadNotify) { // Signal the thread to die and wait for it to do so, or just kill
// the thread if it looks like it might be lost in client code.
tbDebugOut(( DEB_NOTIFY, "set notify thread die event %lx\n", _evtEndNotifyThread.GetHandle() ));
_evtEndNotifyThread.Set();
DWORD dw = WaitForSingleObject( _threadNotify, INFINITE );
BOOL fCloseWorked = CloseHandle( _threadNotify );
Win4Assert( fCloseWorked && "CloseHandle of notify thread failed" );
_threadNotify = 0;
tbDebugOut(( DEB_NOTIFY, "notify thread is history \n")); } } //_EndNotifyThread
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_StartNotifyThread
//
// Synopsis: Starts the notification thread
//
// History: 17 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
inline void CRowsetAsynchNotification::_StartNotifyThread() { tbDebugOut(( DEB_NOTIFY, "starting rowset notify thread\n" ));
// First advise, create the thread
Win4Assert(0 == _threadNotify); _threadNotify = CreateThread( 0, 65536, (LPTHREAD_START_ROUTINE) _NotifyThread, this, 0, & _threadNotifyId );
if (0 == _threadNotify) THROW( CException() ); }
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_NotifyThread, private
//
// Synopsis: Entry point for notification thread
//
// Arguments: [self] -- a container to call to do the work
//
// Returns: Thread exit code
//
// Notes: this function is "static"
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
DWORD CRowsetAsynchNotification::_NotifyThread( CRowsetAsynchNotification *self) { TRANSLATE_EXCEPTIONS;
DWORD dw = self->_DoNotifications();
UNTRANSLATE_EXCEPTIONS;
return dw; } //_NotifyThread
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_DoNotifications, private
//
// Synopsis: Collects notifications and passes them out to clients.
// Loops sending notifications until event to end thread
// arrives.
//
// Returns: Thread exit code
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
DWORD CRowsetAsynchNotification::_DoNotifications() { BOOL fContinue = TRUE;
do { TRY { if (fContinue && _AsynchConnectionPoint.GetAdviseCount() ) fContinue = _DoAsynchNotification();
if (fContinue && _fDoWatch && _WatchConnectionPoint.GetAdviseCount() ) fContinue = _DoWatchNotification();
if (_fPopulationComplete && !_fDoWatch) fContinue = FALSE; } CATCH( CException, e ) { // don't want to 'break' out of a catch block, use variable
fContinue = FALSE; } END_CATCH;
// Sleep for a bit, but wake up if the thread is to go away,
ULONG x = _evtEndNotifyThread.Wait( defNotificationSleepDuration, FALSE );
if ( STATUS_WAIT_0 == x ) fContinue = FALSE; } while ( fContinue );
return 0; } //_DoNotifications
//+-------------------------------------------------------------------------
//
// Function: IsLowResources
//
// Synopsis: Returns TRUE if it looks like the error is resource related.
//
// Returns: BOOL - TRUE if low on resources
//
// History: 9 May 1999 dlee
//
//--------------------------------------------------------------------------
BOOL IsLowResources( SCODE sc ) { return E_OUTOFMEMORY == sc || STATUS_NO_MEMORY == sc || STATUS_COMMITMENT_LIMIT == sc || STATUS_INSUFFICIENT_RESOURCES == sc || HRESULT_FROM_WIN32( ERROR_COMMITMENT_LIMIT ) == sc || HRESULT_FROM_WIN32( ERROR_NO_SYSTEM_RESOURCES ) == sc || STG_E_INSUFFICIENTMEMORY == sc; } //IsLowResources
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_DoAsynchNotification, private
//
// Synopsis: Collects asynch notifications and passes them out to clients.
// Tracks whether rowset population is completed, after which
// no more notifications are given (except for completion
// notifications for new advises).
//
// Returns: BOOL - TRUE if notification thread should continue
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
inline DWORD MapAsynchPhaseAndOp( ULONG op, ULONG phase ) { Win4Assert( op == 0 && phase < 4 ); return (((op+1) << 4) | (1 << phase)); }
#define ASYNC_PHASE_MASK 0x0F
#define ASYNC_OP_MASK 0xF0
BOOL CRowsetAsynchNotification::_DoAsynchNotification() { DBCOUNTITEM ulDenominator = 0, ulNumerator = 0; DBCOUNTITEM cRows = 0; BOOL fNewRows = FALSE; SCODE sc = S_OK;
TRY { CNotificationSync Sync( _evtEndNotifyThread.GetHandle() ); sc = _query.RatioFinished( Sync, _hCursor, ulDenominator, ulNumerator, cRows, fNewRows ); // Did the main thread tell this thread to go away?
if ( SUCCEEDED( sc ) ) { Win4Assert( ulDenominator > 0 && ulNumerator <= ulDenominator ); if (fNewRows) OnRowChange( _pRowset, 0, 0, DBREASON_ROW_ASYNCHINSERT, DBEVENTPHASE_DIDEVENT, TRUE ); if (!_fPopulationComplete && (ulNumerator == ulDenominator)) { OnRowsetChange( _pRowset, DBREASON_ROWSET_POPULATIONCOMPLETE, DBEVENTPHASE_DIDEVENT, TRUE ); _fPopulationComplete = TRUE; } } } CATCH( CException, e ) { sc = e.GetErrorCode(); } END_CATCH;
if ( STATUS_CANCELLED == sc ) return FALSE;
BOOL fLowResources = IsLowResources( sc );
if ( !fLowResources && !SUCCEEDED( sc ) ) return FALSE;
// Give notice to all active advises.
// Need to be careful here to avoid deadlock. The enumerator
// grabs the CPC's mutex. This limits what the client can do.
ULONG ulAsynchPhase = DBASYNCHPHASE_POPULATION; if (_fPopulationComplete) ulAsynchPhase = DBASYNCHPHASE_COMPLETE; ULONG dwOpMask = MapAsynchPhaseAndOp( DBASYNCHOP_OPEN, ulAsynchPhase );
CEnumConnectionsLite Enum( _AsynchConnectionPoint ); CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( 0 != pConnCtx ) { IDBAsynchNotify *pNotifyAsynch = (IDBAsynchNotify *)(pConnCtx->_pIUnk); tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging asynch client %x\n", pNotifyAsynch )); SCODE sc = S_OK;
if ( fLowResources ) { //
// Let the client know we're really wedged and they might not
// get more notifications consistently.
//
sc = pNotifyAsynch->OnLowResource( 0 ); } else { if ( 0 == (pConnCtx->_dwSpare & dwOpMask) ) { sc = pNotifyAsynch->OnProgress( DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, ulNumerator, ulDenominator, ulAsynchPhase, 0); }
BOOL fOnStop = FALSE; if ( _fPopulationComplete && S_OK == sc ) { sc = DB_S_UNWANTEDPHASE; fOnStop = TRUE; } if (DB_S_UNWANTEDPHASE == sc) pConnCtx->_dwSpare |= (dwOpMask & ASYNC_PHASE_MASK); else if (DB_S_UNWANTEDOPERATION == sc) pConnCtx->_dwSpare |= (dwOpMask & ASYNC_OP_MASK); else if (E_NOTIMPL == sc) pConnCtx->_dwSpare |= (ASYNC_PHASE_MASK | ASYNC_OP_MASK);
if ( fOnStop ) pNotifyAsynch->OnStop( DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, S_OK, 0 ); }
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging asynch client\n" )); pConnCtx = Enum.Next(); }
// Keep trying to get notifications even if fLowResources is TRUE
return TRUE; } //_DoAsynchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_DoWatchNotification, private
//
// Synopsis: Collects watch notifications and passes them out to clients.
//
// Returns: BOOL - TRUE if notification thread should continue
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
BOOL CRowsetAsynchNotification::_DoWatchNotification() { // get notification information from the cursor
CNotificationSync Sync( _evtEndNotifyThread.GetHandle() ); DBWATCHNOTIFY changeType;
SCODE sc = _query.GetNotifications( Sync, changeType );
// Did the main thread tell this thread to go away?
if (STATUS_CANCELLED == sc) return FALSE;
if (!SUCCEEDED(sc)) return FALSE;
// got some, give them to all active advises
// Need to be careful here to avoid deadlock. The enumerator
// grabs the CPC's mutex. We need to break out of the loop
// if the notify thread needs to go away...
tbDebugOut(( DEB_NOTIFY, "rownotfy: watch type %d\n", changeType )); CEnumConnectionsLite Enum( _WatchConnectionPoint ); CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( pConnCtx ) { IRowsetWatchNotify *pNotifyWatch = (IRowsetWatchNotify *)(pConnCtx->_pIUnk); tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging watch client %x\n", pNotifyWatch )); pNotifyWatch->OnChange( _pRowset, changeType); tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging watch client\n" )); pConnCtx = Enum.Next(); }
return TRUE; } //_DoWatchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::AdviseHelper, private static
//
// Synopsis: Starts the notification thread if this is the first advise.
//
// Arguments: [pHelperContext] - "this" pointer
// [pConnPt] - the connection point, either Async or Watch CP
// [pConnCtx] - pointer to connection context
//
// Notes: CPC critical section is assumed to be held.
//
// History: 17 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::AdviseHelper( PVOID pHelperContext, CConnectionPointBase * pConnPt, CConnectionContext * pConnCtx ) { CRowsetAsynchNotification * pSelf = (CRowsetAsynchNotification *) pHelperContext;
pSelf->_cAdvise++; if (1 == pSelf->_cAdvise) { // First advise, create the notification thread
pSelf->_StartNotifyThread(); Win4Assert(0 != pSelf->_threadNotify); } }
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::UnadviseHelper, private static
//
// Synopsis: Stops the notification thread if this is the last active advise.
//
// Arguments: [pHelperContext] -- "this" pointer
// [pConnPt] -- the connection point, either Async or
// Watch CP
// [pConnCtx] -- pointer to connection context
// [lock] -- CPC lock
//
// Notes: CPC critical section is assumed to be held.
//
// History: 17 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::UnadviseHelper( PVOID pHelperContext, CConnectionPointBase * pConnPt, CConnectionContext * pConnCtx, CReleasableLock & lock ) { CRowsetAsynchNotification * pSelf = (CRowsetAsynchNotification *) pHelperContext;
Win4Assert( pSelf->_cAdvise > 0 ); if ( --pSelf->_cAdvise == 0 ) { // Release the lock so the notify thread has a change to end
// when we tell it to end.
lock.Release();
pSelf->_EndNotifyThread(); } }
//+-------------------------------------------------------------------------
//
// Function: MapPhaseAndReason, inline local
//
// Synopsis: Encode IRowsetNotify phase and reason into a bit mask
// for supported phases and reasons. Luckily, most supported
// reasons have only a single phase. Multi-phase reasons take
// 5 bits.
//
// Returns: DWORD - bit mask for the reason/phase combination
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
const maxSinglePhaseReasons = 8; const maxMultiPhaseReasons = 4; const maxPhases = 5; const bitDidNotifyVote = 0x80000000;
inline DWORD MapPhaseAndReason( ULONG phase, ULONG reason ) { Win4Assert( reason <= 21 && phase <= 4 );
BOOL fSinglePhase = TRUE; DWORD dwMappedReason = 0;
switch (reason) { // single-phase reasons
case DBREASON_ROW_ASYNCHINSERT: dwMappedReason = 0; break; case DBREASON_ROWSET_RELEASE: dwMappedReason = 1; break; case DBREASON_ROW_ACTIVATE: dwMappedReason = 2; break; case DBREASON_ROW_RELEASE: dwMappedReason = 3; break; case DBREASON_ROWSET_POPULATIONCOMPLETE: dwMappedReason = 4; break; case DBREASON_ROWSET_POPULATIONSTOPPED: dwMappedReason = 5; break;
// multi-phase reasons
case DBREASON_ROWSET_FETCHPOSITIONCHANGE: dwMappedReason = 0; fSinglePhase = FALSE; break;
default: tbDebugOut(( DEB_ERROR, "MapPhaseAndReason - reason: %d phase: %d\n", reason, phase )); Win4Assert( !"MapPhaseAndReason: unhandled reason" ); }
Win4Assert( FALSE == fSinglePhase || phase == DBEVENTPHASE_DIDEVENT ); if (fSinglePhase) { Win4Assert( dwMappedReason < maxSinglePhaseReasons ); dwMappedReason = (1 << dwMappedReason); } else { dwMappedReason = dwMappedReason*maxPhases + phase + maxSinglePhaseReasons; Win4Assert( dwMappedReason <= 30 ); dwMappedReason = (1 << dwMappedReason); }
return dwMappedReason; }
//+-------------------------------------------------------------------------
//
// Function: MapAllPhases, inline local
//
// Synopsis: Return a bit mask that includes all bits for all supported
// phases of the encoded reason.
//
// Arguments: [dwMappedReasonAndPhase] - a bit mask returned by
// MapPhaseAndReason.
//
// Returns: DWORD - bit mask for all phases
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
inline DWORD MapAllPhases( DWORD dwMappedReasonAndPhase ) { Win4Assert( dwMappedReasonAndPhase != 0 );
if (dwMappedReasonAndPhase < (1<<maxSinglePhaseReasons)) return dwMappedReasonAndPhase;
// dwMask == 0x00001F00
DWORD dwMask = (((1<<maxPhases)-1) << maxSinglePhaseReasons); for (unsigned i=0; i<maxMultiPhaseReasons; i++) { if (dwMask & dwMappedReasonAndPhase) return (dwMappedReasonAndPhase | dwMask);
dwMask = dwMask << maxPhases; } Win4Assert(! "MapAllPhases - fell out of loop!" ); return 0; }
//+-------------------------------------------------------------------------
//
// Method: IRowsetNotify::OnXxxxChange
//
// Synopsis: Dispatches CRowsetNotification notifications to clients.
//
// Returns: SCODE
//
// History: 11 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
SCODE CRowsetNotification::OnFieldChange ( IRowset * pRowset, HROW hRow, DBORDINAL cColumns, DBORDINAL rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny ) { SCODE sc = S_OK;
// NOTE: OnFieldChange is not generated by our rowsets. This code
// doesn't handle unwanted phases or reasons.
CEnumConnectionsLite Enum( *this ); CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( pConnCtx ) { IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk); tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify )); SCODE sc1 = pNotify->OnFieldChange( pRowset, hRow, cColumns, rgColumns, eReason, ePhase, fCantDeny ); tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" )); pConnCtx = Enum.Next(); } return sc; }
SCODE CRowsetNotification::OnRowChange ( IRowset * pRowset, DBCOUNTITEM cRows, const HROW rghRows[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny ) { // Note: we don't generate any row notifications which can be denied.
Win4Assert( fCantDeny && DBEVENTPHASE_DIDEVENT == ePhase );
DWORD dwMask = MapPhaseAndReason( ePhase, eReason ); CEnumConnectionsLite Enum( *this ); CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( pConnCtx ) { if (0 == (pConnCtx->_dwSpare & dwMask)) { IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk); tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify )); SCODE sc1 = pNotify->OnRowChange( pRowset, cRows, rghRows, eReason, ePhase, fCantDeny ); tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" )); if (DB_S_UNWANTEDPHASE == sc1) pConnCtx->_dwSpare |= dwMask; else if (DB_S_UNWANTEDREASON == sc1) pConnCtx->_dwSpare |= MapAllPhases(dwMask); } pConnCtx = Enum.Next(); } return S_OK; }
//+-------------------------------------------------------------------------
//
// Method: CRowsetNotification::DoRowsetChangeCallout, private
//
// Synopsis: Issues an OnRowsetChange notification to all active
// advises. If [fCantDeny] is FALSE, tracks which advises
// were successfully notified and checks the votes.
//
// Arguments: [Enum] - a connection context enumeration
// [pRowset] - the rowset issuing the notification
// [eReason] - the notification reason
// [ePhase] - the notification phase
// [fCantDeny] - if TRUE, notification can't be denied
//
// Notes: CPC critical section is assumed to be held.
//
// History: 08 Apr 1998 AlanW
//
//--------------------------------------------------------------------------
BOOL CRowsetNotification::DoRowsetChangeCallout ( CEnumConnectionsLite & Enum, IRowset * pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny ) { DWORD dwMask = MapPhaseAndReason( ePhase, eReason ); CConnectionPointBase::CConnectionContext *pConnCtx; BOOL fVeto = FALSE;
for ( pConnCtx = Enum.First(); pConnCtx; pConnCtx = Enum.Next() ) { if ( 0 == (pConnCtx->_dwSpare & dwMask) ) { IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk); tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify )); SCODE sc1 = pNotify->OnRowsetChange( pRowset, eReason, ePhase, fCantDeny );
if ( !fCantDeny ) { if (S_FALSE == sc1) { tbDebugOut(( DEB_NOTIFY, "rownotfy: notify client veto'ed\n" )); fVeto = TRUE; break; } pConnCtx->_dwSpare |= bitDidNotifyVote; } if (DB_S_UNWANTEDPHASE == sc1) pConnCtx->_dwSpare |= dwMask; else if (DB_S_UNWANTEDREASON == sc1) pConnCtx->_dwSpare |= MapAllPhases(dwMask);
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" )); } } return fVeto; }
SCODE CRowsetNotification::OnRowsetChange ( IRowset * pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny ) { // Note: we don't use SYNCHAFTER, and ABOUTTODO is done here after voting
Win4Assert( DBEVENTPHASE_ABOUTTODO != ePhase && DBEVENTPHASE_SYNCHAFTER != ePhase );
BOOL fVeto = FALSE; CEnumConnectionsLite Enum( *this );
if ( DBEVENTPHASE_OKTODO == ePhase ) { Win4Assert( !fCantDeny );
fVeto = DoRowsetChangeCallout( Enum, pRowset, eReason, DBEVENTPHASE_OKTODO, FALSE ); if ( ! fVeto ) fVeto = DoRowsetChangeCallout( Enum, pRowset, eReason, DBEVENTPHASE_ABOUTTODO, FALSE );
DWORD dwMask = MapPhaseAndReason( DBEVENTPHASE_FAILEDTODO, eReason ); CConnectionPointBase::CConnectionContext *pConnCtx;
for ( pConnCtx = Enum.First(); pConnCtx; pConnCtx = Enum.Next() ) { if (fVeto && 0 == (pConnCtx->_dwSpare & dwMask) && 0 != (pConnCtx->_dwSpare & bitDidNotifyVote) ) { IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk); tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x for FAIL\n", pNotify )); SCODE sc1 = pNotify->OnRowsetChange( pRowset, eReason, DBEVENTPHASE_FAILEDTODO, TRUE ); tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client for FAIL\n" ));
if (DB_S_UNWANTEDPHASE == sc1) pConnCtx->_dwSpare |= dwMask; else if (DB_S_UNWANTEDREASON == sc1) pConnCtx->_dwSpare |= MapAllPhases(dwMask); } pConnCtx->_dwSpare &= ~bitDidNotifyVote; } } else { // a non-vetoable notification; just send the notifies
Win4Assert( fCantDeny ); DoRowsetChangeCallout( Enum, pRowset, eReason, ePhase, TRUE ); } return fVeto ? S_FALSE : S_OK; }
|