|
|
//
// lqueue.cpp
//
// Lock queue code.
//
#include "private.h"
#include "ic.h"
#include "tim.h"
#include "dim.h"
//+---------------------------------------------------------------------------
//
// _EditSessionQiCallback
//
//----------------------------------------------------------------------------
HRESULT CAsyncQueueItem::_EditSessionQiCallback(CInputContext *pic, struct _TS_QUEUE_ITEM *pItem, QiCallbackCode qiCode) { switch (qiCode) { case QI_ADDREF: pItem->state.aqe.paqi->_AddRef(); break;
case QI_DISPATCH: return pItem->state.aqe.paqi->DoDispatch(pic);
case QI_FREE: pItem->state.aqe.paqi->_Release(); break; } return S_OK; }
//+---------------------------------------------------------------------------
//
// _CheckReadOnly
//
//----------------------------------------------------------------------------
void CAsyncQueueItem::_CheckReadOnly(CInputContext *pic) { TS_STATUS dcs; if (SUCCEEDED(pic->GetStatus(&dcs))) { if (dcs.dwDynamicFlags & TF_SD_READONLY) _item.dwFlags &= ~TF_ES_WRITE; } }
//+---------------------------------------------------------------------------
//
// _QueueItem
//
//----------------------------------------------------------------------------
HRESULT CInputContext::_QueueItem(TS_QUEUE_ITEM *pItem, BOOL fForceAsync, HRESULT *phrSession) { TS_QUEUE_ITEM *pQueueItem; int iItem; HRESULT hr; HRESULT hrRet; BOOL fSync; BOOL fNeedWriteLock; BOOL fSyncLockFailed = FALSE;
if (pItem->dwFlags & TF_ES_WRITE) { // write access implies property write access
pItem->dwFlags |= TF_ES_PROPERTY_WRITE; }
fSync = (pItem->dwFlags & TF_ES_SYNC);
Assert(!(fSync && fForceAsync)); // doesn't make sense
fNeedWriteLock = _fLockHeld ? (pItem->dwFlags & TF_ES_WRITE) && !(_dwlt & TF_ES_WRITE) : FALSE;
if (fForceAsync) goto QueueItem;
if (!fSync && _rgLockQueue.Count() > 0) { // there's already something in the queue
// so we can't handle an async request until later
fForceAsync = TRUE; goto QueueItem; }
if (_fLockHeld) { if (fSync) { // sync
// we can't handle a sync write req while holding a read lock
*phrSession = fNeedWriteLock ? TF_E_SYNCHRONOUS : _DispatchQueueItem(pItem); } else { // async
if (fNeedWriteLock) { // we need a write lock we don't currently hold
fForceAsync = TRUE; goto QueueItem; }
*phrSession = _DispatchQueueItem(pItem); }
return S_OK; }
QueueItem: if ((pQueueItem = _rgLockQueue.Append(1)) == NULL) { *phrSession = E_FAIL; return E_OUTOFMEMORY; }
*pQueueItem = *pItem; _AddRefQueueItem(pQueueItem);
hrRet = S_OK; *phrSession = TF_S_ASYNC;
if (!fForceAsync) { Assert(!_fLockHeld);
_fLockHeld = TRUE; _dwlt = pItem->dwFlags;
hrRet = SafeRequestLock(_ptsi, pItem->dwFlags & ~TF_ES_PROPERTY_WRITE, phrSession);
_fLockHeld = FALSE;
// possibly this was a synch request, but the app couldn't grant it
// now we need to make sure the queue item is cleared
if ((iItem = _rgLockQueue.Count() - 1) >= 0) { TS_QUEUE_ITEM *pItemTemp; pItemTemp = _rgLockQueue.GetPtr(iItem); if (pItemTemp->dwFlags & TF_ES_SYNC) { Assert(*phrSession == TS_E_SYNCHRONOUS); // only reason why item is still in queue should be app lock rejection
_ReleaseQueueItem(pItemTemp); _rgLockQueue.Remove(iItem, 1);
fSyncLockFailed = TRUE; } }
if (_ptsi == NULL) { // someone popped this ic during the RequestLock above
goto Exit; } }
// make sure synchronous edit session gets cleared off the queue no matter what after the lock request!
_Dbg_AssertNoSyncQueueItems();
// this shouldn't go reentrant, but just to be safe do it last
if (_fLockHeld) { if (fForceAsync && fNeedWriteLock) { SafeRequestLock(_ptsi, TS_LF_READWRITE, &hr); // Issue: try to recover if app goes reentrant?
Assert(hr == TS_S_ASYNC); // app should have granted this async
} } else if (!fSyncLockFailed) { // we don't hold a lock currently
if (fForceAsync || (fSync && _rgLockQueue.Count() > 0)) { // ask for another lock later, if there are pending async items in the queue
_PostponeLockRequest(pItem->dwFlags); } }
Exit: return hrRet; }
//+---------------------------------------------------------------------------
//
// _EmptyLockQueue
//
//----------------------------------------------------------------------------
HRESULT CInputContext::_EmptyLockQueue(DWORD dwLockFlags, BOOL fAppChangesSent) { TS_QUEUE_ITEM *pItem; TS_QUEUE_ITEM item; int iItem; HRESULT hr;
Assert(_fLockHeld); // what are we doing emptying the queue without a lock?!
if ((iItem = _rgLockQueue.Count() - 1) < 0) return S_OK;
//
// special case: there may be a single synchronous es at the end of the queue
//
pItem = _rgLockQueue.GetPtr(iItem);
if (pItem->dwFlags & TF_ES_SYNC) { item = *pItem; _rgLockQueue.Remove(iItem, 1);
if (fAppChangesSent) { Assert(0); // this is suspicious...the app should not let this happen.
// What's happened: the app had some pending changes, but hadn't responed to
// our lock requests yet. Then some TIP asked for a sync lock, which we just got.
// Normally, it is unlikely that an app would still have pending changes by the time
// a tip fires off -- the app should clear any pending changes before starting a
// transaction like a key event or reconversion. However, another possibility is
// that a rogue TIP is using the sync flag for a private event, which is discouraged
// because it could fail here.
// in any case, we won't continue until the app changes have been dealt with...must back out with an error.
return E_UNEXPECTED; }
if ((item.dwFlags & TF_ES_WRITE) && !(dwLockFlags & TF_ES_WRITE)) { Assert(0); // app granted wrong access?
return E_UNEXPECTED; } Assert(!(item.dwFlags & TF_ES_WRITE) || (item.dwFlags & TF_ES_PROPERTY_WRITE)); // write implies property write
hr = _DispatchQueueItem(&item); _ReleaseQueueItem(&item);
return hr; }
//
// handle any asynch requests
//
while (_rgLockQueue.Count() > 0) { pItem = _rgLockQueue.GetPtr(0);
Assert(!(pItem->dwFlags & TF_ES_SYNC)); // should never see synch item here!
// make sure the lock we've been given is good enough for the queued es
if ((pItem->dwFlags & TF_ES_WRITE) && !(dwLockFlags & TF_ES_WRITE)) { // ask for an upgrade anyways, to try to recover
SafeRequestLock(_ptsi, TS_LF_READWRITE, &hr); // Issue: try to recover if app goes reentrant?
Assert(hr == TS_S_ASYNC); // app should have granted this async
break; }
item = *pItem; _rgLockQueue.Remove(0, 1); _DispatchQueueItem(&item); _ReleaseQueueItem(&item); }
return S_OK; }
//+---------------------------------------------------------------------------
//
// _AbortQueueItems
//
//----------------------------------------------------------------------------
void CInputContext::_AbortQueueItems() { TS_QUEUE_ITEM *pItem; int i;
for (i=0; i<_rgLockQueue.Count(); i++) { pItem = _rgLockQueue.GetPtr(i);
Assert(!(pItem->dwFlags & TF_ES_SYNC)); // should never see synch item here!
_ReleaseQueueItem(pItem); }
_rgLockQueue.Clear();
if (_dwPendingLockRequest) { SYSTHREAD *psfn = GetSYSTHREAD();
if (psfn) psfn->_dwLockRequestICRef--;
_dwPendingLockRequest = 0; } }
//+---------------------------------------------------------------------------
//
// _PostponeLockRequest
//
//----------------------------------------------------------------------------
void CInputContext::_PostponeLockRequest(DWORD dwFlags) { dwFlags &= TF_ES_READWRITE;
Assert(dwFlags != 0);
// we don't need to upgrade the req because we can do that inside the
// lock grant more efficiently
if (_dwPendingLockRequest == 0) { SYSTHREAD *psfn = GetSYSTHREAD();
if (!psfn) return; if (!_nLockReqPostDisableRef && !psfn->_fLockRequestPosted) { if (!PostThreadMessage(GetCurrentThreadId(), g_msgPrivate, TFPRIV_LOCKREQ, 0)) { return; }
psfn->_fLockRequestPosted = TRUE; }
psfn->_dwLockRequestICRef++; }
_dwPendingLockRequest |= dwFlags; }
//+---------------------------------------------------------------------------
//
// _PostponeLockRequestCallback
//
//----------------------------------------------------------------------------
/* static */ void CInputContext::_PostponeLockRequestCallback(SYSTHREAD *psfn, CInputContext *pic) { CThreadInputMgr *tim; CDocumentInputManager *dim; int iDim; int iContext; DWORD dwFlags; HRESULT hr;
Assert(psfn);
if (!psfn->_dwLockRequestICRef) return;
// need to verify pic is still valid
tim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn); if (!tim) return;
for (iDim = 0; iDim < tim->_rgdim.Count(); iDim++) { dim = tim->_rgdim.Get(iDim);
for (iContext = 0; iContext <= dim->_GetCurrentStack(); iContext++) { CInputContext *picCur = dim->_GetIC(iContext); if (!pic || (picCur == pic)) { // we found this ic, it's valid
dwFlags = picCur->_dwPendingLockRequest; if (dwFlags) { picCur->_dwPendingLockRequest = 0;
Assert(psfn->_dwLockRequestICRef > 0); psfn->_dwLockRequestICRef--;
SafeRequestLock(picCur->_ptsi, dwFlags, &hr); }
if (pic) return; } } } }
//+---------------------------------------------------------------------------
//
// EnableLockRequestPosting
//
//----------------------------------------------------------------------------
HRESULT CInputContext::EnableLockRequestPosting(BOOL fEnable) { if (!fEnable) { _nLockReqPostDisableRef++; } else { if (_nLockReqPostDisableRef > 0) _nLockReqPostDisableRef--; } return S_OK; }
|