diff options
Diffstat (limited to 'src/pal/src/include/pal/thread.hpp')
-rw-r--r-- | src/pal/src/include/pal/thread.hpp | 838 |
1 files changed, 838 insertions, 0 deletions
diff --git a/src/pal/src/include/pal/thread.hpp b/src/pal/src/include/pal/thread.hpp new file mode 100644 index 0000000000..e6dacd2136 --- /dev/null +++ b/src/pal/src/include/pal/thread.hpp @@ -0,0 +1,838 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + + + +Module Name: + + include/pal/thread.hpp + +Abstract: + Header file for thread structures + + + +--*/ + +#ifndef _PAL_THREAD_HPP_ +#define _PAL_THREAD_HPP_ + +#include "corunix.hpp" +#include "shm.hpp" +#include "cs.hpp" + +#include <pthread.h> +#include <sys/syscall.h> +#if HAVE_MACH_EXCEPTIONS +#include <mach/mach.h> +#endif // HAVE_MACH_EXCEPTIONS + +#include "threadsusp.hpp" +#include "tls.hpp" +#include "synchobjects.hpp" +#include <errno.h> + +namespace CorUnix +{ + enum PalThreadType + { + UserCreatedThread, + PalWorkerThread, + SignalHandlerThread + }; + + PAL_ERROR + InternalCreateThread( + CPalThread *pThread, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + DWORD dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, + DWORD dwCreationFlags, + PalThreadType eThreadType, + LPDWORD lpThreadId, + HANDLE *phThread + ); + + PAL_ERROR + InternalGetThreadPriority( + CPalThread *pThread, + HANDLE hTargetThread, + int *piNewPriority + ); + + PAL_ERROR + InternalSetThreadPriority( + CPalThread *pThread, + HANDLE hTargetThread, + int iNewPriority + ); + + PAL_ERROR + InternalGetThreadDataFromHandle( + CPalThread *pThread, + HANDLE hThread, + DWORD dwRightsRequired, + CPalThread **ppTargetThread, + IPalObject **ppobjThread + ); + + VOID + InternalEndCurrentThread( + CPalThread *pThread + ); + + PAL_ERROR + InternalCreateDummyThread( + CPalThread *pThread, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + CPalThread **ppDummyThread, + HANDLE *phThread + ); + + PAL_ERROR + InitializeGlobalThreadData( + void + ); + + PAL_ERROR + CreateThreadData( + CPalThread **ppThread + ); + + PAL_ERROR + CreateThreadObject( + CPalThread *pThread, + CPalThread *pNewThread, + HANDLE *phThread + ); + + PAL_ERROR + InitializeEndingThreadsData( + void + ); + + BOOL + GetThreadTimesInternal( + IN HANDLE hThread, + OUT LPFILETIME lpKernelTime, + OUT LPFILETIME lpUserTime); + +#ifdef FEATURE_PAL_SXS +#if HAVE_MACH_EXCEPTIONS + + // Structure used to return data about a single handler to a caller. + struct MachExceptionHandler + { + exception_mask_t m_mask; + exception_handler_t m_handler; + exception_behavior_t m_behavior; + thread_state_flavor_t m_flavor; + }; + + // Class abstracting previously registered Mach exception handlers for a thread. + struct CThreadMachExceptionHandlers + { + public: + // Maximum number of exception ports we hook. Must be the count + // of all bits set in the exception masks defined in machexception.h. + static const int s_nPortsMax = 6; + + // Saved exception ports, exactly as returned by + // thread_swap_exception_ports. + mach_msg_type_number_t m_nPorts; + exception_mask_t m_masks[s_nPortsMax]; + exception_handler_t m_handlers[s_nPortsMax]; + exception_behavior_t m_behaviors[s_nPortsMax]; + thread_state_flavor_t m_flavors[s_nPortsMax]; + + CThreadMachExceptionHandlers() : + m_nPorts(-1) + { + } + + // Get handler details for a given type of exception. If successful the structure pointed at by + // pHandler is filled in and true is returned. Otherwise false is returned. + bool GetHandler(exception_type_t eException, MachExceptionHandler *pHandler); + + private: + // Look for a handler for the given exception within the given handler node. Return its index if + // successful or -1 otherwise. + int GetIndexOfHandler(exception_mask_t bmExceptionMask); + }; +#endif // HAVE_MACH_EXCEPTIONS +#endif // FEATURE_PAL_SXS + + class CThreadSEHInfo : public CThreadInfoInitializer + { + public: +#if !HAVE_MACH_EXCEPTIONS + BOOL safe_state; + int signal_code; +#endif // !HAVE_MACH_EXCEPTIONSG + + CThreadSEHInfo() + { + }; + }; + + /* In the windows CRT there is a constant defined for the max width + of a _ecvt conversion. That constant is 348. 348 for the value, plus + the exponent value, decimal, and sign if required. */ +#define ECVT_MAX_COUNT_SIZE 348 +#define ECVT_MAX_BUFFER_SIZE 357 + + /*STR_TIME_SIZE is defined as 26 the size of the + return val by ctime_r*/ +#define STR_TIME_SIZE 26 + + class CThreadCRTInfo : public CThreadInfoInitializer + { + public: + CHAR * strtokContext; // Context for strtok function + WCHAR * wcstokContext; // Context for wcstok function + struct PAL_tm localtimeBuffer; // Buffer for localtime function + CHAR ctimeBuffer[ STR_TIME_SIZE ]; // Buffer for ctime function + CHAR ECVTBuffer[ ECVT_MAX_BUFFER_SIZE ]; // Buffer for _ecvt function. + + CThreadCRTInfo() : + strtokContext(NULL), + wcstokContext(NULL) + { + ZeroMemory(&localtimeBuffer, sizeof(localtimeBuffer)); + ZeroMemory(ctimeBuffer, sizeof(ctimeBuffer)); + ZeroMemory(ECVTBuffer, sizeof(ECVTBuffer)); + }; + }; + + class CPalThread + { + friend + PAL_ERROR + CorUnix::InternalCreateThread( + CPalThread *, + LPSECURITY_ATTRIBUTES, + DWORD, + LPTHREAD_START_ROUTINE, + LPVOID, + DWORD, + PalThreadType, + LPDWORD, + HANDLE* + ); + + friend + PAL_ERROR + InternalCreateDummyThread( + CPalThread *pThread, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + CPalThread **ppDummyThread, + HANDLE *phThread + ); + + friend + PAL_ERROR + InternalSetThreadPriority( + CPalThread *, + HANDLE, + int + ); + + friend + PAL_ERROR + InitializeGlobalThreadData( + void + ); + + friend + PAL_ERROR + CreateThreadData( + CPalThread **ppThread + ); + + friend + PAL_ERROR + CreateThreadObject( + CPalThread *pThread, + CPalThread *pNewThread, + HANDLE *phThread + ); + + friend CatchHardwareExceptionHolder; + + private: + + CPalThread *m_pNext; + DWORD m_dwExitCode; + BOOL m_fExitCodeSet; + CRITICAL_SECTION m_csLock; + bool m_fLockInitialized; + bool m_fIsDummy; + + // + // Minimal reference count, used primarily for cleanup purposes. A + // new thread object has an initial refcount of 1. This initial + // reference is removed by CorUnix::InternalEndCurrentThread. + // + // The only other spot the refcount is touched is from within + // CPalObjectBase::ReleaseReference -- incremented before the + // destructors for an ojbect are called, and decremented afterwords. + // This permits the freeing of the thread structure to happen after + // the freeing of the enclosing thread object has completed. + // + + LONG m_lRefCount; + + // + // The IPalObject for this thread. The thread will release its reference + // to this object when it exits. + // + + IPalObject *m_pThreadObject; + + // + // Thread ID info + // + + SIZE_T m_threadId; + DWORD m_dwLwpId; + pthread_t m_pthreadSelf; + +#if HAVE_MACH_THREADS + mach_port_t m_machPortSelf; +#endif + + // > 0 when there is an exception holder which causes h/w + // exceptions to be sent down the C++ exception chain. + int m_hardwareExceptionHolderCount; + + // + // Start info + // + + LPTHREAD_START_ROUTINE m_lpStartAddress; + LPVOID m_lpStartParameter; + BOOL m_bCreateSuspended; + + int m_iThreadPriority; + PalThreadType m_eThreadType; + + // + // pthread mutex / condition variable for gating thread startup. + // InternalCreateThread waits on the condition variable to determine + // when the new thread has reached passed all failure points in + // the entry routine + // + + pthread_mutex_t m_startMutex; + pthread_cond_t m_startCond; + bool m_fStartItemsInitialized; + bool m_fStartStatus; + bool m_fStartStatusSet; + + // Base address of the stack of this thread + void* m_stackBase; + // Limit address of the stack of this thread + void* m_stackLimit; + + // The default stack size of a newly created thread (currently 256KB) + // when the dwStackSize paramter of PAL_CreateThread() + // is zero. This value can be set by setting the + // environment variable PAL_THREAD_DEFAULT_STACK_SIZE + // (the value should be in bytes and in hex). + static DWORD s_dwDefaultThreadStackSize; + + // + // The thread entry routine (called from InternalCreateThread) + // + + static void* ThreadEntry(void * pvParam); + +#ifdef FEATURE_PAL_SXS + // + // Data for PAL side-by-side support + // + + private: + // This is set whenever this thread is currently executing within + // a region of code that depends on this instance of the PAL + // in the process. + bool m_fInPal; + +#if HAVE_MACH_EXCEPTIONS + // Record of Mach exception handlers that were already registered when we register our own CoreCLR + // specific handlers. + CThreadMachExceptionHandlers m_sMachExceptionHandlers; +#endif // HAVE_MACH_EXCEPTIONS +#endif // FEATURE_PAL_SXS + + public: + + // + // Embedded information for areas owned by other subsystems + // + + CThreadSynchronizationInfo synchronizationInfo; + CThreadSuspensionInfo suspensionInfo; + CThreadSEHInfo sehInfo; + CThreadTLSInfo tlsInfo; + CThreadApcInfo apcInfo; + CThreadCRTInfo crtInfo; + + CPalThread() + : + m_pNext(NULL), + m_dwExitCode(STILL_ACTIVE), + m_fExitCodeSet(FALSE), + m_fLockInitialized(FALSE), + m_fIsDummy(FALSE), + m_lRefCount(1), + m_pThreadObject(NULL), + m_threadId(0), + m_dwLwpId(0), + m_pthreadSelf(0), +#if HAVE_MACH_THREADS + m_machPortSelf(0), +#endif + m_hardwareExceptionHolderCount(0), + m_lpStartAddress(NULL), + m_lpStartParameter(NULL), + m_bCreateSuspended(FALSE), + m_iThreadPriority(THREAD_PRIORITY_NORMAL), + m_eThreadType(UserCreatedThread), + m_fStartItemsInitialized(FALSE), + m_fStartStatus(FALSE), + m_fStartStatusSet(FALSE), + m_stackBase(NULL), + m_stackLimit(NULL) +#ifdef FEATURE_PAL_SXS + , m_fInPal(TRUE) +#endif // FEATURE_PAL_SXS + { + }; + + virtual ~CPalThread(); + + PAL_ERROR + RunPreCreateInitializers( + void + ); + + // + // m_threadId and m_dwLwpId must be set before calling + // RunPostCreateInitializers + // + + PAL_ERROR + RunPostCreateInitializers( + void + ); + + // + // SetStartStatus is called by THREADEntry or InternalSuspendNewThread + // to inform InternalCreateThread of the results of the thread's + // initialization. InternalCreateThread calls WaitForStartStatus to + // obtain this information (and will not return to its caller until + // the info is available). + // + + void + SetStartStatus( + bool fStartSucceeded + ); + + bool + WaitForStartStatus( + void + ); + + void + Lock( + CPalThread *pThread + ) + { + InternalEnterCriticalSection(pThread, &m_csLock); + }; + + void + Unlock( + CPalThread *pThread + ) + { + InternalLeaveCriticalSection(pThread, &m_csLock); + }; + + // + // The following three methods provide access to the + // native lock used to protect thread native wait data. + // + + void + AcquireNativeWaitLock( + void + ) + { + synchronizationInfo.AcquireNativeWaitLock(); + } + + void + ReleaseNativeWaitLock( + void + ) + { + synchronizationInfo.ReleaseNativeWaitLock(); + } + + bool + TryAcquireNativeWaitLock( + void + ) + { + return synchronizationInfo.TryAcquireNativeWaitLock(); + } + + static void + SetLastError( + DWORD dwLastError + ) + { + // Reuse errno to store last error + errno = dwLastError; + }; + + static DWORD + GetLastError( + void + ) + { + // Reuse errno to store last error + return errno; + }; + + void + SetExitCode( + DWORD dwExitCode + ) + { + m_dwExitCode = dwExitCode; + m_fExitCodeSet = TRUE; + }; + + BOOL + GetExitCode( + DWORD *pdwExitCode + ) + { + *pdwExitCode = m_dwExitCode; + return m_fExitCodeSet; + }; + + SIZE_T + GetThreadId( + void + ) + { + return m_threadId; + }; + + DWORD + GetLwpId( + void + ) + { + return m_dwLwpId; + }; + + pthread_t + GetPThreadSelf( + void + ) + { + return m_pthreadSelf; + }; + +#if HAVE_MACH_THREADS + mach_port_t + GetMachPortSelf( + void + ) + { + return m_machPortSelf; + }; +#endif + + bool + IsHardwareExceptionsEnabled() + { + return m_hardwareExceptionHolderCount > 0; + } + + LPTHREAD_START_ROUTINE + GetStartAddress( + void + ) + { + return m_lpStartAddress; + }; + + LPVOID + GetStartParameter( + void + ) + { + return m_lpStartParameter; + }; + + BOOL + GetCreateSuspended( + void + ) + { + return m_bCreateSuspended; + }; + + PalThreadType + GetThreadType( + void + ) + { + return m_eThreadType; + }; + + int + GetThreadPriority( + void + ) + { + return m_iThreadPriority; + }; + + IPalObject * + GetThreadObject( + void + ) + { + return m_pThreadObject; + } + + BOOL + IsDummy( + void + ) + { + return m_fIsDummy; + }; + + CPalThread* + GetNext( + void + ) + { + return m_pNext; + }; + + void + SetNext( + CPalThread *pNext + ) + { + m_pNext = pNext; + }; + + void + AddThreadReference( + void + ); + + void + ReleaseThreadReference( + void + ); + + // Get base address of the current thread's stack + static + void * + GetStackBase( + void + ); + + // Get cached base address of this thread's stack + // Can be called only for the current thread. + void * + GetCachedStackBase( + void + ); + + // Get limit address of the current thread's stack + static + void * + GetStackLimit( + void + ); + + // Get cached limit address of this thread's stack + // Can be called only for the current thread. + void * + GetCachedStackLimit( + void + ); + +#ifdef FEATURE_PAL_SXS + // + // Functions for PAL side-by-side support + // + + // This function needs to be called on a thread when it enters + // a region of code that depends on this instance of the PAL + // in the process. + PAL_ERROR Enter(PAL_Boundary boundary); + + // This function needs to be called on a thread when it leaves + // a region of code that depends on this instance of the PAL + // in the process. + PAL_ERROR Leave(PAL_Boundary boundary); + + // Returns TRUE whenever this thread is executing in a region + // of code that depends on this instance of the PAL in the process. + BOOL IsInPal() + { + return m_fInPal; + }; + +#if HAVE_MACH_EXCEPTIONS + // Hook Mach exceptions, i.e., call thread_swap_exception_ports + // to replace the thread's current exception ports with our own. + // The previously active exception ports are saved. Called when + // this thread enters a region of code that depends on this PAL. + // Should only fail on internal errors. + PAL_ERROR EnableMachExceptions(); + + // Unhook Mach exceptions, i.e., call thread_set_exception_ports + // to restore the thread's exception ports with those we saved + // in EnableMachExceptions. Called when this thread leaves a + // region of code that depends on this PAL. Should only fail + // on internal errors. + PAL_ERROR DisableMachExceptions(); + + // The exception handling thread needs to be able to get at the list of handlers that installing our + // own handler on a thread has displaced (in case we need to forward an exception that we don't want + // to handle). + CThreadMachExceptionHandlers *GetSavedMachHandlers() + { + return &m_sMachExceptionHandlers; + } +#endif // HAVE_MACH_EXCEPTIONS +#endif // FEATURE_PAL_SXS + }; + +#if defined(FEATURE_PAL_SXS) + extern "C" CPalThread *CreateCurrentThreadData(); +#endif // FEATURE_PAL_SXS + + inline CPalThread *GetCurrentPalThread() + { + return reinterpret_cast<CPalThread*>(pthread_getspecific(thObjKey)); + } + + inline CPalThread *InternalGetCurrentThread() + { + CPalThread *pThread = GetCurrentPalThread(); +#if defined(FEATURE_PAL_SXS) + if (pThread == nullptr) + pThread = CreateCurrentThreadData(); +#endif // FEATURE_PAL_SXS + return pThread; + } + +/*** + + $$TODO: These are needed only to support cross-process thread duplication + + class CThreadImmutableData + { + public: + DWORD dwProcessId; + }; + + class CThreadSharedData + { + public: + DWORD dwThreadId; + DWORD dwExitCode; + }; +***/ + + // + // The process local information for a thread is just a pointer + // to the underlying CPalThread object. + // + + class CThreadProcessLocalData + { + public: + CPalThread *pThread; + }; + + extern CObjectType otThread; +} + +BOOL +TLSInitialize( + void + ); + +VOID +TLSCleanup( + void + ); + +VOID +WaitForEndingThreads( + void + ); + +extern int free_threads_spinlock; + +extern PAL_ActivationFunction g_activationFunction; +extern PAL_SafeActivationCheckFunction g_safeActivationCheckFunction; + +/*++ +Macro: + THREADSilentGetCurrentThreadId + +Abstract: + Same as GetCurrentThreadId, but it doesn't output any traces. + It is useful for tracing functions to display the thread ID + without generating any new traces. + + TODO: how does the perf of pthread_self compare to + InternalGetCurrentThread when we find the thread in the + cache? + + If the perf of pthread_self is comparable to that of the stack + bounds based lookaside system, why aren't we using it in the + cache? + + In order to match the thread ids that debuggers use at least for + linux we need to use gettid(). + +--*/ +#if defined(__linux__) +#define THREADSilentGetCurrentThreadId() (SIZE_T)syscall(SYS_gettid) +#elif defined(__APPLE__) +inline SIZE_T THREADSilentGetCurrentThreadId() { + uint64_t tid; + pthread_threadid_np(pthread_self(), &tid); + return (SIZE_T)tid; +} +#elif defined(__NetBSD__) +#include <lwp.h> +#define THREADSilentGetCurrentThreadId() (SIZE_T)_lwp_self() +#else +#define THREADSilentGetCurrentThreadId() (SIZE_T)pthread_self() +#endif + +#endif // _PAL_THREAD_HPP_ |