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.

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