Source code of Windows XP (NT5)
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.

1392 lines
45 KiB

  1. /* --------------------------------------------------------------------
  2. Microsoft OS/2 LAN Manager
  3. Copyright(c) Microsoft Corp., 1991-2001
  4. -------------------------------------------------------------------- */
  5. /* --------------------------------------------------------------------
  6. Description :
  7. Provides RPC server side context handle management
  8. History :
  9. stevez 01-15-91 First bits into the bucket.
  10. Kamen Moutafov [kamenm] Sep 2000 Threw away all of Steve's bits and
  11. buckets, and rewrote it to fix or pave
  12. the road for the fixing of the following
  13. design bugs:
  14. - non-serialized context handles are
  15. unusable, and mixing serialized and
  16. non-serialized doesn't work
  17. - stealing context handles
  18. - gradual rundown of context handles
  19. - poor scalability of the context handle
  20. code (high cost of individual context
  21. handles) and contention on the context
  22. list
  23. -------------------------------------------------------------------- */
  24. #include <precomp.hxx>
  25. #include <context.hxx>
  26. #include <SContext.hxx>
  27. #include <HndlSvr.hxx>
  28. #include <CtxColl.hxx>
  29. #include <OSFPcket.hxx>
  30. #ifdef SCONTEXT_UNIT_TESTS
  31. #define CODE_COVERAGE_CHECK ASSERT(_NOT_COVERED_)
  32. inline ServerContextHandle *
  33. AllocateServerContextHandle (
  34. IN void *CtxGuard
  35. )
  36. {
  37. if ((GetRandomLong() % 9999) == 0)
  38. return NULL;
  39. return new ServerContextHandle(CtxGuard);
  40. }
  41. #ifdef GENERATE_STATUS_FAILURE
  42. #undef GENERATE_STATUS_FAILURE
  43. #endif // GENERATE_STATUS_FAILURE
  44. #define GENERATE_STATUS_FAILURE(s) \
  45. if ((GetRandomLong() % 9999) == 0) \
  46. s = RPC_S_OUT_OF_MEMORY;
  47. #define GENERATE_ADD_TO_COLLECTION_FAILURE(scall, ctx, status) \
  48. if ((GetRandomLong() % 9999) == 0) \
  49. { \
  50. scall->RemoveFromActiveContextHandles(ctx); \
  51. status = RPC_S_OUT_OF_MEMORY; \
  52. } \
  53. #define GENERATE_LOCK_FAILURE(ctx, th, wcptr, status) \
  54. if ((GetRandomLong() % 9999) == 0) \
  55. { \
  56. ctx->Lock.Unlock(wcptr); \
  57. th->FreeWaiterCache(wcptr); \
  58. status = RPC_S_OUT_OF_MEMORY; \
  59. } \
  60. #else
  61. #define CODE_COVERAGE_CHECK
  62. inline ServerContextHandle *
  63. AllocateServerContextHandle (
  64. IN void *CtxGuard
  65. )
  66. {
  67. return new ServerContextHandle(CtxGuard);
  68. }
  69. #define GENERATE_STATUS_FAILURE(s)
  70. #define GENERATE_ADD_TO_COLLECTION_FAILURE(scall, ctx, status)
  71. #define GENERATE_LOCK_FAILURE(ctx, th, wcptr, status)
  72. #endif
  73. WIRE_CONTEXT NullContext; // all zeros
  74. // per process variable defining what is the synchronization mode
  75. // for new context handles that don't have NDR level default
  76. unsigned int DontSerializeContext = 0;
  77. inline BOOL
  78. DoesContextHandleNeedExclusiveLock (
  79. IN unsigned long Flags
  80. )
  81. /*++
  82. Routine Description:
  83. Determines if a context handle needs exclusive lock or
  84. shared lock.
  85. Arguments:
  86. Flags - the flags given to the runtime by NDR.
  87. Return Value:
  88. non-zero if the context handle needs exclusive lock. FALSE
  89. otherwise. There is no failure for this function.
  90. --*/
  91. {
  92. // make sure exactly one flag is set
  93. ASSERT((Flags & RPC_CONTEXT_HANDLE_FLAGS) != RPC_CONTEXT_HANDLE_FLAGS);
  94. switch (Flags & RPC_CONTEXT_HANDLE_FLAGS)
  95. {
  96. case RPC_CONTEXT_HANDLE_SERIALIZE:
  97. // serialize is Exclusive
  98. return TRUE;
  99. case RPC_CONTEXT_HANDLE_DONT_SERIALIZE:
  100. // non-serialized is Shared
  101. return FALSE;
  102. }
  103. return (DontSerializeContext == 0);
  104. }
  105. inline ContextCollection *
  106. GetContextCollection (
  107. IN RPC_BINDING_HANDLE BindingHandle
  108. )
  109. /*++
  110. Routine Description:
  111. Gets the context collection from the call object and
  112. throws exception if this fails
  113. Arguments:
  114. BindingHandle - the scall
  115. Return Value:
  116. The collection. If getting the collection fails, an
  117. exception is thrown
  118. --*/
  119. {
  120. RPC_STATUS RpcStatus;
  121. ContextCollection *CtxCollection;
  122. RpcStatus = ((SCALL *)BindingHandle)->GetAssociationContextCollection(&CtxCollection);
  123. if (RpcStatus != RPC_S_OK)
  124. {
  125. RpcpRaiseException(RpcStatus);
  126. }
  127. return CtxCollection;
  128. }
  129. void
  130. DestroyContextCollection (
  131. IN ContextCollection *CtxCollection
  132. )
  133. /*++
  134. Routine Description:
  135. Destroys all context handles in the collection, regardless
  136. of guard value. The context handles are rundown before destruction in
  137. case they aren't used. If they are, the rundown needed flag is set
  138. Arguments:
  139. CtxCollection - the collection of context handles.
  140. --*/
  141. {
  142. DestroyContextHandlesForGuard((PVOID)CtxCollection,
  143. TRUE, // Rundown context handle
  144. NULL // nuke all contexts, regardless of guard value
  145. );
  146. delete CtxCollection;
  147. }
  148. void
  149. NDRSRundownContextHandle (
  150. IN ServerContextHandle *ContextHandle
  151. )
  152. /*++
  153. Routine Description:
  154. Runs down a context handle by calling the user's rundown routine
  155. Arguments:
  156. ContextHandle - the context handle
  157. Notes:
  158. This routine will only touch the UserRunDown and UserContext members
  159. of Context. This allows caller to make up ServerContextHandles on the fly
  160. and just fill in those two members.
  161. --*/
  162. {
  163. // Only contexts which have a rundown and
  164. // are valid are cleaned up.
  165. if ((ContextHandle->UserRunDown != NULL)
  166. && ContextHandle->UserContext)
  167. {
  168. RpcTryExcept
  169. {
  170. (*ContextHandle->UserRunDown)(ContextHandle->UserContext);
  171. }
  172. RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
  173. {
  174. #if DBG
  175. DbgPrint("Routine %p threw an exception %lu (0x%08lX) - this is illegal\n", ContextHandle->UserRunDown, RpcExceptionCode(), RpcExceptionCode());
  176. ASSERT(!"The rundown routine is not allowed to throw exceptions")
  177. #endif
  178. }
  179. RpcEndExcept
  180. }
  181. }
  182. void
  183. DestroyContextHandlesForGuard (
  184. IN PVOID CtxCollectionPtr,
  185. IN BOOL RundownContextHandle,
  186. IN void *CtxGuard OPTIONAL
  187. )
  188. /*++
  189. Routine Description:
  190. Each context handle in this association with the specified
  191. guard *and* a zero refcount will be cleaned up in a way
  192. determined by RundownContextHandle (see comment for
  193. RundownContextHandle below)
  194. Arguments:
  195. Context - the context for the association
  196. RundownContextHandle - if non-zero, rundown the context handle
  197. If zero, just cleanup the runtime part and the app will
  198. cleanup its part
  199. CtxGuard - the guard for which to cleanup context handles. If
  200. NULL, all context handles will be cleaned.
  201. Notes:
  202. Access to a context handle with lifetime refcount only is implicitly
  203. synchronized as this function will be called from two places -
  204. the association rundown, and RpcServerUnregisterIfEx. In the
  205. former case all connections are gone, and nobody can come
  206. and start using the context handle. In the second, the interface
  207. is unregistered, and again nobody can come and start using the
  208. context handle. Therefore each context handle with lifetime refcount only
  209. (and for the specified guard) is synrchronized. This is not
  210. true however for the list itself. In the association rundown
  211. case some context handles may be used asynchronously for parked
  212. calls, and we need to synchronize access to the list.
  213. If this is called from RpcServerUnregisterIfEx, then all context
  214. handles must have zero refcount as before unregistering the
  215. interface we must have waited for all calls to complete.
  216. --*/
  217. {
  218. ContextCollection *CtxCollection = (ContextCollection *)CtxCollectionPtr;
  219. LIST_ENTRY *NextListEntry;
  220. ServerContextHandle *CurrentContextHandle;
  221. // N.B. It may seem like there is a race condition here b/n the two
  222. // callers of this function - RpcServerUnregisterIfEx & the destructor
  223. // of the association, as they can be called independently, and start
  224. // partying on the same list. However, the RpcServerUnregisterIfEx
  225. // branch will either take the association mutex, or will add a
  226. // refcount on the association, so that the association can
  227. // never be destroyed while this function is called by the
  228. // RpcServerUnregisterIfEx branch, and it is implicitly
  229. // synchronized as far as destruction is concerned. We still
  230. // need to synchronize access to the list
  231. CtxCollection->Lock();
  232. // for each user created context for this assoication, check
  233. // whether it fits our criteria for destruction, and if yes,
  234. // destroy it. This is an abnormal case, so we don't care about
  235. // performance
  236. NextListEntry = NULL;
  237. while ((CurrentContextHandle = CtxCollection->GetNext(&NextListEntry)) != NULL)
  238. {
  239. // if we were asked to clean up for a specific context
  240. // guard, check whether there is a match
  241. if (CtxGuard && (CtxGuard != CurrentContextHandle->CtxGuard))
  242. {
  243. // there is no match - move on to the next
  244. continue;
  245. }
  246. // NextListEntry is valid even after destruction of the current
  247. // context handle - we don't need to worry about that
  248. CtxCollection->Remove(CurrentContextHandle);
  249. ASSERT((CurrentContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollectionMask) == 0);
  250. CurrentContextHandle->Flags |= ServerContextHandle::ContextRemovedFromCollectionMask
  251. | ServerContextHandle::ContextNeedsRundown;
  252. // remove the lifetime reference. If the refcount drops to 0, we can
  253. // do the cleanup.
  254. if (CurrentContextHandle->RemoveReference() == 0)
  255. {
  256. if (RundownContextHandle)
  257. {
  258. NDRSRundownContextHandle(CurrentContextHandle);
  259. }
  260. delete CurrentContextHandle;
  261. }
  262. // N.B. Don't touch the CurrentContextHandle below this. We have released
  263. // our refcount
  264. #if DBG
  265. // enforce it on checked
  266. CurrentContextHandle = NULL;
  267. #endif
  268. }
  269. CtxCollection->Unlock();
  270. }
  271. void
  272. FinishUsingContextHandle (
  273. IN SCALL *CallObject,
  274. IN ServerContextHandle *ContextHandle,
  275. IN BOOL fUserDeletedContext
  276. )
  277. /*++
  278. Routine Description:
  279. Perform functions commonly needed when execution returns
  280. from the server manager routine - if the context is in
  281. the list of active context handles, unlock it and remove it.
  282. Decrease the refcount, and if 0, remove the context from
  283. the collection, and if rundown as asked for, fire the rundown.
  284. Arguments:
  285. CallObject - the server-side call object (the scall)
  286. ContextHandle - the context handle
  287. fUserDeletedContext - non-zero if the user has deleted the context handle
  288. (i.e. set UserContext to NULL)
  289. --*/
  290. {
  291. ServerContextHandle *RemovedContextHandle;
  292. ContextCollection *CtxCollection = NULL;
  293. long LocalRefCount;
  294. RPC_STATUS RpcStatus;
  295. SWMRWaiter *WaiterCache;
  296. THREAD *Thread;
  297. BOOL fRemoveLifeTimeReference;
  298. RemovedContextHandle = CallObject->RemoveFromActiveContextHandles(ContextHandle);
  299. if (fUserDeletedContext)
  300. {
  301. RpcStatus = CallObject->GetAssociationContextCollection(&CtxCollection);
  302. // the getting of the collection must succeed here, as we have already
  303. // created it, and we're simply getting it
  304. ASSERT(RpcStatus == RPC_S_OK);
  305. fRemoveLifeTimeReference = FALSE;
  306. CtxCollection->Lock();
  307. // if the context is still in the collection, remove it and take
  308. // down the lifetime reference
  309. if ((ContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollectionMask) == 0)
  310. {
  311. ContextHandle->Flags |= ServerContextHandle::ContextRemovedFromCollectionMask;
  312. fRemoveLifeTimeReference = TRUE;
  313. CtxCollection->Remove(ContextHandle);
  314. }
  315. CtxCollection->Unlock();
  316. // do it outside the lock
  317. if (fRemoveLifeTimeReference)
  318. {
  319. LocalRefCount = ContextHandle->RemoveReference();
  320. ASSERT(LocalRefCount);
  321. }
  322. }
  323. // if we were able to extract it from the list of active context handles, it must
  324. // have been active, and thus needs unlocking
  325. if (RemovedContextHandle)
  326. {
  327. WaiterCache = NULL;
  328. ContextHandle->Lock.Unlock(&WaiterCache);
  329. Thread = ThreadSelf();
  330. if (Thread)
  331. {
  332. Thread->FreeWaiterCache(&WaiterCache);
  333. }
  334. else
  335. {
  336. SWMRLock::FreeWaiterCache(&WaiterCache);
  337. }
  338. }
  339. LocalRefCount = ContextHandle->RemoveReference();
  340. if (LocalRefCount == 0)
  341. {
  342. // if we were asked to rundown by the rundown code, do it.
  343. if (ContextHandle->Flags & ServerContextHandle::ContextNeedsRundownMask)
  344. {
  345. NDRSRundownContextHandle(ContextHandle);
  346. }
  347. ASSERT (ContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollectionMask);
  348. delete ContextHandle;
  349. }
  350. }
  351. ServerContextHandle *
  352. FindAndAddRefContextHandle (
  353. IN ContextCollection *CtxCollection,
  354. IN WIRE_CONTEXT *WireContext,
  355. IN PVOID CtxGuard,
  356. OUT BOOL *ContextHandleNewlyCreated
  357. )
  358. /*++
  359. Routine Description:
  360. Attempts to find the context handle for the given wire buffer,
  361. and if found, add a refcount to it, and return it.
  362. Arguments:
  363. CtxCollection - the context handle collection.
  364. WireContext - the on-the-wire representation of the context
  365. CtxGuard - the context guard - if NULL, then any context handle
  366. matches. If non-NULL, the context handle that matches the
  367. wire context must have the same context guard in order for it
  368. to be considered a match.
  369. ContextHandleNewlyCreated - a pointer to a boolean variable that
  370. will be set to non-zero if the context handle had the newly
  371. created flag set, or to FALSE if it didn't. If the return value
  372. is NULL, this is undefined.
  373. Return Value:
  374. The found context handle. NULL if no matching context handle was
  375. found.
  376. Notes:
  377. The newly created flag is always taken down regardless of other
  378. paremeters.
  379. --*/
  380. {
  381. ServerContextHandle *ContextHandle;
  382. BOOL LocalContextHandleNewlyCreated = FALSE;
  383. CtxCollection->Lock();
  384. ContextHandle = CtxCollection->Find(WireContext);
  385. // if we have found a context handle, and is from the same interface, or
  386. // we don't care from what interface it is, get it
  387. if (ContextHandle
  388. && (
  389. (ContextHandle->CtxGuard == CtxGuard)
  390. ||
  391. (CtxGuard == NULL)
  392. )
  393. )
  394. {
  395. ASSERT(ContextHandle->ReferenceCount);
  396. // the only two flags that can be possibly set here are ContextAllocState and/or
  397. // ContextNewlyCreated. ContextAllocState *must* be ContextCompletedAlloc.
  398. // ASSERT that
  399. ASSERT(ContextHandle->Flags & ServerContextHandle::ContextAllocState);
  400. ASSERT((ContextHandle->Flags &
  401. ~(ServerContextHandle::ContextAllocState | ServerContextHandle::ContextNewlyCreatedMask))
  402. == 0);
  403. ContextHandle->AddReference();
  404. if (ContextHandle->Flags & ServerContextHandle::ContextNewlyCreatedMask)
  405. {
  406. LocalContextHandleNewlyCreated = TRUE;
  407. }
  408. // take down the ContextNewlyCreated flag. Since we know that the only other
  409. // flag that can be set at this point is ContextNewlyCreated, a simple assignment
  410. // is sufficient
  411. ContextHandle->Flags = ServerContextHandle::ContextCompletedAlloc;
  412. }
  413. else
  414. {
  415. ContextHandle = NULL;
  416. }
  417. CtxCollection->Unlock();
  418. *ContextHandleNewlyCreated = LocalContextHandleNewlyCreated;
  419. return ContextHandle;
  420. }
  421. void
  422. NDRSContextHandlePostDispatchProcessing (
  423. IN SCALL *SCall,
  424. ServerContextHandle *CtxHandle
  425. )
  426. /*++
  427. Routine Description:
  428. Performs post dispatch processing needed for in only context
  429. handles. If the context handle was NULL on input, just delete
  430. it. Else, finish using it.
  431. Arguments:
  432. BindingHandle - the server-side binding handle (the scall)
  433. CtxHandle - the context handle
  434. --*/
  435. {
  436. if ((CtxHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextPendingAlloc)
  437. {
  438. CODE_COVERAGE_CHECK;
  439. // [in] only context handle that didn't get set
  440. delete CtxHandle;
  441. }
  442. else
  443. {
  444. FinishUsingContextHandle(SCall,
  445. CtxHandle,
  446. FALSE // fUserDeletedContextHandle
  447. );
  448. }
  449. }
  450. void
  451. NDRSContextEmergencyCleanup (
  452. IN RPC_BINDING_HANDLE BindingHandle,
  453. IN OUT NDR_SCONTEXT hContext,
  454. IN NDR_RUNDOWN UserRunDownIn,
  455. IN PVOID UserContext,
  456. IN BOOL ManagerRoutineException)
  457. /*++
  458. Routine Description:
  459. Perform emergency cleanup if the manager routine throws an exception,
  460. or marshalling fails, or an async call is aborted. In the process,
  461. if the context handle was actively used, it must finish using it.
  462. Arguments:
  463. BindingHandle - the server-side binding handle (the scall)
  464. hContext - the hContext created during unmarshalling.
  465. UserRunDownIn - if hContext is non-NULL, the user rundown from
  466. there will be used. This parameter will be used only if
  467. hContext is NULL.
  468. UserContext - the user context returned from the user. This will be
  469. set only in case 9 (see below). For all other cases, it will be 0
  470. ManagerRoutineException - non-zero if the exception was thrown
  471. from the manager routine.
  472. Notes:
  473. Here's the functionality matrix for this function:
  474. NDR will not call runtime in cases 1, 4 and 8
  475. User C Exc Handle Clea- Run- Finish Further use of
  476. N Unm Mar From: tx(To:) ept Type hCtx nup down UsingCH context handle on the client:
  477. -- ---- --- ----- ------- --- ------ ---- ----- ----- ------ -----------------------------
  478. 1 N NA NA NA NA NA NA N N N *As if the call was never made
  479. 2a Y N NULL NA Y NA !NULL Y N N *As if the call was never made
  480. 2b Y N !NULL NA Y NA !NULL N N Y *As if the call was never made
  481. 4 Y Y Any NULL N Any Any N N N *New context on the server
  482. 5a Y Y NULL !NULL N Any Marker Y Y N *As if the call was never made
  483. 5b Y Y !NULL !NULL N Any Marker N N N *To: value on the server
  484. 6a Y N NULL NULL N !ret !NULL Y N N *As if the call was never made
  485. 6b Y N !NULL NULL N !ret !NULL Y N Y *Invalid context from the server
  486. 7a Y N NULL !NULL N !ret !NULL Y Y N *As if the call was never made
  487. 7b Y N !NULL !NULL N !ret !NULL N N Y To: value on the server
  488. 8 Y N NA NULL N ret NULL N N N *NA (i.e. no retval)
  489. 9 Y N NA !NULL N ret NULL N Y N *NA (i.e. no retval)
  490. N.B. This routine throws exceptions on failure. Only datagram context handles have failure
  491. paths (aside from claiming critical section failures)
  492. --*/
  493. {
  494. ServerContextHandle *ContextHandle = (ServerContextHandle *)hContext;
  495. ContextCollection *CtxCollection;
  496. SCALL *SCall = (SCALL *)BindingHandle;
  497. BOOL ContextHandleNewlyCreated;
  498. DictionaryCursor cursor;
  499. PVOID Buffer;
  500. ASSERT(SCall->Type(SCALL_TYPE));
  501. LogEvent(SU_EXCEPT, EV_DELETE, ContextHandle, UserContext, ManagerRoutineException, 1, 0);
  502. // N.B. The following code doesn't make sense unless you have gone
  503. // through the notes in the comments. Please, read the notes before you
  504. // read this code
  505. if (ManagerRoutineException)
  506. {
  507. ASSERT(ContextHandle != NULL);
  508. // Cases 2a, 2b
  509. // Detect case 2a and cleanup runtime stuff for it
  510. if ((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextPendingAlloc)
  511. {
  512. // case 2a started with NULL context handle - no need to call
  513. // FinishUsingContextHandle
  514. delete ContextHandle;
  515. }
  516. else
  517. {
  518. // case 2b - we started with a non-NULL context handle - we need to finish
  519. // using it
  520. FinishUsingContextHandle(SCall,
  521. ContextHandle,
  522. FALSE // fUserDeletedContext
  523. );
  524. }
  525. }
  526. else if (ContextHandle == NULL)
  527. {
  528. ServerContextHandle TempItem(NULL);
  529. // Case 9
  530. // This must be a return value context handle, which the user has set to !NULL,
  531. // but we encountered marshalling problems before marshalling it. In this
  532. // case, simply rundown the user context.
  533. CODE_COVERAGE_CHECK;
  534. ASSERT(UserRunDownIn);
  535. ASSERT(UserContext);
  536. // create a temp context we can use for rundowns
  537. TempItem.UserRunDown = UserRunDownIn;
  538. TempItem.UserContext = UserContext;
  539. NDRSRundownContextHandle(&TempItem);
  540. }
  541. else if (ContextHandle == CONTEXT_HANDLE_AFTER_MARSHAL_MARKER)
  542. {
  543. // Cases 5a, 5b.
  544. // The context handle has been marshalled. Since we have released
  545. // all reference to the context handle, we cannot touch it. We need
  546. // to go back and search for the context handle again. It may have
  547. // been deleted either through a rundown, or by an attacker guessing
  548. // the context handle. Either way we want to handle it gracefully
  549. // Once we find the context handle (and get a lock on it), we need
  550. // to check if it has been used in the meantime, and if not, we
  551. // can proceed with the cleanup. If yes, just ignore it.
  552. CtxCollection = GetContextCollection(BindingHandle);
  553. // this must succeed as we have already obtained the collection once
  554. // during umarshalling
  555. ASSERT(CtxCollection);
  556. // in case 5b, we won't find anything, since we don't put buffers in
  557. // the collection. In this case, the loop will exit, and we'll be fine
  558. SCall->ActiveContextHandles.Reset(cursor);
  559. while ((Buffer = SCall->ActiveContextHandles.Next(cursor)) != 0)
  560. {
  561. // if this is not a buffer
  562. if (((ULONG_PTR)Buffer & SCALL::DictionaryEntryIsBuffer) == 0)
  563. {
  564. CODE_COVERAGE_CHECK;
  565. continue;
  566. }
  567. Buffer = (PVOID)((ULONG_PTR)Buffer & (~(SCALL::DictionaryEntryIsBuffer)));
  568. ContextHandle = FindAndAddRefContextHandle(CtxCollection,
  569. (WIRE_CONTEXT *)Buffer,
  570. NULL, // CtxGuard
  571. &ContextHandleNewlyCreated
  572. );
  573. if (ContextHandle)
  574. {
  575. if (ContextHandleNewlyCreated)
  576. {
  577. // Case 5a
  578. // this context handle was newly created - it cannot be used
  579. // by anybody, and it cannot be in the active calls collection
  580. // Therefore, it is safe to set the flag without holding the
  581. // lock and to call FinishUsingContextHandle, which will decrement
  582. // the ref count and will rundown & cleanup the context handle
  583. ContextHandle->Flags |= ServerContextHandle::ContextNeedsRundown;
  584. FinishUsingContextHandle(SCall,
  585. ContextHandle,
  586. TRUE // fUserDeletedContext
  587. );
  588. }
  589. else
  590. {
  591. CODE_COVERAGE_CHECK;
  592. // somebody managed to use the context handle - just finish off using it
  593. FinishUsingContextHandle(SCall,
  594. ContextHandle,
  595. FALSE // fUserDeletedContext
  596. );
  597. }
  598. }
  599. }
  600. }
  601. else if ((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextPendingAlloc)
  602. {
  603. // Cases 6a, 7a
  604. UserContext = ContextHandle->UserContext;
  605. if (UserContext)
  606. {
  607. // if we're in case 7a
  608. NDRSRundownContextHandle(ContextHandle);
  609. }
  610. // cases 6a, 7a
  611. delete ContextHandle;
  612. }
  613. else if (UserContext == NULL)
  614. {
  615. // Case 6b
  616. // this is the case where we have transition from !NULL to NULL
  617. // and marshalling hasn't passed yet
  618. ASSERT((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextCompletedAlloc);
  619. FinishUsingContextHandle(SCall,
  620. ContextHandle,
  621. TRUE // fUserDeletedContext
  622. );
  623. }
  624. else
  625. {
  626. UserContext = ContextHandle->UserContext;
  627. // Cases 7b
  628. ASSERT(UserContext != NULL);
  629. ASSERT((ContextHandle->Flags & ServerContextHandle::ContextAllocState) == ServerContextHandle::ContextCompletedAlloc);
  630. // the context handle was actively used - finish using it
  631. FinishUsingContextHandle(SCall,
  632. ContextHandle,
  633. FALSE // fUserDeletedContext
  634. );
  635. }
  636. }
  637. void
  638. ByteSwapWireContext(
  639. IN WIRE_CONTEXT *WireContext,
  640. IN unsigned char *DataRepresentation
  641. )
  642. /*++
  643. Routine Description:
  644. If necessary, the wire context will be byte swapped in place.
  645. Arguments:
  646. WireContext - Supplies the wire context be byte swapped and returns the
  647. resulting byte swapped context.
  648. DataRepresentation - Supplies the data representation of the supplied wire
  649. context.
  650. Notes:
  651. The wire context is guaranteed only 4 byte alignment.
  652. --*/
  653. {
  654. if ( ( DataConvertEndian(DataRepresentation) != 0 )
  655. && ( WireContext != 0 ) )
  656. {
  657. WireContext->ContextType = RpcpByteSwapLong(WireContext->ContextType);
  658. ByteSwapUuid((class RPC_UUID *)&WireContext->ContextUuid);
  659. }
  660. }
  661. NDR_SCONTEXT RPC_ENTRY
  662. NDRSContextUnmarshall (
  663. IN void *pBuff,
  664. IN unsigned long DataRepresentation
  665. )
  666. {
  667. return(NDRSContextUnmarshall2(I_RpcGetCurrentCallHandle(),
  668. pBuff,
  669. DataRepresentation,
  670. RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
  671. RPC_CONTEXT_HANDLE_DEFAULT_FLAGS));
  672. }
  673. NDR_SCONTEXT RPC_ENTRY
  674. NDRSContextUnmarshallEx(
  675. IN RPC_BINDING_HANDLE BindingHandle,
  676. IN void *pBuff,
  677. IN unsigned long DataRepresentation
  678. )
  679. {
  680. return(NDRSContextUnmarshall2(BindingHandle,
  681. pBuff,
  682. DataRepresentation,
  683. RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
  684. RPC_CONTEXT_HANDLE_DEFAULT_FLAGS));
  685. }
  686. // make sure the public structure and our private ones agree on where is the user context
  687. C_ASSERT(FIELD_OFFSET(ServerContextHandle, UserContext) == ((LONG)(LONG_PTR)&(((NDR_SCONTEXT)0)->userContext)));
  688. NDR_SCONTEXT RPC_ENTRY
  689. NDRSContextUnmarshall2 (
  690. IN RPC_BINDING_HANDLE BindingHandle,
  691. IN void *pBuff,
  692. IN unsigned long DataRepresentation,
  693. IN void *CtxGuard,
  694. IN unsigned long Flags
  695. )
  696. /*++
  697. Routine Description:
  698. Translate a NDR context to a handle
  699. The stub calls this routine to lookup a NDR wire format context into
  700. a context handle that can be used with the other context functions
  701. provided for the stubs use.
  702. Arguments:
  703. BindingHandle - the server side binding handle (scall)
  704. pBuff - pointer to the on-the-wire represenation of the context handle
  705. DataRepresentation - specifies the NDR data representation
  706. CtxGuard - non-NULL and interface unique id for strict context handles. NULL
  707. for non-strict context handles
  708. Flags - the flags for this operation.
  709. Return Value:
  710. A handle usable by NDR. Failures are reported by throwing exceptions.
  711. --*/
  712. {
  713. ServerContextHandle *ContextHandle;
  714. ServerContextHandle *TempContextHandle;
  715. ContextCollection *CtxCollection;
  716. WIRE_CONTEXT *WireContext;
  717. THREAD * Thread;
  718. RPC_STATUS RpcStatus;
  719. BOOL fFound;
  720. SCALL *SCall;
  721. SWMRWaiter *WaiterCache;
  722. BOOL Ignore;
  723. ByteSwapWireContext((WIRE_CONTEXT *) pBuff,
  724. (unsigned char *) &DataRepresentation);
  725. // even if we don't put it in the collection, make sure that
  726. // we call this function to force creating the collection
  727. // if it isn't there. If it fails, it will throw an exception
  728. CtxCollection = GetContextCollection(BindingHandle);
  729. WireContext = (WIRE_CONTEXT *)pBuff;
  730. if (!WireContext || WireContext->IsNullContext())
  731. {
  732. // Allocate a new context
  733. ContextHandle = AllocateServerContextHandle(CtxGuard);
  734. if (ContextHandle == NULL)
  735. {
  736. RpcpErrorAddRecord(EEInfoGCRuntime,
  737. RPC_S_OUT_OF_MEMORY,
  738. EEInfoDLNDRSContextUnmarshall2_30,
  739. sizeof(ServerContextHandle));
  740. RpcRaiseException(RPC_S_OUT_OF_MEMORY);
  741. }
  742. #if DBG
  743. if (CtxGuard == RPC_CONTEXT_HANDLE_DEFAULT_GUARD)
  744. RpcpInterfaceForCallDoesNotUseStrict(BindingHandle);
  745. #endif
  746. // we don't put it in the active context handle list, because
  747. // non of the APIs work on newly created context handles.
  748. // We don't put it in the context collection either, allowing
  749. // us to put it on unmarshalling only if it is non-zero.
  750. }
  751. else
  752. {
  753. ContextHandle = FindAndAddRefContextHandle(CtxCollection,
  754. WireContext,
  755. CtxGuard,
  756. &Ignore // ContextHandleNewlyCreated
  757. );
  758. if (!ContextHandle)
  759. {
  760. RpcpErrorAddRecord(EEInfoGCRuntime,
  761. RPC_X_SS_CONTEXT_MISMATCH,
  762. EEInfoDLNDRSContextUnmarshall2_10,
  763. WireContext->GetDebugULongLong1(),
  764. WireContext->GetDebugULongLong2()
  765. );
  766. RpcpRaiseException(RPC_X_SS_CONTEXT_MISMATCH);
  767. }
  768. SCall = (SCALL *)BindingHandle;
  769. RpcStatus = SCall->AddToActiveContextHandles(ContextHandle);
  770. GENERATE_ADD_TO_COLLECTION_FAILURE(SCall, ContextHandle, RpcStatus)
  771. if (RpcStatus != RPC_S_OK)
  772. {
  773. // remove the refcount and kill if it is the last one
  774. // Since this is not in the collection, no unlock
  775. // attempt will be made
  776. FinishUsingContextHandle(SCall,
  777. ContextHandle,
  778. FALSE // fUserDeletedContext
  779. );
  780. RpcpErrorAddRecord(EEInfoGCRuntime,
  781. RpcStatus,
  782. EEInfoDLNDRSContextUnmarshall2_50);
  783. RpcpRaiseException(RpcStatus);
  784. }
  785. Thread = RpcpGetThreadPointer();
  786. ASSERT(Thread);
  787. // here it must have been found. Find out what mode do we want this locked
  788. // in.
  789. if (DoesContextHandleNeedExclusiveLock(Flags))
  790. {
  791. Thread->GetWaiterCache(&WaiterCache, SCall, swmrwtWriter);
  792. RpcStatus = ContextHandle->Lock.LockExclusive(&WaiterCache);
  793. }
  794. else
  795. {
  796. Thread->GetWaiterCache(&WaiterCache, SCall, swmrwtReader);
  797. RpcStatus = ContextHandle->Lock.LockShared(&WaiterCache);
  798. }
  799. // in rare cases the lock operation may yield a cached waiter.
  800. // Make sure we handle it
  801. Thread->FreeWaiterCache(&WaiterCache);
  802. GENERATE_LOCK_FAILURE(ContextHandle, Thread, &WaiterCache, RpcStatus)
  803. if (RpcStatus != RPC_S_OK)
  804. {
  805. // first, we need to remove the context handle from the active calls
  806. // collection. This is necessary so that when we finish using it, it
  807. // doesn't attempt to unlock the handle (which it will attempt if the
  808. // handle is in the active contexts collection).
  809. TempContextHandle = SCall->RemoveFromActiveContextHandles(ContextHandle);
  810. ASSERT(TempContextHandle);
  811. FinishUsingContextHandle(SCall,
  812. ContextHandle,
  813. FALSE // fUserDeletedContext
  814. );
  815. RpcpErrorAddRecord(EEInfoGCRuntime,
  816. RpcStatus,
  817. EEInfoDLNDRSContextUnmarshall2_40);
  818. RpcpRaiseException(RpcStatus);
  819. }
  820. // did we pick a deleted context? Since we have a refcount, it can't go away
  821. // but it may very well have been marked deleted while we were waiting to get a lock
  822. // on the context handle. Check, and bail out if this is the case. This can
  823. // happen either if we encountered a rundown while waiting for the context
  824. // handle lock, or if an exclusive user before us deleted the context handle
  825. if (ContextHandle->Flags & ServerContextHandle::ContextRemovedFromCollection)
  826. {
  827. CODE_COVERAGE_CHECK;
  828. // since the context handle is in the active contexts collection,
  829. // this code will unlock it
  830. FinishUsingContextHandle(SCall,
  831. ContextHandle,
  832. FALSE // fUserDeletedContext
  833. );
  834. RpcpErrorAddRecord(EEInfoGCRuntime,
  835. RPC_X_SS_CONTEXT_MISMATCH,
  836. EEInfoDLNDRSContextUnmarshall2_20,
  837. WireContext->GetDebugULongLong1(),
  838. WireContext->GetDebugULongLong2()
  839. );
  840. RpcpRaiseException(RPC_X_SS_CONTEXT_MISMATCH);
  841. }
  842. }
  843. return ((NDR_SCONTEXT) ContextHandle);
  844. }
  845. void RPC_ENTRY
  846. NDRSContextMarshallEx (
  847. IN RPC_BINDING_HANDLE BindingHandle,
  848. IN OUT NDR_SCONTEXT hContext,
  849. OUT void *pBuffer,
  850. IN NDR_RUNDOWN userRunDownIn
  851. )
  852. {
  853. NDRSContextMarshall2(BindingHandle,
  854. hContext,
  855. pBuffer,
  856. userRunDownIn,
  857. RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
  858. RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
  859. }
  860. void RPC_ENTRY
  861. NDRSContextMarshall (
  862. IN OUT NDR_SCONTEXT hContext,
  863. OUT void *pBuff,
  864. IN NDR_RUNDOWN userRunDownIn
  865. )
  866. {
  867. NDRSContextMarshall2(I_RpcGetCurrentCallHandle(),
  868. hContext,
  869. pBuff,
  870. userRunDownIn,
  871. RPC_CONTEXT_HANDLE_DEFAULT_GUARD,
  872. RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
  873. }
  874. void RPC_ENTRY
  875. NDRSContextMarshall2(
  876. IN RPC_BINDING_HANDLE BindingHandle,
  877. IN OUT NDR_SCONTEXT hContext,
  878. OUT void *pBuff,
  879. IN NDR_RUNDOWN UserRunDownIn,
  880. IN void *CtxGuard,
  881. IN unsigned long
  882. )
  883. /*++
  884. Routine Description:
  885. Marshall the context handle. If set to NULL, it will be destroyed.
  886. Arguments:
  887. BindingHandle - the server side binding handle (the scall)
  888. hContext - the NDR handle of the context handle
  889. pBuff - buffer to marshell to
  890. UserRunDownIn - user function to be called when the rundown occurs
  891. CtxGuard - the magic id used to differentiate contexts created on
  892. different interfaces
  893. --*/
  894. {
  895. RPC_STATUS RpcStatus;
  896. ServerContextHandle *ContextHandle = (ServerContextHandle *)hContext;
  897. ContextCollection *CtxCollection;
  898. SCALL *SCall;
  899. BOOL fUserDeletedContextHandle;
  900. WIRE_CONTEXT *WireContext;
  901. SCall = (SCALL *)BindingHandle;
  902. // 0 for the flags is ContextPendingAlloc. If this is a new context, it
  903. // cannot have ContextNeedsRundown, because it's not in the collection.
  904. // It cannot have ContextRemovedFromCollection for the same reason.
  905. // Therefore, testing for 0 is sufficient to determine if this is a new
  906. // context
  907. if (ContextHandle->Flags == 0)
  908. {
  909. if (ContextHandle->UserContext == NULL)
  910. {
  911. // NULL to NULL - just delete the context handle
  912. ContextHandle->WireContext.CopyToBuffer(pBuff);
  913. delete ContextHandle;
  914. }
  915. else
  916. {
  917. // the context handle was just created - initialize the members that
  918. // weren't initialized before and insert it in the list
  919. ContextHandle->Flags = ServerContextHandle::ContextNewlyCreated
  920. | ServerContextHandle::ContextCompletedAlloc;
  921. // UserContext was already set by NDR
  922. ContextHandle->UserRunDown = UserRunDownIn;
  923. ASSERT(CtxGuard == ContextHandle->CtxGuard);
  924. // create the UUID
  925. RpcStatus = UuidCreateSequential((UUID *)&ContextHandle->WireContext.ContextUuid);
  926. GENERATE_STATUS_FAILURE(RpcStatus);
  927. if (RpcStatus == RPC_S_OK)
  928. {
  929. RpcStatus = SCall->AddToActiveContextHandles(
  930. (ServerContextHandle *) ((ULONG_PTR) pBuff | SCALL::DictionaryEntryIsBuffer));
  931. GENERATE_ADD_TO_COLLECTION_FAILURE(SCall, (ServerContextHandle *)((ULONG_PTR) pBuff & SCALL::DictionaryEntryIsBuffer), RpcStatus);
  932. }
  933. if ((RpcStatus != RPC_S_OK)
  934. && (RpcStatus != RPC_S_UUID_LOCAL_ONLY))
  935. {
  936. // run down the context handle
  937. NDRSRundownContextHandle(ContextHandle);
  938. // in a sense, marshalling failed
  939. delete ContextHandle;
  940. RpcpErrorAddRecord(EEInfoGCRuntime,
  941. RpcStatus,
  942. EEInfoDLNDRSContextMarshall2_10);
  943. RpcpRaiseException(RpcStatus);
  944. }
  945. ContextHandle->WireContext.CopyToBuffer(pBuff);
  946. CtxCollection = GetContextCollection(BindingHandle);
  947. // the context collection must have been created during
  948. // marshalling. This cannot fail here
  949. ASSERT(CtxCollection);
  950. CtxCollection->Lock();
  951. CtxCollection->Add(ContextHandle);
  952. CtxCollection->Unlock();
  953. }
  954. return;
  955. }
  956. fUserDeletedContextHandle = (ContextHandle->UserContext == NULL);
  957. if (fUserDeletedContextHandle)
  958. {
  959. WireContext = (WIRE_CONTEXT *)pBuff;
  960. WireContext->SetToNull();
  961. }
  962. else
  963. {
  964. ContextHandle->WireContext.CopyToBuffer(pBuff);
  965. }
  966. FinishUsingContextHandle(SCall, ContextHandle, fUserDeletedContextHandle);
  967. }
  968. void RPC_ENTRY
  969. I_RpcSsDontSerializeContext (
  970. void
  971. )
  972. /*++
  973. Routine Description:
  974. By default, context handles are serialized at the server. One customer
  975. who doesn't like that is the spooler. They make use of a single context
  976. handle by two threads at a time. This API is used to turn off serializing
  977. access to context handles for the process. It has been superseded by
  978. shared/exclusive access to the context, and must not be used anymore.
  979. --*/
  980. {
  981. DontSerializeContext = 1;
  982. }
  983. ServerContextHandle *
  984. NDRSConvertUserContextToContextHandle (
  985. IN SCALL *SCall,
  986. IN PVOID UserContext
  987. )
  988. /*++
  989. Routine Description:
  990. Finds the context handle corresponding to the specified
  991. UserContext and returns it.
  992. Arguments:
  993. SCall - the server side call object (the SCall)
  994. UserContext - the user context as given to the user by NDR. For
  995. in/out parameters, this will be a pointer to the UserContext
  996. field in the ServerContextHandle. For in parameters, this will
  997. be a value equal to the UserContext field in the
  998. ServerContextHandle. We don't know what type of context handle
  999. is this, so we have to search both, giving precedence to in/out
  1000. as they are more precise.
  1001. Return Value:
  1002. NULL if the UserContext couldn't be matched to any context handle.
  1003. The context handle pointer otherwise.
  1004. --*/
  1005. {
  1006. DictionaryCursor cursor;
  1007. ServerContextHandle *CurrentCtxHandle = NULL;
  1008. ServerContextHandle *UserContextMatchingCtxHandle = NULL;
  1009. if (SCall->InvalidHandle(SCALL_TYPE))
  1010. return (NULL);
  1011. SCall->ActiveContextHandles.Reset(cursor);
  1012. while ((CurrentCtxHandle = SCall->ActiveContextHandles.Next(cursor)) != 0)
  1013. {
  1014. // make sure this is not a buffer pointer for some reason
  1015. ASSERT (((ULONG_PTR)CurrentCtxHandle & SCALL::DictionaryEntryIsBuffer) == 0);
  1016. if (&CurrentCtxHandle->UserContext == UserContext)
  1017. {
  1018. return CurrentCtxHandle;
  1019. }
  1020. if (CurrentCtxHandle->UserContext == UserContext)
  1021. {
  1022. UserContextMatchingCtxHandle = CurrentCtxHandle;
  1023. }
  1024. }
  1025. // if we didn't find anything, this will be NULL and this is what we will
  1026. // return
  1027. return UserContextMatchingCtxHandle;
  1028. }
  1029. RPCRTAPI
  1030. RPC_STATUS
  1031. RPC_ENTRY
  1032. RpcSsContextLockExclusive (
  1033. IN RPC_BINDING_HANDLE ServerBindingHandle,
  1034. IN PVOID UserContext
  1035. )
  1036. /*++
  1037. Routine Description:
  1038. Lock the specified context for exclusive use.
  1039. Arguments:
  1040. ServerBindingHandle - the server side binding handle (the SCall)
  1041. UserContext - the user context as given to the user by NDR. For
  1042. in/out parameters, this will be a pointer to the UserContext
  1043. field in the ServerContextHandle. For in parameters, this will
  1044. be a value equal to the UserContext field in the
  1045. ServerContextHandle. We don't know what type of context handle
  1046. is this, so we have to search both, giving precedence to in/out
  1047. as they are more precise.
  1048. Return Value:
  1049. RPC_S_OK, ERROR_INVALID_HANDLE if the ServerBindingHandle or the
  1050. UserContext are invalid, a Win32 error if the locking failed,
  1051. or ERROR_MORE_WRITES if two readers attempted to upgrade to Exclusive
  1052. and one was evicted from its read lock (see the comment in
  1053. SWMR::ConvertToExclusive)
  1054. --*/
  1055. {
  1056. ServerContextHandle *ContextHandle;
  1057. SCALL *SCall = (SCALL *)ServerBindingHandle;
  1058. SWMRWaiter *WaiterCache;
  1059. THREAD *ThisThread;
  1060. RPC_STATUS RpcStatus;
  1061. if (SCall == NULL)
  1062. {
  1063. SCall = (SCALL *) RpcpGetThreadContext();
  1064. // if there is still no context, it will be handled by
  1065. // NDRSConvertUserContextToContextHandle below.
  1066. }
  1067. ContextHandle = NDRSConvertUserContextToContextHandle(SCall,
  1068. UserContext);
  1069. if (ContextHandle == NULL)
  1070. return ERROR_INVALID_HANDLE;
  1071. // try to get a waiter for the locking
  1072. ThisThread = ThreadSelf();
  1073. if (ThisThread == NULL)
  1074. return RPC_S_OUT_OF_MEMORY;
  1075. WaiterCache = NULL;
  1076. // we cannot allocate a waiter from the thread,
  1077. // because by definition we already have a lock, and
  1078. // the waiter for this lock may come from the thread.
  1079. // Since the thread tends to overwrite the previous
  1080. // waiter on recursive allocation, we don't want to
  1081. // do that.
  1082. RpcStatus = ContextHandle->Lock.ConvertToExclusive(&WaiterCache);
  1083. // if a waiter was produced, store it. Conversion
  1084. // operations can produce spurious waiters because of
  1085. // race conditions
  1086. ThisThread->FreeWaiterCache(&WaiterCache);
  1087. return RpcStatus;
  1088. }
  1089. RPCRTAPI
  1090. RPC_STATUS
  1091. RPC_ENTRY
  1092. RpcSsContextLockShared (
  1093. IN RPC_BINDING_HANDLE ServerBindingHandle,
  1094. IN PVOID UserContext
  1095. )
  1096. /*++
  1097. Routine Description:
  1098. Lock the specified context for shared use.
  1099. Arguments:
  1100. ServerBindingHandle - the server side binding handle (the SCall)
  1101. UserContext - the user context as given to the user by NDR. For
  1102. in/out parameters, this will be a pointer to the UserContext
  1103. field in the ServerContextHandle. For in parameters, this will
  1104. be a value equal to the UserContext field in the
  1105. ServerContextHandle. We don't know what type of context handle
  1106. is this, so we have to search both, giving precedence to in/out
  1107. as they are more precise.
  1108. Return Value:
  1109. RPC_S_OK, ERROR_INVALID_HANDLE if the ServerBindingHandle or the
  1110. UserContext are invalid, a Win32 error if the locking failed
  1111. --*/
  1112. {
  1113. ServerContextHandle *ContextHandle;
  1114. SCALL *SCall = (SCALL *)ServerBindingHandle;
  1115. SWMRWaiter *WaiterCache;
  1116. THREAD *ThisThread;
  1117. RPC_STATUS RpcStatus;
  1118. if (SCall == NULL)
  1119. {
  1120. SCall = (SCALL *) RpcpGetThreadContext();
  1121. // if there is still no context, it will be handled by
  1122. // NDRSConvertUserContextToContextHandle below.
  1123. }
  1124. ContextHandle = NDRSConvertUserContextToContextHandle(SCall,
  1125. UserContext);
  1126. if (ContextHandle == NULL)
  1127. return ERROR_INVALID_HANDLE;
  1128. // try to get a waiter for the locking
  1129. ThisThread = ThreadSelf();
  1130. if (ThisThread == NULL)
  1131. return RPC_S_OUT_OF_MEMORY;
  1132. WaiterCache = NULL;
  1133. // we cannot allocate a waiter from the thread,
  1134. // because by definition we already have a lock, and
  1135. // the waiter for this lock may come from the thread.
  1136. // Since the thread tends to overwrite the previous
  1137. // waiter on recursive allocation, we don't want to
  1138. // do that.
  1139. RpcStatus = ContextHandle->Lock.ConvertToShared(&WaiterCache,
  1140. TRUE // fSyncCacheUsed
  1141. );
  1142. // if a waiter was produced, store it. Conversion
  1143. // operations can produce spurious waiters because of
  1144. // race conditions
  1145. ThisThread->FreeWaiterCache(&WaiterCache);
  1146. return RpcStatus;
  1147. }