1002 lines
19 KiB
C++
1002 lines
19 KiB
C++
//+-------------------------------------------------------------------------
|
||
//
|
||
// Microsoft Windows
|
||
//
|
||
// Copyright (C) Microsoft Corporation, 1990 - 1999
|
||
//
|
||
// File: threads.cxx
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
/* --------------------------------------------------------------------
|
||
|
||
Microsoft OS/2 LAN Manager
|
||
Copyright(c) Microsoft Corp., 1990
|
||
|
||
-------------------------------------------------------------------- */
|
||
/* --------------------------------------------------------------------
|
||
|
||
File: threads.cxx
|
||
|
||
Description:
|
||
|
||
This file provides a system independent threads package for use on the
|
||
NT operating system.
|
||
|
||
History:
|
||
5/24/90 [mikemon] File created.
|
||
Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff
|
||
|
||
-------------------------------------------------------------------- */
|
||
|
||
#include <precomp.hxx>
|
||
|
||
#include <rpcuuid.hxx>
|
||
#include <binding.hxx>
|
||
#include <handle.hxx>
|
||
#include <hndlsvr.hxx>
|
||
#include <lpcpack.hxx>
|
||
#include <lpcsvr.hxx>
|
||
#include <ProtBind.hxx>
|
||
#include <lpcclnt.hxx>
|
||
|
||
unsigned long DefaultThreadStackSize = 0;
|
||
|
||
void
|
||
PauseExecution (
|
||
unsigned long milliseconds
|
||
)
|
||
{
|
||
|
||
Sleep(milliseconds);
|
||
|
||
}
|
||
|
||
|
||
DLL::DLL (
|
||
IN RPC_CHAR * DllName,
|
||
OUT RPC_STATUS * Status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We will load a dll and create an object to represent it.
|
||
|
||
Arguments:
|
||
|
||
DllName - Supplies the name of the dll to be loaded.
|
||
|
||
Status - Returns the status of the operation. This will only be set
|
||
if an error occurs. The possible error values are as follows.
|
||
|
||
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to load
|
||
the dll.
|
||
|
||
RPC_S_INVALID_ARG - The requested dll can not be found.
|
||
|
||
--*/
|
||
{
|
||
DWORD dwLastError;
|
||
|
||
if (RpcpStringCompare(DllName, RPC_T("rpcrt4.dll")) == 0)
|
||
{
|
||
DllHandle = 0;
|
||
return;
|
||
}
|
||
|
||
DllHandle = (void *)LoadLibrary((const RPC_SCHAR *)DllName);
|
||
if ( DllHandle == 0 )
|
||
{
|
||
dwLastError = GetLastError();
|
||
if ( (dwLastError == ERROR_NOT_ENOUGH_MEMORY)
|
||
|| (dwLastError == ERROR_COMMITMENT_LIMIT) )
|
||
{
|
||
*Status = RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
else
|
||
{
|
||
*Status = RPC_S_INVALID_ARG;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
DLL::~DLL (
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We just need to free the library, but only if was actually loaded.
|
||
|
||
--*/
|
||
{
|
||
if ( DllHandle != 0 )
|
||
{
|
||
BOOL Status = FreeLibrary((HMODULE)DllHandle);
|
||
ASSERT( Status );
|
||
}
|
||
}
|
||
|
||
|
||
extern HANDLE GetCompletionPortHandleForThread(void);
|
||
extern void ReleaseCompletionPortHandleForThread(HANDLE h);
|
||
|
||
|
||
void *
|
||
DLL::GetEntryPoint (
|
||
IN char * Procedure
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We obtain the entry point for a specified procedure from this dll.
|
||
|
||
Arguments:
|
||
|
||
Procedure - Supplies the name of the entry point.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the requested procedure will be returned if it can be
|
||
found; otherwise, zero will be returned.
|
||
|
||
--*/
|
||
{
|
||
FARPROC ProcedurePointer;
|
||
|
||
if (DllHandle == 0)
|
||
{
|
||
if (strcmp(Procedure, "TransportLoad") == 0)
|
||
return (PVOID)TransportLoad;
|
||
else if (strcmp(Procedure, "GetCompletionPortHandleForThread") == 0)
|
||
return (PVOID)GetCompletionPortHandleForThread;
|
||
else if (strcmp(Procedure, "ReleaseCompletionPortHandleForThread") == 0)
|
||
return (PVOID) ReleaseCompletionPortHandleForThread;
|
||
|
||
ASSERT(0);
|
||
return NULL;
|
||
}
|
||
|
||
ProcedurePointer = GetProcAddress((HINSTANCE)DllHandle, (LPSTR) Procedure);
|
||
if ( ProcedurePointer == 0 )
|
||
{
|
||
ASSERT( GetLastError() == ERROR_PROC_NOT_FOUND );
|
||
}
|
||
|
||
return(ProcedurePointer);
|
||
}
|
||
|
||
unsigned long
|
||
CurrentTimeSeconds (
|
||
void
|
||
)
|
||
// Return the current time in seconds. When this time is counted
|
||
// from is not particularly important since it is used as a delta.
|
||
{
|
||
|
||
return(GetTickCount()*1000L);
|
||
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
SetThreadStackSize (
|
||
IN unsigned long ThreadStackSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We want to set the default thread stack size.
|
||
|
||
Arguments:
|
||
|
||
ThreadStackSize - Supplies the required thread stack size in bytes.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK - We will always return this, because this routine always
|
||
succeeds.
|
||
|
||
--*/
|
||
{
|
||
if (DefaultThreadStackSize < ThreadStackSize)
|
||
DefaultThreadStackSize = ThreadStackSize;
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
long
|
||
ThreadGetRpcCancelTimeout (
|
||
)
|
||
{
|
||
|
||
THREAD * ThreadInfo = RpcpGetThreadPointer();
|
||
|
||
ASSERT(ThreadInfo);
|
||
return (ThreadInfo->CancelTimeout);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
ThreadStartRoutine (
|
||
IN THREAD * Thread
|
||
)
|
||
{
|
||
RpcpSetThreadPointer(Thread);
|
||
|
||
Thread->StartRoutine();
|
||
|
||
return 0;
|
||
}
|
||
|
||
THREAD *
|
||
ThreadSelfHelper (
|
||
)
|
||
{
|
||
THREAD * Thread;
|
||
RPC_STATUS Status = RPC_S_OK;
|
||
|
||
Thread = new THREAD(&Status);
|
||
if (Thread == 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (Status != RPC_S_OK)
|
||
{
|
||
delete Thread;
|
||
return 0;
|
||
}
|
||
|
||
return Thread;
|
||
}
|
||
|
||
|
||
RPC_STATUS RPC_ENTRY
|
||
RpcMgmtSetCancelTimeout(
|
||
long Timeout
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
An application will use this routine to set the cancel
|
||
timeout for a thread.
|
||
|
||
Arguments:
|
||
|
||
Timeout - Supplies the cancel timeout value to set in the thread.
|
||
0 = No cancel timeout
|
||
n = seconds
|
||
RPC_C_CANCEL_INFINITE_TIMEOUT = Infinite
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK - The operation completed successfully.
|
||
|
||
RPC_S_INVALID_TIMEOUT - The specified timeout value is invalid.
|
||
|
||
--*/
|
||
{
|
||
InitializeIfNecessary();
|
||
|
||
THREAD *Thread = ThreadSelf() ;
|
||
|
||
if (!Thread)
|
||
{
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
|
||
return Thread->SetCancelTimeout(Timeout);
|
||
}
|
||
|
||
RPC_STATUS
|
||
RegisterForCancels(
|
||
CALL * Call
|
||
)
|
||
{
|
||
THREAD * ThreadInfo = ThreadSelf();
|
||
if (ThreadInfo == 0)
|
||
{
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
|
||
return ThreadInfo->RegisterForCancels(Call);
|
||
}
|
||
|
||
RPC_STATUS
|
||
UnregisterForCancels(
|
||
)
|
||
{
|
||
THREAD *ThreadInfo = RpcpGetThreadPointer();
|
||
|
||
ASSERT(ThreadInfo);
|
||
|
||
ThreadInfo->UnregisterForCancels ();
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
|
||
|
||
RPC_STATUS RPC_ENTRY
|
||
RpcCancelThread(
|
||
IN void * ThreadHandle
|
||
)
|
||
{
|
||
if (!QueueUserAPC(CancelAPCRoutine, ThreadHandle, 0))
|
||
{
|
||
return RPC_S_ACCESS_DENIED;
|
||
}
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
RPC_STATUS RPC_ENTRY
|
||
RpcCancelThreadEx (
|
||
IN void *ThreadHandle,
|
||
IN long Timeout
|
||
)
|
||
{
|
||
if (!QueueUserAPC(CancelExAPCRoutine, ThreadHandle, (UINT_PTR) Timeout))
|
||
{
|
||
return RPC_S_ACCESS_DENIED;
|
||
}
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
|
||
RPC_STATUS RPC_ENTRY
|
||
RpcServerTestCancel (
|
||
IN RPC_BINDING_HANDLE BindingHandle OPTIONAL
|
||
)
|
||
/*++
|
||
Function Name:RpcServerTestCancel
|
||
|
||
Parameters:
|
||
BindingHandle - This is the SCALL on which the server is trying to test for cancels.
|
||
|
||
Description:
|
||
New API used by a server to check if a call has been cancelled.
|
||
If BindingHandle is NULL, the test is perfomed on the dispatched call
|
||
on the thread on which it is called. Async servers need to call
|
||
RpcServerTestCancel(RpcAsyncGetCallHandle(pAsync))
|
||
|
||
Returns:
|
||
RPC_S_OK: The call was cancelled
|
||
RPC_S_CALL_IN_PROGRESS: The call was not cancelled
|
||
RPC_S_INVALID_BINDING: The handle is invalid
|
||
RPC_S_NO_CALL_ACTIVE: No call is active on this thread
|
||
--*/
|
||
{
|
||
CALL *Call;
|
||
|
||
if (BindingHandle == 0)
|
||
{
|
||
Call = (CALL *) RpcpGetThreadContext();
|
||
if (Call == 0)
|
||
{
|
||
#if DBG
|
||
PrintToDebugger("RPC: RpcServerTestCancel: no call active\n");
|
||
#endif
|
||
return RPC_S_NO_CALL_ACTIVE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Call = (CALL *) BindingHandle;
|
||
if (Call->InvalidHandle(SCALL_TYPE))
|
||
{
|
||
#if DBG
|
||
PrintToDebugger("RPC: RpcServerTestCancel: bad handle\n");
|
||
#endif
|
||
return RPC_S_INVALID_BINDING;
|
||
}
|
||
}
|
||
|
||
if (Call->TestCancel())
|
||
{
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
return RPC_S_CALL_IN_PROGRESS;
|
||
}
|
||
|
||
|
||
RPC_STATUS RPC_ENTRY
|
||
RpcTestCancel(
|
||
)
|
||
/*++
|
||
Function Name:RpcTestCancel
|
||
|
||
Parameters:
|
||
|
||
Description:
|
||
This function is here only for backward compatibility. The new API to test
|
||
for cancels is RpcServerTestCancel.
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
return RpcServerTestCancel(0);
|
||
}
|
||
|
||
void RPC_ENTRY
|
||
RpcServerYield ()
|
||
{
|
||
THREAD * ThreadInfo = RpcpGetThreadPointer();
|
||
ASSERT(ThreadInfo);
|
||
|
||
if (ThreadInfo)
|
||
{
|
||
ThreadInfo->YieldThread();
|
||
}
|
||
}
|
||
|
||
|
||
THREAD::THREAD (
|
||
IN THREAD_PROC Procedure,
|
||
IN void * Parameter,
|
||
OUT RPC_STATUS * Status
|
||
#ifdef RPC_OLD_IO_PROTECTION
|
||
) : ProtectCount(1), ReleaseCount(0),
|
||
#else
|
||
) :
|
||
#endif
|
||
ThreadEvent(Status)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We construct a thread in this method. It is a little bit weird, because
|
||
we need to be able to clean things up if we cant create the thread.
|
||
|
||
Arguments:
|
||
|
||
Procedure - Supplies the procedure which the new thread should execute.
|
||
|
||
Parameter - Supplies a parameter to be passed to the procedure.
|
||
|
||
RpcStatus - Returns the status of the operation. This will be set to
|
||
RPC_S_OUT_OF_THREADS if we can not create a new thread.
|
||
|
||
--*/
|
||
{
|
||
unsigned long ThreadIdentifier;
|
||
HANDLE ImpersonationToken, NewToken = 0;
|
||
NTSTATUS NtStatus;
|
||
|
||
CommonConstructor();
|
||
|
||
SavedProcedure = Procedure;
|
||
SavedParameter = Parameter;
|
||
HandleToThread = 0;
|
||
|
||
if (*Status != RPC_S_OK)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (IsServerSideDebugInfoEnabled())
|
||
{
|
||
DebugCell = (DebugThreadInfo *) AllocateCell(&DebugCellTag);
|
||
if (DebugCell == NULL)
|
||
{
|
||
*Status = RPC_S_OUT_OF_MEMORY;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
DebugCell->TypeHeader = 0;
|
||
DebugCell->Type = dctThreadInfo;
|
||
DebugCell->Status = dtsAllocated;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there is a token on the calling thread, null it out
|
||
//
|
||
if (OpenThreadToken (GetCurrentThread(),
|
||
TOKEN_IMPERSONATE | TOKEN_QUERY,
|
||
TRUE,
|
||
&ImpersonationToken) == FALSE)
|
||
{
|
||
if ( GetLastError() == ERROR_NO_TOKEN )
|
||
{
|
||
ImpersonationToken = 0 ;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// More interesting. There may be a token, and
|
||
// we can't open it. Note that the anonymous token can't be opened
|
||
// this way. Complain:
|
||
//
|
||
#if DBG
|
||
PrintToDebugger( "RPC : OpenThreadToken returned %d\n", GetLastError() );
|
||
#endif
|
||
ASSERT(0);
|
||
*Status = RPC_S_ACCESS_DENIED;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
NtStatus = NtSetInformationThread(NtCurrentThread(),
|
||
ThreadImpersonationToken,
|
||
&NewToken,
|
||
sizeof(HANDLE));
|
||
|
||
if (!NT_SUCCESS(NtStatus))
|
||
{
|
||
*Status = RPC_S_ACCESS_DENIED;
|
||
#if DBG
|
||
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
|
||
#endif
|
||
ASSERT(0);
|
||
CloseHandle(ImpersonationToken);
|
||
return;
|
||
}
|
||
}
|
||
|
||
HandleToThread = CreateThread(0, DefaultThreadStackSize,
|
||
(LPTHREAD_START_ROUTINE) ThreadStartRoutine,
|
||
this, 0, &ThreadIdentifier);
|
||
if ( HandleToThread == 0 )
|
||
{
|
||
*Status = RPC_S_OUT_OF_THREADS;
|
||
}
|
||
|
||
if (ImpersonationToken)
|
||
{
|
||
//
|
||
// If there was a token, restore it.
|
||
//
|
||
NtStatus = NtSetInformationThread(NtCurrentThread(),
|
||
ThreadImpersonationToken,
|
||
&ImpersonationToken,
|
||
sizeof(HANDLE));
|
||
|
||
#if DBG
|
||
if (!NT_SUCCESS(NtStatus))
|
||
{
|
||
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
|
||
}
|
||
#endif // DBG
|
||
|
||
CloseHandle(ImpersonationToken);
|
||
}
|
||
}
|
||
|
||
|
||
THREAD::THREAD (
|
||
OUT RPC_STATUS * Status
|
||
#ifdef RPC_OLD_IO_PROTECTION
|
||
) : ProtectCount(1), ReleaseCount(0),
|
||
#else
|
||
) :
|
||
#endif
|
||
ThreadEvent(Status)
|
||
/*++
|
||
Routine Description:
|
||
This overloaded constructor is called only by the main app thread.
|
||
this is needed because in WMSG we will be dispatching in the
|
||
context of main app thread.
|
||
|
||
Arguments:
|
||
RpcStatus - Returns the status of the operation
|
||
--*/
|
||
{
|
||
HANDLE hProcess ;
|
||
|
||
CommonConstructor();
|
||
|
||
SavedProcedure = 0;
|
||
SavedParameter = 0;
|
||
HandleToThread = 0;
|
||
|
||
if (*Status != RPC_S_OK)
|
||
{
|
||
return;
|
||
}
|
||
|
||
hProcess = GetCurrentProcess() ;
|
||
|
||
if (!DuplicateHandle(hProcess,
|
||
GetCurrentThread(),
|
||
hProcess,
|
||
&HandleToThread,
|
||
0,
|
||
FALSE,
|
||
DUPLICATE_SAME_ACCESS))
|
||
{
|
||
*Status = RPC_S_OUT_OF_MEMORY ;
|
||
return;
|
||
}
|
||
|
||
RpcpSetThreadPointer(this);
|
||
|
||
*Status = RPC_S_OK ;
|
||
}
|
||
|
||
void
|
||
THREAD::CommonConstructor (
|
||
)
|
||
/*++
|
||
Function Name:CommonConstructor
|
||
|
||
Parameters:
|
||
|
||
Description:
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
CancelTimeout = RPC_C_CANCEL_INFINITE_TIMEOUT;
|
||
Context = 0;
|
||
SecurityContext = 0;
|
||
ClearCallCancelledFlag();
|
||
fAsync = FALSE;
|
||
ExtendedStatus = RPC_S_OK;
|
||
DebugCell = NULL;
|
||
ThreadEEInfo = NULL;
|
||
NDRSlot = NULL;
|
||
CachedLrpcCall = NULL;
|
||
LastSuccessfullyDestroyedContext = NULL;
|
||
CachedWaiterPtr = NULL;
|
||
CachedEEInfoBlock = NULL;
|
||
ParametersOfCachedEEInfo = 0;
|
||
|
||
ActiveCall = 0;
|
||
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
BufferCache[i].pList = 0;
|
||
BufferCache[i].cBlocks = 0;
|
||
}
|
||
|
||
#ifdef CHECK_MUTEX_INVERSION
|
||
ConnectionMutexHeld = 0;
|
||
#endif
|
||
}
|
||
|
||
THREAD::~THREAD (
|
||
)
|
||
{
|
||
ASSERT (0 == SecurityContext);
|
||
#ifdef RPC_OLD_IO_PROTECTION
|
||
ASSERT(ProtectCount == ReleaseCount.GetInteger());
|
||
#endif
|
||
|
||
if (CachedWaiterPtr && (CachedWaiterPtr != &CachedWaiter))
|
||
SWMRLock::FreeWaiterCache(&CachedWaiterPtr);
|
||
|
||
if (DebugCell != NULL)
|
||
{
|
||
FreeCell(DebugCell, &DebugCellTag);
|
||
}
|
||
|
||
if ( HandleToThread != 0 )
|
||
{
|
||
CloseHandle(HandleToThread);
|
||
}
|
||
|
||
if (CachedLrpcCall)
|
||
{
|
||
delete CachedLrpcCall;
|
||
}
|
||
|
||
if (ThreadEEInfo)
|
||
PurgeEEInfo();
|
||
|
||
if (CachedEEInfoBlock)
|
||
FreeEEInfoRecordShallow(CachedEEInfoBlock);
|
||
|
||
gBufferCache->ThreadDetach(this);
|
||
}
|
||
|
||
void *
|
||
THREAD::ThreadHandle (
|
||
)
|
||
/*++
|
||
Function Name:ThreadHandle
|
||
|
||
Parameters:
|
||
|
||
Description:
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
while ( HandleToThread == 0 )
|
||
{
|
||
PauseExecution(100L);
|
||
}
|
||
|
||
return(HandleToThread);
|
||
}
|
||
|
||
RPC_STATUS
|
||
THREAD::SetCancelTimeout (
|
||
IN long Timeout
|
||
)
|
||
/*++
|
||
Function Name:SetCancelTimeout
|
||
|
||
Parameters:
|
||
|
||
Description:
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
|
||
CancelTimeout = Timeout;
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
|
||
void
|
||
SetExtendedError (
|
||
IN RPC_STATUS Status
|
||
)
|
||
{
|
||
THREAD *pThread = ThreadSelf();
|
||
|
||
if (pThread == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
pThread->SetExtendedError(Status);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
I_RpcGetExtendedError (
|
||
)
|
||
{
|
||
THREAD *pThread = ThreadSelf();
|
||
|
||
if (pThread == 0)
|
||
{
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
|
||
return pThread->GetExtendedError();
|
||
}
|
||
|
||
|
||
HANDLE RPC_ENTRY
|
||
I_RpcTransGetThreadEvent(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the receive event specific to this thread.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
Returns the send event. This function will not fail
|
||
--*/
|
||
{
|
||
THREAD *pThis = RpcpGetThreadPointer();
|
||
|
||
ASSERT(pThis);
|
||
ASSERT(pThis->ThreadEvent.EventHandle);
|
||
|
||
return(pThis->ThreadEvent.EventHandle);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
THREAD::CancelCall (
|
||
IN BOOL fTimeoutValid,
|
||
IN long Timeout
|
||
)
|
||
{
|
||
RPC_STATUS Status;
|
||
|
||
if (fTimeoutValid)
|
||
{
|
||
CancelTimeout = Timeout;
|
||
}
|
||
|
||
SetCallCancelledFlag();
|
||
if (ActiveCall)
|
||
{
|
||
Status = ActiveCall->Cancel(HandleToThread);
|
||
}
|
||
else
|
||
{
|
||
Status = RPC_S_NO_CALL_ACTIVE;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
THREAD::RegisterForCancels (
|
||
IN CALL *Call
|
||
)
|
||
{
|
||
Call->NestingCall = ActiveCall;
|
||
ActiveCall = Call;
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
|
||
void
|
||
THREAD::UnregisterForCancels (
|
||
)
|
||
{
|
||
if (ActiveCall)
|
||
{
|
||
ActiveCall = ActiveCall->NestingCall;
|
||
}
|
||
|
||
//
|
||
// Need to think about when we should cancel the next call if ActiveCall != 0
|
||
//
|
||
}
|
||
|
||
void
|
||
THREAD::PurgeEEInfo (
|
||
void
|
||
)
|
||
{
|
||
ASSERT(ThreadEEInfo != NULL);
|
||
FreeEEInfoChain(ThreadEEInfo);
|
||
ThreadEEInfo = NULL;
|
||
}
|
||
|
||
VOID RPC_ENTRY
|
||
CancelAPCRoutine (
|
||
IN ULONG_PTR Timeout
|
||
)
|
||
{
|
||
RPC_STATUS Status;
|
||
THREAD *Thread = ThreadSelf() ;
|
||
|
||
if (Thread == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Status = Thread->CancelCall();
|
||
#if DBG
|
||
if (Status != RPC_S_OK)
|
||
{
|
||
PrintToDebugger("RPC: CancelCall failed %d\n", Status);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void
|
||
THREAD::GetWaiterCache (
|
||
OUT SWMRWaiter **WaiterCache,
|
||
IN SCALL *SCall,
|
||
IN SWMRWaiterType WaiterType
|
||
)
|
||
{
|
||
ASSERT(WaiterCache != NULL);
|
||
|
||
if (CachedWaiterPtr)
|
||
{
|
||
LogEvent(SU_THREAD, EV_POP, CachedWaiterPtr->hEvent, CachedWaiterPtr, 0, 1, 1);
|
||
// if we have something in the cache, we may as well use it
|
||
*WaiterCache = CachedWaiterPtr;
|
||
CachedWaiterPtr = NULL;
|
||
}
|
||
else if ((WaiterType == swmrwtWriter) && SCall->IsSyncCall())
|
||
{
|
||
// the cache is empty, but this is exclusive, sync usage, so we can
|
||
// borrow the thread event and cook up a waiter
|
||
SWMRWaiter::CookupWaiterFromEventAndBuffer(&CachedWaiter, WaiterType, ThreadEvent.EventHandle);
|
||
*WaiterCache = &CachedWaiter;
|
||
}
|
||
else
|
||
{
|
||
// the cache is empty and this is either shared or async usage so we can't
|
||
// use the thread buffer/event.
|
||
*WaiterCache = NULL;
|
||
}
|
||
}
|
||
|
||
void
|
||
THREAD::FreeWaiterCache (
|
||
IN OUT SWMRWaiter **WaiterCache
|
||
)
|
||
{
|
||
ASSERT(WaiterCache != NULL);
|
||
|
||
// if we got something in the cache and this is not a cooked up item
|
||
if (*WaiterCache && (*WaiterCache != &CachedWaiter))
|
||
{
|
||
if (CachedWaiterPtr)
|
||
{
|
||
// if our cache is already full, just free the new waiter
|
||
SWMRLock::FreeWaiterCache(WaiterCache);
|
||
}
|
||
else
|
||
{
|
||
// store it
|
||
CachedWaiterPtr = *WaiterCache;
|
||
LogEvent(SU_THREAD, EV_PUSH, CachedWaiterPtr->hEvent, CachedWaiterPtr, 0, 1, 1);
|
||
*WaiterCache = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID RPC_ENTRY
|
||
CancelExAPCRoutine (
|
||
IN ULONG_PTR Timeout
|
||
)
|
||
{
|
||
RPC_STATUS Status;
|
||
THREAD *Thread = ThreadSelf() ;
|
||
|
||
if (Thread == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Status = Thread->CancelCall(TRUE, (long) Timeout);
|
||
#if DBG
|
||
if (Status != RPC_S_OK)
|
||
{
|
||
PrintToDebugger("RPC: CancelCall failed %d\n", Status);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
|
||
void
|
||
RpcpRaiseException (
|
||
IN RPC_STATUS exception
|
||
)
|
||
{
|
||
if ( exception == STATUS_ACCESS_VIOLATION )
|
||
{
|
||
exception = ERROR_NOACCESS;
|
||
}
|
||
|
||
RaiseException(exception,
|
||
EXCEPTION_NONCONTINUABLE,
|
||
0,
|
||
0);
|
||
|
||
ASSERT(0);
|
||
}
|
||
|
||
|
||
|
||
void RPC_ENTRY
|
||
RpcRaiseException (
|
||
IN RPC_STATUS exception
|
||
)
|
||
{
|
||
NukeStaleEEInfoIfNecessary(exception);
|
||
RpcpRaiseException(exception);
|
||
}
|
||
|
||
|