914 lines
29 KiB
C++
914 lines
29 KiB
C++
|
//----------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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;
|
||
|
}
|