Leaked source code of windows server 2003
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

//
// 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;
}