Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

956 lines
18 KiB

/* --------------------------------------------------------------------
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.
-------------------------------------------------------------------- */
#include <precomp.hxx>
#ifdef NTENV
#include <rpcuuid.hxx>
#include <binding.hxx>
#include <handle.hxx>
#endif
static unsigned long DefaultThreadStackSize = 0;
#ifdef DEBUGRPC
extern void
__dl (
IN void * obj
);
#endif
void **
ThreadSharedMemoryContext(
)
{
THREAD * thread = ThreadSelf();
return(&thread->SharedMemoryProtocol);
}
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.
--*/
{
#ifdef NTENV
DllHandle = (void *)LoadLibraryW(DllName);
#endif
#ifdef DOSWIN32RPC
DllHandle = (void *)LoadLibraryA((const char *)DllName);
#endif
if ( DllHandle == 0 )
{
if ( GetLastError() == ERROR_NOT_ENOUGH_MEMORY )
{
*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 );
}
}
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;
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.
--*/
{
DefaultThreadStackSize = ThreadStackSize;
return(RPC_S_OK);
}
//
//----------------------------Windows 95----------------------------------
//
#ifdef DOSWIN32RPC
unsigned long RpcTlsIndex = ~0;
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.
--*/
{
return (RPC_S_CANNOT_SUPPORT);
}
RPC_STATUS RPC_ENTRY
RpcTestCancel(
)
{
return (RPC_S_CANNOT_SUPPORT);
}
RPC_STATUS RPC_ENTRY
RpcCancelThread(
IN void * Thread
)
{
return (RPC_S_CANNOT_SUPPORT);
}
unsigned
RpcpThreadTestCancel(
)
{
return 0;
}
RPC_STATUS
RpcpThreadCancel(
void * ThreadHandle
)
{
return RPC_S_CANNOT_SUPPORT;
}
void RPC_ENTRY
RpcRaiseException (
RPC_STATUS exception
)
{
if ( exception == STATUS_ACCESS_VIOLATION )
{
exception = ERROR_NOACCESS;
}
RaiseException((DWORD) exception,0,0,0);
}
DWORD
ThreadStartRoutine (
IN THREAD * Thread
)
{
RpcpSetThreadPointer(Thread);
Thread->StartRoutine();
delete Thread;
return 0;
}
int
InitializeThreads (
)
{
RpcTlsIndex = TlsAlloc();
if (RpcTlsIndex == -1)
return(1);
return(0);
}
THREAD *
ThreadSelf (
)
{
THREAD * Thread;
Thread = RpcpGetThreadPointer();
if (Thread == 0 &&
(Thread = (THREAD *) new char [sizeof(THREAD)]) )
{
memset(Thread, 0, sizeof(THREAD));
Thread->CancelTimeout = RPC_C_CANCEL_INFINITE_TIMEOUT;
RpcpSetThreadPointer(Thread);
}
return(Thread);
}
//
//----------------------------Windows NT----------------------------------
//
#elif NTENV
ACTIVE_THREAD_DICT * ActiveThreads;
#define NO_SLOT (0xffff)
#define CANCEL_TIMEOUT_SHIFT (16)
#define CANCEL_TIMEOUT_LIMIT (0x7fff)
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.
--*/
{
unsigned long Bits = (unsigned long) RpcpGetThreadPointer();
//
// Brand new client thread. Mark high bit, set long timeout,
//
if (!Bits)
{
Bits = HIGHBITSET | NO_SLOT | (CANCEL_TIMEOUT_LIMIT << CANCEL_TIMEOUT_SHIFT);
}
//
// Server threads should not be cancelled from the local process.
//
if (Bits & HIGHBITSET)
{
if (Timeout < 0 || Timeout > CANCEL_TIMEOUT_LIMIT)
{
Timeout = CANCEL_TIMEOUT_LIMIT;
}
Bits &= ~(CANCEL_TIMEOUT_LIMIT << CANCEL_TIMEOUT_SHIFT);
Bits |= (Timeout << CANCEL_TIMEOUT_SHIFT);
RpcpSetThreadPointer((THREAD *) Bits);
}
else
{
((THREAD *) Bits)->CancelTimeout = Timeout ;
}
return RPC_S_OK;
}
unsigned
RpcpThreadTestCancel(
)
{
if (NtTestAlert() == STATUS_ALERTED)
{
return 1;
}
else
{
return 0;
}
}
RPC_STATUS
RpcpThreadCancel(
void * ThreadHandle
)
{
NTSTATUS NtStatus;
NtStatus = NtAlertThread((HANDLE) ThreadHandle);
if (NT_SUCCESS(NtStatus))
{
return RPC_S_OK;
}
else
{
return RtlNtStatusToDosError(NtStatus);
}
}
RPC_STATUS
RpcpThreadIdFromHandle(
IN void * ThreadHandle,
DWORD * pThreadId
)
{
NTSTATUS NtStatus;
THREAD_BASIC_INFORMATION ThreadInfo;
NtStatus = NtQueryInformationThread((HANDLE) ThreadHandle,
ThreadBasicInformation,
&ThreadInfo,
sizeof(ThreadInfo),
0
);
if (!NT_SUCCESS(NtStatus))
{
return RtlNtStatusToDosError(NtStatus);
}
if ((unsigned long) ThreadInfo.ClientId.UniqueProcess != GetCurrentProcessId())
{
return RPC_S_CANNOT_SUPPORT;
}
*pThreadId = (DWORD) ThreadInfo.ClientId.UniqueThread;
return RPC_S_OK;
}
void RPC_ENTRY
RpcRaiseException (
RPC_STATUS exception
)
{
if ( exception == STATUS_ACCESS_VIOLATION )
{
exception = ERROR_NOACCESS;
}
EXCEPTION_RECORD ExceptionRecord;
ExceptionRecord.ExceptionCode = (NTSTATUS) exception;
ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD) 0;
ExceptionRecord.ExceptionFlags = 0;
ExceptionRecord.NumberParameters = 0;
RtlRaiseException(&ExceptionRecord);
}
NTSTATUS
ThreadStartRoutine (
IN THREAD * Thread
)
{
RpcpSetThreadPointer(Thread);
Thread->StartRoutine();
delete Thread;
return 0;
}
int
InitializeThreads (
)
{
RPC_STATUS Status = RPC_S_OK;
ActiveThreads = new ACTIVE_THREAD_DICT(&Status);
if (!ActiveThreads || Status != RPC_S_OK)
{
delete ActiveThreads;
return 1;
}
return 0;
}
THREAD *
ThreadSelf (
)
{
THREAD * Thread;
Thread = RpcpGetThreadPointer();
if (Thread == 0 &&
(Thread = (THREAD *) new char [sizeof(THREAD)]) )
{
memset(Thread, 0, sizeof(THREAD));
Thread->CancelTimeout = RPC_C_CANCEL_INFINITE_TIMEOUT;
RpcpSetThreadPointer(Thread);
}
return(Thread);
}
RPC_STATUS
RegisterForCancels(
CONNECTION * Connection
)
{
unsigned long Bits = (unsigned long) RpcpGetThreadPointer();
//
// Brand new client thread. Mark high bit, set long timeout,
//
if (!Bits)
{
Bits = HIGHBITSET | NO_SLOT | (CANCEL_TIMEOUT_LIMIT << CANCEL_TIMEOUT_SHIFT);
}
unsigned Slot;
if (Bits & HIGHBITSET)
{
Slot = Bits & 0xffff;
Slot = ActiveThreads->RegisterThread( GetCurrentThreadId(),
Connection,
Slot
);
Bits &= 0xffff0000;
Bits |= Slot;
RpcpSetThreadPointer((THREAD *) Bits);
}
else
{
Slot = ((THREAD *) Bits)->Slot;
Slot = ActiveThreads->RegisterThread( GetCurrentThreadId(),
Connection,
Slot
);
((THREAD *) Bits)->Slot = Slot;
}
if (NO_SLOT == Slot)
{
return RPC_S_OUT_OF_MEMORY;
}
return RPC_S_OK;
}
RPC_STATUS
UnregisterForCancels(
)
{
unsigned long Bits = (unsigned long) RpcpGetThreadPointer();
if (Bits & HIGHBITSET)
{
unsigned Slot = Bits & 0xffff;
if (0 == ActiveThreads->UnregisterThread(Slot))
{
Bits &= 0xffff0000;
Bits |= NO_SLOT;
RpcpSetThreadPointer((THREAD *) Bits);
}
}
else
{
if (Bits)
{
THREAD * Thread = RpcpGetThreadPointer();
if (0 == ActiveThreads->UnregisterThread(Thread->Slot))
{
Thread->Slot = NO_SLOT ;
}
}
}
return RPC_S_OK;
}
unsigned
ACTIVE_THREAD_DICT::RegisterThread(
unsigned long ThreadId,
CONNECTION * Connection,
unsigned Index
)
{
unsigned InitialIndex;
THREAD_CALL_INFO * OldData = 0;
if (Index == NO_SLOT)
{
Index = HashThreadId(ThreadId);
InitialIndex = Index;
RequestGlobalMutex();
while (Index < Size && Data[Index].ThreadId)
{
++Index;
}
if (Index == Size)
{
Index = 0;
while (Index < InitialIndex && Data[Index].ThreadId)
{
++Index;
}
if (Index == InitialIndex)
{
//
// The table is full. Increase its size.
//
THREAD_CALL_INFO * NewData = new THREAD_CALL_INFO[2 * Size];
if (!NewData)
{
ClearGlobalMutex();
return NO_SLOT;
}
memcpy(NewData,
Data,
Size * sizeof(THREAD_CALL_INFO)
);
memset(NewData + Size,
0,
Size * sizeof(THREAD_CALL_INFO)
);
OldData = Data;
Index = Size;
Data = NewData;
Size *= 2;
ASSERT(Size < 0x10000);
}
}
}
else
{
RequestGlobalMutex();
}
ASSERT(Index < Size);
if (Data[Index].ThreadId)
{
ASSERT(Data[Index].ThreadId == GetCurrentThreadId());
ASSERT(Data[Index].Connection);
}
else
{
ASSERT(Data[Index].Connection == 0);
}
Connection->NestingCall = Data[Index].Connection;
Data[Index].Connection = Connection;
Data[Index].ThreadId = ThreadId;
ClearGlobalMutex();
delete OldData;
return Index;
}
CONNECTION *
ACTIVE_THREAD_DICT::UnregisterThread(
unsigned Index
)
{
RequestGlobalMutex();
CONNECTION * OldConnection = Data[Index].Connection;
CONNECTION * NewConnection = OldConnection->NestingCall;
ASSERT( OldConnection );
ASSERT( Data[Index].ThreadId == GetCurrentThreadId() );
Data[Index].Connection = NewConnection;
if (!NewConnection)
{
Data[Index].ThreadId = 0;
}
ClearGlobalMutex();
OldConnection->NestingCall = 0;
return NewConnection;
}
void *
ACTIVE_THREAD_DICT::Find(
unsigned long ThreadId
)
{
void * Conn = 0;
unsigned Index = HashThreadId(ThreadId);
unsigned InitialIndex = Index;
RequestGlobalMutex();
while (Index < Size && Data[Index].ThreadId != ThreadId)
{
++Index;
}
if (Index < Size)
{
Conn = Data[Index].Connection;
ClearGlobalMutex();
return Conn;
}
while (Index < InitialIndex && Data[Index].ThreadId != ThreadId)
{
++Index;
}
if (Index < InitialIndex)
{
Conn = Data[Index].Connection;
ClearGlobalMutex();
return Conn;
}
ClearGlobalMutex();
return 0;
}
RPC_STATUS RPC_ENTRY
RpcCancelThread(
IN void * ThreadHandle
)
{
RPC_STATUS Status;
DWORD ThreadId;
Status = RpcpThreadIdFromHandle(ThreadHandle, &ThreadId);
if (Status)
{
return Status;
}
GlobalMutexRequest();
CONNECTION * Connection = (CONNECTION *) ActiveThreads->Find(ThreadId);
if (Connection)
{
Status = Connection->Cancel(ThreadHandle);
GlobalMutexClear();
return Status;
}
else
{
GlobalMutexClear();
return RpcpThreadCancel(ThreadHandle);
}
ASSERT(0);
}
RPC_STATUS RPC_ENTRY
RpcTestCancel(
)
{
unsigned Cancelled;
void * Context = RpcpGetThreadContext();
if (Context)
{
CONNECTION * Connection = (CONNECTION *) Context;
Cancelled = Connection->TestCancel();
}
else
{
Cancelled = RpcpThreadTestCancel();
}
if (Cancelled)
{
return RPC_S_OK;
}
else
{
return RPC_S_ACCESS_DENIED;
}
}
//
//------------------------------------------------------------------------
//
#else
#error "unknown operating system"
#endif
THREAD::THREAD (
IN THREAD_PROC Procedure,
IN void * Parameter,
OUT RPC_STATUS * RpcStatus
) : ProtectCount(0)
/*++
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;
SavedProcedure = Procedure;
SavedParameter = Parameter;
Context = 0;
ImpersonatingClient = 0;
SecurityContext = 0;
#ifdef NTENV
Slot = NO_SLOT ;
hThreadEvent = 0;
#endif
CancelTimeout = RPC_C_CANCEL_INFINITE_TIMEOUT;
HandleToThread = CreateThread(0, DefaultThreadStackSize,
(LPTHREAD_START_ROUTINE) ThreadStartRoutine,
this, 0, &ThreadIdentifier);
if ( HandleToThread == 0 )
{
*RpcStatus = RPC_S_OUT_OF_THREADS;
}
}
THREAD::THREAD (
OUT RPC_STATUS * RpcStatus
) : ProtectCount(0)
/*++
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
--*/
{
unsigned long Bits = (unsigned long) RpcpGetThreadPointer() ;
#ifdef NTENV
if (Bits)
{
ASSERT(Bits & HIGHBITSET) ;
Slot = Bits & 0x0000FFFF ;
CancelTimeout = ((Bits & ~HIGHBITSET) >> CANCEL_TIMEOUT_SHIFT ) ;
}
else
{
Slot = NO_SLOT ;
CancelTimeout = RPC_C_CANCEL_INFINITE_TIMEOUT;
}
hThreadEvent = 0;
#endif
SavedProcedure = 0;
SavedParameter = 0;
Context = 0;
ImpersonatingClient = 0;
SecurityContext = 0;
HandleToThread = 0 ;
RpcpSetThreadPointer(this);
*RpcStatus = RPC_S_OK ;
}
THREAD::~THREAD (
)
{
ASSERT (0 == ImpersonatingClient);
ASSERT (0 == SecurityContext);
if ( HandleToThread != 0 )
{
CloseHandle(HandleToThread);
}
#ifdef NTENV
if (hThreadEvent)
{
CloseHandle(hThreadEvent);
}
#endif
}
void *
THREAD::ThreadHandle (
)
{
while ( HandleToThread == 0 )
{
PauseExecution(100L);
}
return(HandleToThread);
}