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.

1014 lines
21 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1990 - 1999
  6. //
  7. // File: threads.cxx
  8. //
  9. //--------------------------------------------------------------------------
  10. /* --------------------------------------------------------------------
  11. Microsoft OS/2 LAN Manager
  12. Copyright(c) Microsoft Corp., 1990
  13. -------------------------------------------------------------------- */
  14. /* --------------------------------------------------------------------
  15. File: threads.cxx
  16. Description:
  17. This file provides a system independent threads package for use on the
  18. NT operating system.
  19. History:
  20. 5/24/90 [mikemon] File created.
  21. Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff
  22. -------------------------------------------------------------------- */
  23. #include <precomp.hxx>
  24. #include <rpcuuid.hxx>
  25. #include <binding.hxx>
  26. #include <handle.hxx>
  27. #include <hndlsvr.hxx>
  28. #include <lpcpack.hxx>
  29. #include <lpcsvr.hxx>
  30. #include <ProtBind.hxx>
  31. #include <lpcclnt.hxx>
  32. unsigned long DefaultThreadStackSize = 0;
  33. void
  34. PauseExecution (
  35. unsigned long milliseconds
  36. )
  37. {
  38. Sleep(milliseconds);
  39. }
  40. DLL::DLL (
  41. IN RPC_CHAR * DllName,
  42. OUT RPC_STATUS * Status
  43. )
  44. /*++
  45. Routine Description:
  46. We will load a dll and create an object to represent it.
  47. Arguments:
  48. DllName - Supplies the name of the dll to be loaded.
  49. Status - Returns the status of the operation. This will only be set
  50. if an error occurs. The possible error values are as follows.
  51. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to load
  52. the dll.
  53. RPC_S_INVALID_ARG - The requested dll can not be found.
  54. --*/
  55. {
  56. DWORD dwLastError;
  57. if (RpcpStringCompare(DllName, RPC_T("rpcrt4.dll")) == 0)
  58. {
  59. DllHandle = 0;
  60. return;
  61. }
  62. // Per the above condition, we can only be loading rpcrt4.dll on this path.
  63. // Since this is a known dll, we do not have to bother loading it explicitly
  64. // from the system folder.
  65. DllHandle = (void *)LoadLibrary((const RPC_SCHAR *)DllName);
  66. if ( DllHandle == 0 )
  67. {
  68. dwLastError = GetLastError();
  69. if ( (dwLastError == ERROR_NOT_ENOUGH_MEMORY)
  70. || (dwLastError == ERROR_COMMITMENT_LIMIT) )
  71. {
  72. *Status = RPC_S_OUT_OF_MEMORY;
  73. }
  74. else
  75. {
  76. *Status = RPC_S_INVALID_ARG;
  77. }
  78. }
  79. }
  80. DLL::~DLL (
  81. )
  82. /*++
  83. Routine Description:
  84. We just need to free the library, but only if was actually loaded.
  85. --*/
  86. {
  87. if ( DllHandle != 0 )
  88. {
  89. BOOL Status = FreeLibrary((HMODULE)DllHandle);
  90. ASSERT( Status );
  91. }
  92. }
  93. extern HANDLE GetCompletionPortHandleForThread(void);
  94. extern void ReleaseCompletionPortHandleForThread(HANDLE h);
  95. void *
  96. DLL::GetEntryPoint (
  97. IN char * Procedure
  98. )
  99. /*++
  100. Routine Description:
  101. We obtain the entry point for a specified procedure from this dll.
  102. Arguments:
  103. Procedure - Supplies the name of the entry point.
  104. Return Value:
  105. A pointer to the requested procedure will be returned if it can be
  106. found; otherwise, zero will be returned.
  107. --*/
  108. {
  109. FARPROC ProcedurePointer;
  110. if (DllHandle == 0)
  111. {
  112. if (strcmp(Procedure, "TransportLoad") == 0)
  113. return (PVOID)TransportLoad;
  114. else if (strcmp(Procedure, "GetCompletionPortHandleForThread") == 0)
  115. return (PVOID)GetCompletionPortHandleForThread;
  116. else if (strcmp(Procedure, "ReleaseCompletionPortHandleForThread") == 0)
  117. return (PVOID) ReleaseCompletionPortHandleForThread;
  118. ASSERT(0);
  119. return NULL;
  120. }
  121. ProcedurePointer = GetProcAddress((HINSTANCE)DllHandle, (LPSTR) Procedure);
  122. if ( ProcedurePointer == 0 )
  123. {
  124. ASSERT( GetLastError() == ERROR_PROC_NOT_FOUND );
  125. }
  126. return(ProcedurePointer);
  127. }
  128. unsigned long
  129. CurrentTimeSeconds (
  130. void
  131. )
  132. // Return the current time in seconds. When this time is counted
  133. // from is not particularly important since it is used as a delta.
  134. {
  135. return(GetTickCount()*1000L);
  136. }
  137. RPC_STATUS
  138. SetThreadStackSize (
  139. IN unsigned long ThreadStackSize
  140. )
  141. /*++
  142. Routine Description:
  143. We want to set the default thread stack size.
  144. Arguments:
  145. ThreadStackSize - Supplies the required thread stack size in bytes.
  146. Return Value:
  147. RPC_S_OK - We will always return this, because this routine always
  148. succeeds.
  149. --*/
  150. {
  151. if (DefaultThreadStackSize < ThreadStackSize)
  152. DefaultThreadStackSize = ThreadStackSize;
  153. return(RPC_S_OK);
  154. }
  155. long
  156. ThreadGetRpcCancelTimeout (
  157. )
  158. {
  159. THREAD * ThreadInfo = RpcpGetThreadPointer();
  160. ASSERT(ThreadInfo);
  161. return (ThreadInfo->CancelTimeout);
  162. }
  163. RPC_STATUS
  164. ThreadStartRoutine (
  165. IN THREAD * Thread
  166. )
  167. {
  168. RpcpSetThreadPointer(Thread);
  169. Thread->StartRoutine();
  170. return 0;
  171. }
  172. THREAD *
  173. ThreadSelfHelper (
  174. )
  175. {
  176. THREAD * Thread;
  177. RPC_STATUS Status = RPC_S_OK;
  178. Thread = new THREAD(&Status);
  179. if (Thread == 0)
  180. {
  181. return 0;
  182. }
  183. if (Status != RPC_S_OK)
  184. {
  185. delete Thread;
  186. return 0;
  187. }
  188. return Thread;
  189. }
  190. RPC_STATUS RPC_ENTRY
  191. RpcMgmtSetCancelTimeout(
  192. long Timeout
  193. )
  194. /*++
  195. Routine Description:
  196. An application will use this routine to set the cancel
  197. timeout for a thread.
  198. Arguments:
  199. Timeout - Supplies the cancel timeout value to set in the thread.
  200. 0 = No cancel timeout
  201. n = seconds
  202. RPC_C_CANCEL_INFINITE_TIMEOUT = Infinite
  203. Return Value:
  204. RPC_S_OK - The operation completed successfully.
  205. RPC_S_INVALID_TIMEOUT - The specified timeout value is invalid.
  206. --*/
  207. {
  208. InitializeIfNecessary();
  209. THREAD *Thread = ThreadSelf() ;
  210. if (!Thread)
  211. {
  212. return RPC_S_OUT_OF_MEMORY;
  213. }
  214. return Thread->SetCancelTimeout(Timeout);
  215. }
  216. RPC_STATUS
  217. RegisterForCancels(
  218. CALL * Call
  219. )
  220. {
  221. THREAD * ThreadInfo = ThreadSelf();
  222. if (ThreadInfo == 0)
  223. {
  224. return RPC_S_OUT_OF_MEMORY;
  225. }
  226. return ThreadInfo->RegisterForCancels(Call);
  227. }
  228. RPC_STATUS
  229. UnregisterForCancels(
  230. )
  231. {
  232. THREAD *ThreadInfo = RpcpGetThreadPointer();
  233. ASSERT(ThreadInfo);
  234. ThreadInfo->UnregisterForCancels ();
  235. return RPC_S_OK;
  236. }
  237. RPC_STATUS RPC_ENTRY
  238. RpcCancelThread(
  239. IN void * ThreadHandle
  240. )
  241. {
  242. if (!QueueUserAPC(CancelAPCRoutine, ThreadHandle, 0))
  243. {
  244. return RPC_S_ACCESS_DENIED;
  245. }
  246. return RPC_S_OK;
  247. }
  248. RPC_STATUS RPC_ENTRY
  249. RpcCancelThreadEx (
  250. IN void *ThreadHandle,
  251. IN long Timeout
  252. )
  253. {
  254. if (!QueueUserAPC(CancelExAPCRoutine, ThreadHandle, (UINT_PTR) Timeout))
  255. {
  256. return RPC_S_ACCESS_DENIED;
  257. }
  258. return RPC_S_OK;
  259. }
  260. RPC_STATUS RPC_ENTRY
  261. RpcServerTestCancel (
  262. IN RPC_BINDING_HANDLE BindingHandle OPTIONAL
  263. )
  264. /*++
  265. Function Name:RpcServerTestCancel
  266. Parameters:
  267. BindingHandle - This is the SCALL on which the server is trying to test for cancels.
  268. Description:
  269. New API used by a server to check if a call has been cancelled.
  270. If BindingHandle is NULL, the test is perfomed on the dispatched call
  271. on the thread on which it is called. Async servers need to call
  272. RpcServerTestCancel(RpcAsyncGetCallHandle(pAsync))
  273. Returns:
  274. RPC_S_OK: The call was cancelled
  275. RPC_S_CALL_IN_PROGRESS: The call was not cancelled
  276. RPC_S_INVALID_BINDING: The handle is invalid
  277. RPC_S_NO_CALL_ACTIVE: No call is active on this thread
  278. --*/
  279. {
  280. CALL *Call;
  281. if (BindingHandle == 0)
  282. {
  283. Call = (CALL *) RpcpGetThreadContext();
  284. if (Call == 0)
  285. {
  286. #if DBG
  287. PrintToDebugger("RPC: RpcServerTestCancel: no call active\n");
  288. #endif
  289. return RPC_S_NO_CALL_ACTIVE;
  290. }
  291. }
  292. else
  293. {
  294. Call = (CALL *) BindingHandle;
  295. if (Call->InvalidHandle(SCALL_TYPE))
  296. {
  297. #if DBG
  298. PrintToDebugger("RPC: RpcServerTestCancel: bad handle\n");
  299. #endif
  300. return RPC_S_INVALID_BINDING;
  301. }
  302. }
  303. if (Call->TestCancel())
  304. {
  305. return RPC_S_OK;
  306. }
  307. return RPC_S_CALL_IN_PROGRESS;
  308. }
  309. RPC_STATUS RPC_ENTRY
  310. RpcTestCancel(
  311. )
  312. /*++
  313. Function Name:RpcTestCancel
  314. Parameters:
  315. Description:
  316. This function is here only for backward compatibility. The new API to test
  317. for cancels is RpcServerTestCancel.
  318. Returns:
  319. --*/
  320. {
  321. return RpcServerTestCancel(0);
  322. }
  323. void RPC_ENTRY
  324. RpcServerYield ()
  325. {
  326. THREAD * ThreadInfo = RpcpGetThreadPointer();
  327. ASSERT(ThreadInfo);
  328. if (ThreadInfo)
  329. {
  330. ThreadInfo->YieldThread();
  331. }
  332. }
  333. THREAD::THREAD (
  334. IN THREAD_PROC Procedure,
  335. IN void * Parameter,
  336. OUT RPC_STATUS * Status
  337. #ifdef RPC_OLD_IO_PROTECTION
  338. ) : ProtectCount(1), ReleaseCount(0),
  339. #else
  340. ) :
  341. #endif
  342. ThreadEvent(Status)
  343. /*++
  344. Routine Description:
  345. We construct a thread in this method. It is a little bit weird, because
  346. we need to be able to clean things up if we cant create the thread.
  347. Arguments:
  348. Procedure - Supplies the procedure which the new thread should execute.
  349. Parameter - Supplies a parameter to be passed to the procedure.
  350. RpcStatus - Returns the status of the operation. This will be set to
  351. RPC_S_OUT_OF_THREADS if we can not create a new thread.
  352. --*/
  353. {
  354. unsigned long ThreadIdentifier;
  355. HANDLE ImpersonationToken, NewToken = 0;
  356. NTSTATUS NtStatus;
  357. CommonConstructor();
  358. SavedProcedure = Procedure;
  359. SavedParameter = Parameter;
  360. HandleToThread = 0;
  361. if (*Status != RPC_S_OK)
  362. {
  363. return;
  364. }
  365. if (IsServerSideDebugInfoEnabled())
  366. {
  367. DebugCell = (DebugThreadInfo *) AllocateCell(&DebugCellTag);
  368. if (DebugCell == NULL)
  369. {
  370. *Status = RPC_S_OUT_OF_MEMORY;
  371. return;
  372. }
  373. else
  374. {
  375. DebugCell->TypeHeader = 0;
  376. DebugCell->Type = dctThreadInfo;
  377. DebugCell->Status = dtsAllocated;
  378. }
  379. }
  380. //
  381. // If there is a token on the calling thread, null it out
  382. //
  383. if (OpenThreadToken (GetCurrentThread(),
  384. TOKEN_IMPERSONATE | TOKEN_QUERY,
  385. TRUE,
  386. &ImpersonationToken) == FALSE)
  387. {
  388. if ( GetLastError() == ERROR_NO_TOKEN )
  389. {
  390. ImpersonationToken = 0 ;
  391. }
  392. else
  393. {
  394. //
  395. // More interesting. There may be a token, and
  396. // we can't open it. Note that the anonymous token can't be opened
  397. // this way. Complain:
  398. //
  399. #if DBG
  400. PrintToDebugger( "RPC : OpenThreadToken returned %d\n", GetLastError() );
  401. #endif
  402. ASSERT(0);
  403. *Status = RPC_S_ACCESS_DENIED;
  404. return;
  405. }
  406. }
  407. else
  408. {
  409. NtStatus = NtSetInformationThread(NtCurrentThread(),
  410. ThreadImpersonationToken,
  411. &NewToken,
  412. sizeof(HANDLE));
  413. if (!NT_SUCCESS(NtStatus))
  414. {
  415. *Status = RPC_S_ACCESS_DENIED;
  416. #if DBG
  417. PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
  418. #endif
  419. ASSERT(0);
  420. CloseHandle(ImpersonationToken);
  421. return;
  422. }
  423. }
  424. HandleToThread = CreateThread(0, DefaultThreadStackSize,
  425. (LPTHREAD_START_ROUTINE) ThreadStartRoutine,
  426. this, 0, &ThreadIdentifier);
  427. if ( HandleToThread == 0 )
  428. {
  429. *Status = RPC_S_OUT_OF_THREADS;
  430. }
  431. if (ImpersonationToken)
  432. {
  433. //
  434. // If there was a token, restore it.
  435. //
  436. NtStatus = NtSetInformationThread(NtCurrentThread(),
  437. ThreadImpersonationToken,
  438. &ImpersonationToken,
  439. sizeof(HANDLE));
  440. #if DBG
  441. if (!NT_SUCCESS(NtStatus))
  442. {
  443. PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
  444. }
  445. #endif // DBG
  446. CloseHandle(ImpersonationToken);
  447. }
  448. }
  449. THREAD::THREAD (
  450. OUT RPC_STATUS * Status
  451. #ifdef RPC_OLD_IO_PROTECTION
  452. ) : ProtectCount(1), ReleaseCount(0),
  453. #else
  454. ) :
  455. #endif
  456. ThreadEvent(Status)
  457. /*++
  458. Routine Description:
  459. This overloaded constructor is called only by the main app thread.
  460. this is needed because in WMSG we will be dispatching in the
  461. context of main app thread.
  462. Arguments:
  463. RpcStatus - Returns the status of the operation
  464. --*/
  465. {
  466. HANDLE hProcess ;
  467. CommonConstructor();
  468. SavedProcedure = 0;
  469. SavedParameter = 0;
  470. HandleToThread = 0;
  471. if (*Status != RPC_S_OK)
  472. {
  473. return;
  474. }
  475. hProcess = GetCurrentProcess() ;
  476. if (!DuplicateHandle(hProcess,
  477. GetCurrentThread(),
  478. hProcess,
  479. &HandleToThread,
  480. 0,
  481. FALSE,
  482. DUPLICATE_SAME_ACCESS))
  483. {
  484. *Status = RPC_S_OUT_OF_MEMORY ;
  485. return;
  486. }
  487. RpcpSetThreadPointer(this);
  488. *Status = RPC_S_OK ;
  489. }
  490. void
  491. THREAD::CommonConstructor (
  492. )
  493. /*++
  494. Function Name:CommonConstructor
  495. Parameters:
  496. Description:
  497. Returns:
  498. --*/
  499. {
  500. CancelTimeout = RPC_C_CANCEL_INFINITE_TIMEOUT;
  501. Context = 0;
  502. SecurityContext = 0;
  503. ClearCallCancelledFlag();
  504. fAsync = FALSE;
  505. ExtendedStatus = RPC_S_OK;
  506. DebugCell = NULL;
  507. ThreadEEInfo = NULL;
  508. NDRSlot = NULL;
  509. CachedLrpcCall = NULL;
  510. LastSuccessfullyDestroyedContext = NULL;
  511. CachedWaiterPtr = NULL;
  512. CachedWaiterAvailable = TRUE;
  513. CachedEEInfoBlock = NULL;
  514. ParametersOfCachedEEInfo = 0;
  515. ActiveCall = 0;
  516. for (int i = 0; i < 4; i++)
  517. {
  518. BufferCache[i].pList = 0;
  519. BufferCache[i].cBlocks = 0;
  520. }
  521. #ifdef CHECK_MUTEX_INVERSION
  522. ConnectionMutexHeld = 0;
  523. #endif
  524. }
  525. THREAD::~THREAD (
  526. )
  527. {
  528. ASSERT (0 == SecurityContext);
  529. #ifdef RPC_OLD_IO_PROTECTION
  530. ASSERT(ProtectCount == ReleaseCount.GetInteger());
  531. #endif
  532. if (CachedWaiterPtr && (CachedWaiterPtr != &CachedWaiter))
  533. SWMRLock::FreeWaiterCache(&CachedWaiterPtr);
  534. if (DebugCell != NULL)
  535. {
  536. FreeCell(DebugCell, &DebugCellTag);
  537. }
  538. if ( HandleToThread != 0 )
  539. {
  540. CloseHandle(HandleToThread);
  541. HandleToThread = UlongToPtr(0xbaadbaad);
  542. }
  543. if (CachedLrpcCall)
  544. {
  545. delete CachedLrpcCall;
  546. }
  547. if (ThreadEEInfo)
  548. PurgeEEInfo();
  549. if (CachedEEInfoBlock)
  550. FreeEEInfoRecordShallow(CachedEEInfoBlock);
  551. gBufferCache->ThreadDetach(this);
  552. }
  553. void *
  554. THREAD::ThreadHandle (
  555. )
  556. /*++
  557. Function Name:ThreadHandle
  558. Parameters:
  559. Description:
  560. Returns:
  561. --*/
  562. {
  563. while ( HandleToThread == 0 )
  564. {
  565. PauseExecution(100L);
  566. }
  567. return(HandleToThread);
  568. }
  569. RPC_STATUS
  570. THREAD::SetCancelTimeout (
  571. IN long Timeout
  572. )
  573. /*++
  574. Function Name:SetCancelTimeout
  575. Parameters:
  576. Description:
  577. Returns:
  578. --*/
  579. {
  580. CancelTimeout = Timeout;
  581. return RPC_S_OK;
  582. }
  583. void
  584. SetExtendedError (
  585. IN RPC_STATUS Status
  586. )
  587. {
  588. THREAD *pThread = ThreadSelf();
  589. if (pThread == 0)
  590. {
  591. return;
  592. }
  593. pThread->SetExtendedError(Status);
  594. }
  595. RPC_STATUS
  596. I_RpcGetExtendedError (
  597. )
  598. {
  599. THREAD *pThread = ThreadSelf();
  600. if (pThread == 0)
  601. {
  602. return RPC_S_OUT_OF_MEMORY;
  603. }
  604. return pThread->GetExtendedError();
  605. }
  606. HANDLE RPC_ENTRY
  607. I_RpcTransGetThreadEvent(
  608. )
  609. /*++
  610. Routine Description:
  611. Returns the receive event specific to this thread.
  612. Arguments:
  613. None
  614. Return Value:
  615. Returns the send event. This function will not fail
  616. --*/
  617. {
  618. THREAD *pThis = RpcpGetThreadPointer();
  619. ASSERT(pThis);
  620. ASSERT(pThis->ThreadEvent.EventHandle);
  621. return(pThis->ThreadEvent.EventHandle);
  622. }
  623. RPC_STATUS
  624. THREAD::CancelCall (
  625. IN BOOL fTimeoutValid,
  626. IN long Timeout
  627. )
  628. {
  629. RPC_STATUS Status;
  630. if (fTimeoutValid)
  631. {
  632. CancelTimeout = Timeout;
  633. }
  634. SetCallCancelledFlag();
  635. if (ActiveCall)
  636. {
  637. Status = ActiveCall->Cancel(HandleToThread);
  638. }
  639. else
  640. {
  641. Status = RPC_S_NO_CALL_ACTIVE;
  642. }
  643. return Status;
  644. }
  645. RPC_STATUS
  646. THREAD::RegisterForCancels (
  647. IN CALL *Call
  648. )
  649. {
  650. Call->NestingCall = ActiveCall;
  651. ActiveCall = Call;
  652. return RPC_S_OK;
  653. }
  654. void
  655. THREAD::UnregisterForCancels (
  656. )
  657. {
  658. if (ActiveCall)
  659. {
  660. ActiveCall = ActiveCall->NestingCall;
  661. }
  662. //
  663. // Need to think about when we should cancel the next call if ActiveCall != 0
  664. //
  665. }
  666. void
  667. THREAD::PurgeEEInfo (
  668. void
  669. )
  670. {
  671. ASSERT(ThreadEEInfo != NULL);
  672. FreeEEInfoChain(ThreadEEInfo);
  673. ThreadEEInfo = NULL;
  674. }
  675. VOID RPC_ENTRY
  676. CancelAPCRoutine (
  677. IN ULONG_PTR Timeout
  678. )
  679. {
  680. RPC_STATUS Status;
  681. THREAD *Thread = ThreadSelf() ;
  682. if (Thread == 0)
  683. {
  684. return;
  685. }
  686. Status = Thread->CancelCall();
  687. #if DBG
  688. if (Status != RPC_S_OK)
  689. {
  690. PrintToDebugger("RPC: CancelCall failed %d\n", Status);
  691. }
  692. #endif
  693. }
  694. void
  695. THREAD::GetWaiterCache (
  696. OUT SWMRWaiter **WaiterCache,
  697. IN SCALL *SCall,
  698. IN SWMRWaiterType WaiterType
  699. )
  700. {
  701. ASSERT(WaiterCache != NULL);
  702. if (CachedWaiterPtr)
  703. {
  704. LogEvent(SU_THREAD, EV_POP, CachedWaiterPtr->hEvent, CachedWaiterPtr, 0, 1, 1);
  705. // if we have something in the cache, we may as well use it
  706. *WaiterCache = CachedWaiterPtr;
  707. CachedWaiterPtr = NULL;
  708. }
  709. else if ((WaiterType == swmrwtWriter) && SCall->IsSyncCall() && CachedWaiterAvailable)
  710. {
  711. // the cache is empty, but this is exclusive, sync usage, so we can
  712. // borrow the thread event and cook up a waiter. We also make sure that
  713. // we aren't using the CachedWaiter already (such as in the multi-context handle case).
  714. CachedWaiterAvailable = FALSE;
  715. SWMRWaiter::CookupWaiterFromEventAndBuffer(&CachedWaiter, WaiterType, ThreadEvent.EventHandle);
  716. *WaiterCache = &CachedWaiter;
  717. }
  718. else
  719. {
  720. // the cache is empty and this is either shared or async usage so we can't
  721. // use the thread buffer/event.
  722. *WaiterCache = NULL;
  723. }
  724. }
  725. void
  726. THREAD::FreeWaiterCache (
  727. IN OUT SWMRWaiter **WaiterCache
  728. )
  729. {
  730. ASSERT(WaiterCache != NULL);
  731. if (*WaiterCache == &CachedWaiter)
  732. {
  733. ASSERT(CachedWaiterAvailable == FALSE);
  734. // We are done using the CachedWaiter, indicate that it is avaliable
  735. CachedWaiterAvailable = TRUE;
  736. }
  737. else if (*WaiterCache)
  738. // if we got something in the cache and this is not a cooked up item
  739. {
  740. if (CachedWaiterPtr)
  741. {
  742. // if our cache is already full, just free the new waiter
  743. SWMRLock::FreeWaiterCache(WaiterCache);
  744. }
  745. else
  746. {
  747. // store it
  748. CachedWaiterPtr = *WaiterCache;
  749. LogEvent(SU_THREAD, EV_PUSH, CachedWaiterPtr->hEvent, CachedWaiterPtr, 0, 1, 1);
  750. *WaiterCache = NULL;
  751. }
  752. }
  753. }
  754. VOID RPC_ENTRY
  755. CancelExAPCRoutine (
  756. IN ULONG_PTR Timeout
  757. )
  758. {
  759. RPC_STATUS Status;
  760. THREAD *Thread = ThreadSelf() ;
  761. if (Thread == 0)
  762. {
  763. return;
  764. }
  765. Status = Thread->CancelCall(TRUE, (long) Timeout);
  766. #if DBG
  767. if (Status != RPC_S_OK)
  768. {
  769. PrintToDebugger("RPC: CancelCall failed %d\n", Status);
  770. }
  771. #endif
  772. }
  773. void
  774. RpcpRaiseException (
  775. IN RPC_STATUS exception
  776. )
  777. {
  778. if ( exception == STATUS_ACCESS_VIOLATION )
  779. {
  780. exception = ERROR_NOACCESS;
  781. }
  782. RaiseException(exception,
  783. EXCEPTION_NONCONTINUABLE,
  784. 0,
  785. 0);
  786. ASSERT(0);
  787. }
  788. void RPC_ENTRY
  789. RpcRaiseException (
  790. IN RPC_STATUS exception
  791. )
  792. {
  793. NukeStaleEEInfoIfNecessary(exception);
  794. RpcpRaiseException(exception);
  795. }