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

  1. //
  2. // lqueue.cpp
  3. //
  4. // Lock queue code.
  5. //
  6. #include "private.h"
  7. #include "ic.h"
  8. #include "tim.h"
  9. #include "dim.h"
  10. //+---------------------------------------------------------------------------
  11. //
  12. // _EditSessionQiCallback
  13. //
  14. //----------------------------------------------------------------------------
  15. HRESULT CAsyncQueueItem::_EditSessionQiCallback(CInputContext *pic, struct _TS_QUEUE_ITEM *pItem, QiCallbackCode qiCode)
  16. {
  17. switch (qiCode)
  18. {
  19. case QI_ADDREF:
  20. pItem->state.aqe.paqi->_AddRef();
  21. break;
  22. case QI_DISPATCH:
  23. return pItem->state.aqe.paqi->DoDispatch(pic);
  24. case QI_FREE:
  25. pItem->state.aqe.paqi->_Release();
  26. break;
  27. }
  28. return S_OK;
  29. }
  30. //+---------------------------------------------------------------------------
  31. //
  32. // _CheckReadOnly
  33. //
  34. //----------------------------------------------------------------------------
  35. void CAsyncQueueItem::_CheckReadOnly(CInputContext *pic)
  36. {
  37. TS_STATUS dcs;
  38. if (SUCCEEDED(pic->GetStatus(&dcs)))
  39. {
  40. if (dcs.dwDynamicFlags & TF_SD_READONLY)
  41. _item.dwFlags &= ~TF_ES_WRITE;
  42. }
  43. }
  44. //+---------------------------------------------------------------------------
  45. //
  46. // _QueueItem
  47. //
  48. //----------------------------------------------------------------------------
  49. HRESULT CInputContext::_QueueItem(TS_QUEUE_ITEM *pItem, BOOL fForceAsync, HRESULT *phrSession)
  50. {
  51. TS_QUEUE_ITEM *pQueueItem;
  52. int iItem;
  53. HRESULT hr;
  54. HRESULT hrRet;
  55. BOOL fSync;
  56. BOOL fNeedWriteLock;
  57. BOOL fSyncLockFailed = FALSE;
  58. if (pItem->dwFlags & TF_ES_WRITE)
  59. {
  60. // write access implies property write access
  61. pItem->dwFlags |= TF_ES_PROPERTY_WRITE;
  62. }
  63. fSync = (pItem->dwFlags & TF_ES_SYNC);
  64. Assert(!(fSync && fForceAsync)); // doesn't make sense
  65. fNeedWriteLock = _fLockHeld ? (pItem->dwFlags & TF_ES_WRITE) && !(_dwlt & TF_ES_WRITE) : FALSE;
  66. if (fForceAsync)
  67. goto QueueItem;
  68. if (!fSync &&
  69. _rgLockQueue.Count() > 0)
  70. {
  71. // there's already something in the queue
  72. // so we can't handle an async request until later
  73. fForceAsync = TRUE;
  74. goto QueueItem;
  75. }
  76. if (_fLockHeld)
  77. {
  78. if (fSync)
  79. {
  80. // sync
  81. // we can't handle a sync write req while holding a read lock
  82. *phrSession = fNeedWriteLock ? TF_E_SYNCHRONOUS : _DispatchQueueItem(pItem);
  83. }
  84. else
  85. {
  86. // async
  87. if (fNeedWriteLock)
  88. {
  89. // we need a write lock we don't currently hold
  90. fForceAsync = TRUE;
  91. goto QueueItem;
  92. }
  93. *phrSession = _DispatchQueueItem(pItem);
  94. }
  95. return S_OK;
  96. }
  97. QueueItem:
  98. if ((pQueueItem = _rgLockQueue.Append(1)) == NULL)
  99. {
  100. *phrSession = E_FAIL;
  101. return E_OUTOFMEMORY;
  102. }
  103. *pQueueItem = *pItem;
  104. _AddRefQueueItem(pQueueItem);
  105. hrRet = S_OK;
  106. *phrSession = TF_S_ASYNC;
  107. if (!fForceAsync)
  108. {
  109. Assert(!_fLockHeld);
  110. _fLockHeld = TRUE;
  111. _dwlt = pItem->dwFlags;
  112. hrRet = SafeRequestLock(_ptsi, pItem->dwFlags & ~TF_ES_PROPERTY_WRITE, phrSession);
  113. _fLockHeld = FALSE;
  114. // possibly this was a synch request, but the app couldn't grant it
  115. // now we need to make sure the queue item is cleared
  116. if ((iItem = _rgLockQueue.Count() - 1) >= 0)
  117. {
  118. TS_QUEUE_ITEM *pItemTemp;
  119. pItemTemp = _rgLockQueue.GetPtr(iItem);
  120. if (pItemTemp->dwFlags & TF_ES_SYNC)
  121. {
  122. Assert(*phrSession == TS_E_SYNCHRONOUS); // only reason why item is still in queue should be app lock rejection
  123. _ReleaseQueueItem(pItemTemp);
  124. _rgLockQueue.Remove(iItem, 1);
  125. fSyncLockFailed = TRUE;
  126. }
  127. }
  128. if (_ptsi == NULL)
  129. {
  130. // someone popped this ic during the RequestLock above
  131. goto Exit;
  132. }
  133. }
  134. // make sure synchronous edit session gets cleared off the queue no matter what after the lock request!
  135. _Dbg_AssertNoSyncQueueItems();
  136. // this shouldn't go reentrant, but just to be safe do it last
  137. if (_fLockHeld)
  138. {
  139. if (fForceAsync && fNeedWriteLock)
  140. {
  141. SafeRequestLock(_ptsi, TS_LF_READWRITE, &hr); // Issue: try to recover if app goes reentrant?
  142. Assert(hr == TS_S_ASYNC); // app should have granted this async
  143. }
  144. }
  145. else if (!fSyncLockFailed)
  146. {
  147. // we don't hold a lock currently
  148. if (fForceAsync || (fSync && _rgLockQueue.Count() > 0))
  149. {
  150. // ask for another lock later, if there are pending async items in the queue
  151. _PostponeLockRequest(pItem->dwFlags);
  152. }
  153. }
  154. Exit:
  155. return hrRet;
  156. }
  157. //+---------------------------------------------------------------------------
  158. //
  159. // _EmptyLockQueue
  160. //
  161. //----------------------------------------------------------------------------
  162. HRESULT CInputContext::_EmptyLockQueue(DWORD dwLockFlags, BOOL fAppChangesSent)
  163. {
  164. TS_QUEUE_ITEM *pItem;
  165. TS_QUEUE_ITEM item;
  166. int iItem;
  167. HRESULT hr;
  168. Assert(_fLockHeld); // what are we doing emptying the queue without a lock?!
  169. if ((iItem = _rgLockQueue.Count() - 1) < 0)
  170. return S_OK;
  171. //
  172. // special case: there may be a single synchronous es at the end of the queue
  173. //
  174. pItem = _rgLockQueue.GetPtr(iItem);
  175. if (pItem->dwFlags & TF_ES_SYNC)
  176. {
  177. item = *pItem;
  178. _rgLockQueue.Remove(iItem, 1);
  179. if (fAppChangesSent)
  180. {
  181. Assert(0); // this is suspicious...the app should not let this happen.
  182. // What's happened: the app had some pending changes, but hadn't responed to
  183. // our lock requests yet. Then some TIP asked for a sync lock, which we just got.
  184. // Normally, it is unlikely that an app would still have pending changes by the time
  185. // a tip fires off -- the app should clear any pending changes before starting a
  186. // transaction like a key event or reconversion. However, another possibility is
  187. // that a rogue TIP is using the sync flag for a private event, which is discouraged
  188. // because it could fail here.
  189. // in any case, we won't continue until the app changes have been dealt with...must back out with an error.
  190. return E_UNEXPECTED;
  191. }
  192. if ((item.dwFlags & TF_ES_WRITE) && !(dwLockFlags & TF_ES_WRITE))
  193. {
  194. Assert(0); // app granted wrong access?
  195. return E_UNEXPECTED;
  196. }
  197. Assert(!(item.dwFlags & TF_ES_WRITE) || (item.dwFlags & TF_ES_PROPERTY_WRITE)); // write implies property write
  198. hr = _DispatchQueueItem(&item);
  199. _ReleaseQueueItem(&item);
  200. return hr;
  201. }
  202. //
  203. // handle any asynch requests
  204. //
  205. while (_rgLockQueue.Count() > 0)
  206. {
  207. pItem = _rgLockQueue.GetPtr(0);
  208. Assert(!(pItem->dwFlags & TF_ES_SYNC)); // should never see synch item here!
  209. // make sure the lock we've been given is good enough for the queued es
  210. if ((pItem->dwFlags & TF_ES_WRITE) && !(dwLockFlags & TF_ES_WRITE))
  211. {
  212. // ask for an upgrade anyways, to try to recover
  213. SafeRequestLock(_ptsi, TS_LF_READWRITE, &hr); // Issue: try to recover if app goes reentrant?
  214. Assert(hr == TS_S_ASYNC); // app should have granted this async
  215. break;
  216. }
  217. item = *pItem;
  218. _rgLockQueue.Remove(0, 1);
  219. _DispatchQueueItem(&item);
  220. _ReleaseQueueItem(&item);
  221. }
  222. return S_OK;
  223. }
  224. //+---------------------------------------------------------------------------
  225. //
  226. // _AbortQueueItems
  227. //
  228. //----------------------------------------------------------------------------
  229. void CInputContext::_AbortQueueItems()
  230. {
  231. TS_QUEUE_ITEM *pItem;
  232. int i;
  233. for (i=0; i<_rgLockQueue.Count(); i++)
  234. {
  235. pItem = _rgLockQueue.GetPtr(i);
  236. Assert(!(pItem->dwFlags & TF_ES_SYNC)); // should never see synch item here!
  237. _ReleaseQueueItem(pItem);
  238. }
  239. _rgLockQueue.Clear();
  240. if (_dwPendingLockRequest)
  241. {
  242. SYSTHREAD *psfn = GetSYSTHREAD();
  243. if (psfn)
  244. psfn->_dwLockRequestICRef--;
  245. _dwPendingLockRequest = 0;
  246. }
  247. }
  248. //+---------------------------------------------------------------------------
  249. //
  250. // _PostponeLockRequest
  251. //
  252. //----------------------------------------------------------------------------
  253. void CInputContext::_PostponeLockRequest(DWORD dwFlags)
  254. {
  255. dwFlags &= TF_ES_READWRITE;
  256. Assert(dwFlags != 0);
  257. // we don't need to upgrade the req because we can do that inside the
  258. // lock grant more efficiently
  259. if (_dwPendingLockRequest == 0)
  260. {
  261. SYSTHREAD *psfn = GetSYSTHREAD();
  262. if (!psfn)
  263. return;
  264. if (!_nLockReqPostDisableRef && !psfn->_fLockRequestPosted)
  265. {
  266. if (!PostThreadMessage(GetCurrentThreadId(), g_msgPrivate, TFPRIV_LOCKREQ, 0))
  267. {
  268. return;
  269. }
  270. psfn->_fLockRequestPosted = TRUE;
  271. }
  272. psfn->_dwLockRequestICRef++;
  273. }
  274. _dwPendingLockRequest |= dwFlags;
  275. }
  276. //+---------------------------------------------------------------------------
  277. //
  278. // _PostponeLockRequestCallback
  279. //
  280. //----------------------------------------------------------------------------
  281. /* static */
  282. void CInputContext::_PostponeLockRequestCallback(SYSTHREAD *psfn, CInputContext *pic)
  283. {
  284. CThreadInputMgr *tim;
  285. CDocumentInputManager *dim;
  286. int iDim;
  287. int iContext;
  288. DWORD dwFlags;
  289. HRESULT hr;
  290. Assert(psfn);
  291. if (!psfn->_dwLockRequestICRef)
  292. return;
  293. // need to verify pic is still valid
  294. tim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn);
  295. if (!tim)
  296. return;
  297. for (iDim = 0; iDim < tim->_rgdim.Count(); iDim++)
  298. {
  299. dim = tim->_rgdim.Get(iDim);
  300. for (iContext = 0; iContext <= dim->_GetCurrentStack(); iContext++)
  301. {
  302. CInputContext *picCur = dim->_GetIC(iContext);
  303. if (!pic || (picCur == pic))
  304. {
  305. // we found this ic, it's valid
  306. dwFlags = picCur->_dwPendingLockRequest;
  307. if (dwFlags)
  308. {
  309. picCur->_dwPendingLockRequest = 0;
  310. Assert(psfn->_dwLockRequestICRef > 0);
  311. psfn->_dwLockRequestICRef--;
  312. SafeRequestLock(picCur->_ptsi, dwFlags, &hr);
  313. }
  314. if (pic)
  315. return;
  316. }
  317. }
  318. }
  319. }
  320. //+---------------------------------------------------------------------------
  321. //
  322. // EnableLockRequestPosting
  323. //
  324. //----------------------------------------------------------------------------
  325. HRESULT CInputContext::EnableLockRequestPosting(BOOL fEnable)
  326. {
  327. if (!fEnable)
  328. {
  329. _nLockReqPostDisableRef++;
  330. }
  331. else
  332. {
  333. if (_nLockReqPostDisableRef > 0)
  334. _nLockReqPostDisableRef--;
  335. }
  336. return S_OK;
  337. }