You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
11 KiB
418 lines
11 KiB
//
|
|
// 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;
|
|
}
|