From: Oliver Kurth Date: Fri, 15 Sep 2017 18:23:51 +0000 (-0700) Subject: VThreadBase: Switch to "stable" thread IDs X-Git-Tag: stable-10.2.0~96 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=47ffaf37a9f604ffc1099d249dba21857dd426d3;p=thirdparty%2Fopen-vm-tools.git VThreadBase: Switch to "stable" thread IDs Now that lib/thread no longer has a dependency on small-numbered VThreadIDs, and now that lib/thread has ceeded control of assigning VThreadIDs to threads, we can make VThreadBase really simple. --- diff --git a/open-vm-tools/lib/include/vthreadBase.h b/open-vm-tools/lib/include/vthreadBase.h index f111eabf5..11534ff2b 100644 --- a/open-vm-tools/lib/include/vthreadBase.h +++ b/open-vm-tools/lib/include/vthreadBase.h @@ -128,20 +128,13 @@ VThread_CurName(void) #define VTHREADBASE_MAX_NAME 32 /* Arbitrary */ -typedef struct { - VThreadID id; -} VThreadBaseData; - /* Common VThreadBase functions */ const char *VThreadBase_CurName(void); VThreadID VThreadBase_CurID(void); void VThreadBase_SetName(const char *name); /* For implementing a thread library */ -Bool VThreadBase_InitWithTLS(VThreadBaseData *tls); void VThreadBase_ForgetSelf(void); -void VThreadBase_SetNoIDFunc(void (*func)(void), - void (*destr)(void *)); /* * See PR 1626963 and function documentation before thinking about using this. diff --git a/open-vm-tools/lib/misc/vthreadBase.c b/open-vm-tools/lib/misc/vthreadBase.c index dd805ddea..2efc14481 100644 --- a/open-vm-tools/lib/misc/vthreadBase.c +++ b/open-vm-tools/lib/misc/vthreadBase.c @@ -29,55 +29,27 @@ * a default name if none exists, but if called * reentrantly (e.g. due to ASSERT) will supply * a failsafe name instead. - * VThreadBase_CurID - Always populates a VThreadID; will Panic if - * this fails (extremely unlikely). - * VThreadBase_SetName - Sets current thread name. Assigns a VThreadID - * if one is not already present. + * VThreadBase_CurID - Returns a VThreadID. + * VThreadBase_SetName - Sets current thread name. * * Functions useful for implementing a full thread library: - * VThreadBase_InitWithTLS - Sets up thread with a specific VThreadID - * and name. Client supplies TLS store. - * VThreadBase_SetNoIDFunc - The NoID function is called whenever an - * unknown thread is seen; it must call - * VThreadBase_InitWithTLS to assign a - * VThreadID. Note that this function - * runs with all signals masked! - * VThreadBase_ForgetSelf - Clears the VThreadID for current thread, + * VThreadBase_ForgetSelf - Clears the thread name for current thread, * to clean up resource usage prior to thread - * exit. + * exit. (Only needed if __thread support is + * unavailable). * Historical quirks: * * Most other code uses VThread_Foo instead of VThreadBase_Foo; the * public header file uses inlines to convert names. * - * VThreadBase is self-initializing; by default, threads will be given - * names like "vthread-1", "vthread-32", etc. Use VThread_SetName to - * provide more meaningful names (often, this is the only initialization - * needed). - * - * The default implementation supports an (effectively) unlimited number - * of threads, and OS-specific primitives may be used to start the - * threads. If lib/thread is used on top of this library, the lib/thread - * NoID function may introduce a smaller limit. - * - * On Linux we make use of a combination of __thread and pthread support - * (pthread being used to get a cleanup destructor). - * On Mac our only option is to use pthreads. - * On Windows we could use the compiler supported thread locals but - * that has issues when dynamically loaded from a library, so instead - * we use the safer Tls* functions. + * By default, threads will be given names like "vthread-123", + * "vthread-987", etc. to match IDs provided by the host operating system. + * Use VThread_SetName to provide more meaningful names. + * + * On most platforms, make use of __thread. + * On a few platforms (see vthreadBase.h definition of VMW_HAVE_TLS), + * use pthread getspecific/setspecific for thread name and signal count. */ -#if defined __APPLE__ -#include -#include -#else -#define TARGET_OS_IPHONE 0 -#endif - -#if defined __linux__ && !defined __ANDROID__ && !TARGET_OS_IPHONE -# define HAVE_TLS -#endif - #if defined _WIN32 # include #else @@ -93,79 +65,13 @@ # include // for gettid(2) # endif # include -# include -# include -# include #endif #include #include /* snprintf */ #include "vmware.h" -#include "vm_atomic.h" #include "vthreadBase.h" -#include "str.h" #include "util.h" -#include "hashTable.h" -#include "hostinfo.h" - - -/* - * Table of thread types - * ===================== - * OS Thread type TLS key type Max TLS keys - * ----------------------------------------------------------------- - * Windows HANDLE / void * DWORD 0xFFFFFFFF - * (Posix) (pthread_t) (pthread_key_t) (PTHREAD_KEYS_MAX) - * Linux unsigned long unsigned int 1024 - * OS X 10.5 struct _opaque * unsigned long 512 - * Solaris 10 unsigned int unsigned int 128 - * FreeBSD 8 struct pthread * int 256 - */ -#if defined _WIN32 -typedef DWORD VThreadBaseKeyType; -#define VTHREADBASE_INVALID_KEY (VThreadBaseKeyType)(TLS_OUT_OF_INDEXES) -#else -typedef pthread_key_t VThreadBaseKeyType; -/* PTHREAD_KEYS_MAX not defined on Android. */ -#if defined __linux__ && !defined PTHREAD_KEYS_MAX -#define VTHREADBASE_INVALID_KEY (VThreadBaseKeyType)(1024) -#else -#define VTHREADBASE_INVALID_KEY (VThreadBaseKeyType)(PTHREAD_KEYS_MAX) -#endif -#endif - -#ifdef HAVE_TLS -static __thread VThreadBaseData *tlsBaseCache = NULL; -static __thread VThreadID tlsIDCache = VTHREAD_INVALID_ID; -#endif - -static void VThreadBaseInit(void); -static void VThreadBaseSimpleNoID(void); -static void VThreadBaseSimpleFreeID(void *tlsData); -static void VThreadBaseSafeDeleteTLS(void *data); - -static struct { - Atomic_Int baseKey; - Atomic_Int threadIDKey; - Atomic_Int dynamicID; - Atomic_Int numThreads; - Atomic_Ptr nativeHash; - void (*noIDFunc)(void); - void (*freeIDFunc)(void *); -} vthreadBaseGlobals = { - { VTHREADBASE_INVALID_KEY }, - { VTHREADBASE_INVALID_KEY }, - { VTHREAD_INVALID_ID + 1 }, - { 0 }, - { 0 }, - VThreadBaseSimpleNoID, - VThreadBaseSimpleFreeID, -}; - -typedef enum VThreadLocal { - VTHREAD_LOCAL_BASE, - VTHREAD_LOCAL_ID -} VThreadLocal; /* @@ -199,9 +105,6 @@ static void VThreadBaseDestruct(void) * Net result, we must either leak a TLS key or the value in the TLS * slot. This path chooses to leak the value in the TLS slot... and * as the value is an integer and not a pointer, there is no leak. - * - * Code elsewhere in this file makes the opposite choice (leak TLS key) - * due to needing a non-trivial destructor. */ if (vthreadNameKey != 0) { (void) pthread_key_delete(vthreadNameKey); @@ -275,132 +178,12 @@ static __thread char vthreadName[VTHREADBASE_MAX_NAME]; #endif /* VMW_HAVE_TLS */ -/* - * Code to mask all asynchronous signals - * - * There are some stretches where an async signal will cause reentrancy, - * and that breaks allocating a VThreadID and/or setting the TLS slot - * atomically. So mask all asynchronous signals; synchronous signals - * (which are generally fatal) are still OK. - * - * Though these functions can return errors; it is not worthwhile to check - * them because the inputs are hard-coded. The only errors allowed - * are EINVAL for invalid argument (and they are all hardcoded valid) and - * EFAULT for the addresses (and a stack pointer will not EFAULT). Besides, - * these routines are used in code paths that cannot Panic without creating - * a Panic-loop. - */ -#if defined _WIN32 -#define NO_ASYNC_SIGNALS_START do { -#define NO_ASYNC_SIGNALS_END } while (0) -#else -#define NO_ASYNC_SIGNALS_START \ - do { \ - sigset_t setMask, oldMask; \ - sigfillset(&setMask); \ - sigdelset(&setMask, SIGBUS); \ - sigdelset(&setMask, SIGSEGV); \ - sigdelset(&setMask, SIGILL); \ - sigdelset(&setMask, SIGABRT); \ - pthread_sigmask(SIG_BLOCK, &setMask, &oldMask); -#define NO_ASYNC_SIGNALS_END \ - pthread_sigmask(SIG_SETMASK, &oldMask, NULL); \ - } while(0) -#endif - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseInitKeysWork -- - * - * Helper function to be only used by VThreadBaseInitKeys. - * - *----------------------------------------------------------------------------- - */ - -static void -VThreadBaseInitKeyWork(Atomic_Int *key, void (*deletePtr)(void*)) -{ - if (Atomic_Read(key) == VTHREADBASE_INVALID_KEY) { - VThreadBaseKeyType newKey; - -#if defined _WIN32 -#if defined VM_WIN_UWP - newKey = FlsAlloc(NULL); -#else - newKey = TlsAlloc(); -#endif - VERIFY(newKey != VTHREADBASE_INVALID_KEY); -#else - Bool success = pthread_key_create(&newKey, deletePtr) == 0; - if (success && newKey == 0) { - /* - * Leak TLS key 0. System libraries have a habit of destroying - * it. See bugs 702818 and 773420. - */ - success = pthread_key_create(&newKey, deletePtr) == 0; - } - VERIFY(success); -#endif - - if (Atomic_ReadIfEqualWrite(key, VTHREADBASE_INVALID_KEY, newKey) != - VTHREADBASE_INVALID_KEY) { - /* Race: someone else init'd */ -#if defined _WIN32 -#if defined VM_WIN_UWP - FlsFree(newKey); -#else - TlsFree(newKey); -#endif -#else - pthread_key_delete(newKey); -#endif - } - } -} - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseRemoveKey -- - * - * Abstracts TLS key deletion. - * - * Results: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -VThreadBaseRemoveKey(Atomic_Int *key) -{ -#if defined _WIN32 -#if defined VM_WIN_UWP - FlsFree(Atomic_Read(key)); -#else - TlsFree(Atomic_Read(key)); -#endif -#else - pthread_key_delete(Atomic_Read(key)); -#endif -} - - /* *----------------------------------------------------------------------------- * * VThreadBase_DeInitialize -- * - * Deletes allocated TLS keys. Notice how this is only used for a very - * special case as the vthread library is not designed to be unloaded - * and normally just leaks these keys. The initialization code assumes - * that once set, these keys never revert to invalid. For the case of - * a copy of the vthread being statically linked into a dlopened library, - * we are using this to avoid leaking the keys immediately before unload. + * (Vestigial. Will be removed shortly -- kevinc) * * See PR 1626963 * @@ -416,243 +199,9 @@ VThreadBaseRemoveKey(Atomic_Int *key) void VThreadBase_DeInitialize(void) { - VThreadBaseRemoveKey(&vthreadBaseGlobals.baseKey); - VThreadBaseRemoveKey(&vthreadBaseGlobals.threadIDKey); -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseInitKeys -- - * - * Initialize the host-specific TLS slots. - * - * Failure to allocate a TLS slot is immediately fatal. Note that - * a TLS slot is generally allocated at the first of: - * - VThread_Init() (e.g. uses lib/thread) - * - VThread_SetName() - * - a Posix signal - * - a lock acquisition - * Since most Panic paths do look up a thread name (and thus need a TLS - * slot), a program that does not want to Panic-loop should call - * one of the above functions very early to "prime" the TLS slot. - * - * Results: - * None. - * - *----------------------------------------------------------------------------- - */ - -static void -VThreadBaseInitKeys(void) -{ - VThreadBaseInitKeyWork(&vthreadBaseGlobals.baseKey, - &VThreadBaseSafeDeleteTLS); - VThreadBaseInitKeyWork(&vthreadBaseGlobals.threadIDKey, NULL); } -#ifdef VMX86_DEBUG -static INLINE Bool -VThreadBaseAreKeysInited(void) -{ - return Atomic_Read(&vthreadBaseGlobals.baseKey) != VTHREADBASE_INVALID_KEY && - Atomic_Read(&vthreadBaseGlobals.threadIDKey) != VTHREADBASE_INVALID_KEY; -} -#endif -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseSetLocal -- - * - * Sets the specified thread local variable to the supplied - * value. This abstracts between pthread, the Windows TLS - * supports, and the use of __thread. Requires that the TLS keys - * are initialized before calling this function. We encourage - * the compiler to inline this with the expectation that "local" - * is a compile time constant. - * - * Note that we use store the thread local value using pthreads - * even when HAVE_TLS is defined. This way we continue to use - * the same pthread destructor path for cleanup as we would - * without HAVE_TLS. - * - *----------------------------------------------------------------------------- - */ - -static INLINE Bool -VThreadBaseSetLocal(VThreadLocal local, void *value) -{ - VThreadBaseKeyType key; - Bool success; - ASSERT(VThreadBaseAreKeysInited()); - if (local == VTHREAD_LOCAL_BASE) { - key = Atomic_Read(&vthreadBaseGlobals.baseKey); - } else { - ASSERT(local == VTHREAD_LOCAL_ID); - key = Atomic_Read(&vthreadBaseGlobals.threadIDKey); - } - ASSERT(key != VTHREADBASE_INVALID_KEY); -#if defined _WIN32 -#if defined VM_WIN_UWP - success = FlsSetValue(key, value); -#else - success = TlsSetValue(key, value); -#endif -#else - success = pthread_setspecific(key, value) == 0; -#endif -#ifdef HAVE_TLS - if (success) { - if (local == VTHREAD_LOCAL_BASE) { - tlsBaseCache = value; - } else { - ASSERT(local == VTHREAD_LOCAL_ID); - tlsIDCache = (VThreadID)(uintptr_t)value; - } - } -#endif - return success; -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseGetLocal -- - * - * Retrives the specified thread local variable. This abstracts - * between pthread, the Windows TLS supports, and the use of - * __thread. See VThreadSetLocal for more details. - * - *----------------------------------------------------------------------------- - */ - -static INLINE void * -VThreadBaseGetLocal(VThreadLocal local) -{ - void *result; -#ifdef HAVE_TLS - if (local == VTHREAD_LOCAL_BASE) { - result = tlsBaseCache; - } else { - ASSERT(local == VTHREAD_LOCAL_ID); - result = (void*)(uintptr_t)tlsIDCache; - } -#else - VThreadBaseKeyType key; - Atomic_Int *keyPtr; - - keyPtr = local == VTHREAD_LOCAL_BASE ? &vthreadBaseGlobals.baseKey : - &vthreadBaseGlobals.threadIDKey; - key = Atomic_Read(keyPtr); - if (UNLIKELY(key == VTHREADBASE_INVALID_KEY)) { - VThreadBaseInitKeys(); - key = Atomic_Read(keyPtr); - } - ASSERT(VThreadBaseAreKeysInited()); - -#if defined _WIN32 -#if defined VM_WIN_UWP - result = FlsGetValue(key); -#else - result = TlsGetValue(key); -#endif -#else - result = pthread_getspecific(key); -#endif -#endif - - return result; -} - - -static INLINE VThreadBaseData * -VThreadBaseGetBase(void) -{ - return (VThreadBaseData *)VThreadBaseGetLocal(VTHREAD_LOCAL_BASE); -} - -static INLINE Bool -VThreadBaseSetBase(VThreadBaseData *base) -{ - return VThreadBaseSetLocal(VTHREAD_LOCAL_BASE, base); -} - -static INLINE VThreadID -VThreadBaseGetID(void) -{ - return (VThreadID)(uintptr_t)VThreadBaseGetLocal(VTHREAD_LOCAL_ID); -} - -static INLINE Bool -VThreadBaseSetID(VThreadID id) -{ - return VThreadBaseSetLocal(VTHREAD_LOCAL_ID, (void*)(uintptr_t)id); -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseGetNativeHash -- - * - * Get the default hash table of native thread IDs. This is used by - * the "simple" allocation function to enable re-using of VThreadIDs. - * - * Results: - * An atomic HashTable *. - * - * Side effects: - * Allocates the HashTable on first call. - * - *----------------------------------------------------------------------------- - */ - -static HashTable * -VThreadBaseGetNativeHash(void) -{ - return HashTable_AllocOnce(&vthreadBaseGlobals.nativeHash, - 128, HASH_INT_KEY | HASH_FLAG_ATOMIC, NULL); -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadGetAndInitBase -- - * - * Get the per-thread data, and assign if there is no data. - * - * Results: - * A VThreadBaseData *. Will succeed or ASSERT. - * - * Side effects: - * Can assign a dynamic VThreadID to the current thread. - * - *----------------------------------------------------------------------------- - */ - -static VThreadBaseData * -VThreadBaseGetAndInitBase(void) -{ - VThreadBaseData *base = VThreadBaseGetBase(); - - ASSERT(vthreadBaseGlobals.noIDFunc != NULL); - - if (UNLIKELY(base == NULL)) { - VThreadBaseInit(); - base = VThreadBaseGetBase(); - } - ASSERT(base != NULL); - - return base; -} - - -#if 0 -// Disabled until used in upcoming change /* *----------------------------------------------------------------------------- * @@ -704,7 +253,6 @@ VThreadBaseGetStableID(void) return (uintptr_t)(void *)pthread_self(); #endif } -#endif /* @@ -715,10 +263,10 @@ VThreadBaseGetStableID(void) * Get the current thread ID. * * Results: - * A VThreadID. Always succeeds. + * A VThreadID. Always succeeds. * * Side effects: - * May assign a dynamic VThreadID if this thread is not known. + * None. * *----------------------------------------------------------------------------- */ @@ -726,14 +274,7 @@ VThreadBaseGetStableID(void) VThreadID VThreadBase_CurID(void) { - VThreadID tid = VThreadBaseGetID(); - if (UNLIKELY(tid == VTHREAD_INVALID_ID)) { - VThreadBaseInit(); - tid = VThreadBaseGetID(); - } - ASSERT(tid != VTHREAD_INVALID_ID); - ASSERT(tid == VThreadBaseGetAndInitBase()->id); - return tid; + return (VThreadID)VThreadBaseGetStableID(); } @@ -939,112 +480,34 @@ VThreadBase_CurName(void) /* *----------------------------------------------------------------------------- * - * VThreadBase_InitWithTLS -- - * - * (Atomic) VThreadBase initialization, using caller-managed memory. - * Gets the VThreadID from within that caller-managed memory. + * VThreadBase_ForgetSelf -- * - * Always "succeeds" in initialization; the return value is useful - * as an indication of whether this was the first initialization. + * Forget the TLS parts of a thread. * - * Note: will NEVER overwrite an existing TLS allocation. May return a - * different VThreadID than requested; this is logged and considered a bug, - * code that cares about the assigned VThreadID should be using lib/thread - * to manage threads, not native OS primitives. (However, there is code - * like that today, so we cannot ASSERT.) + * If not intending to reallocate TLS, avoid querying the thread's + * VThread_CurName between this call and thread destruction. * * Results: - * TRUE if this is the first initialization (e.g. TLS uses supplied - * pointer), FALSE otherwise. + * None. * * Side effects: - * Sets TLS if this is the first initialization. + * None. * *----------------------------------------------------------------------------- */ -Bool -VThreadBase_InitWithTLS(VThreadBaseData *base) // IN: caller-managed storage +void +VThreadBase_ForgetSelf(void) { - Bool firstTime, success; - - VThreadBaseInitKeys(); /* Require key allocation before TLS read */ - ASSERT(base != NULL && base->id != VTHREAD_INVALID_ID); - - NO_ASYNC_SIGNALS_START; - if (VThreadBaseGetBase() == NULL) { - /* - * Windows potentially has the async-signal problem mentioned - * below due to APCs, but in practice it will not happen: - * APCs only get serviced at a few points (Sleep or WaitFor calls), - * and we obviously do not make those between TlsGetValue and - * TlsSetValue. - */ - /* - * The code between the check of pthread_getspecific and the eventually - * call to pthread_setspecific MUST run with async signals blocked, see - * bugs 295686 & 477318. Here, the problem is that we could - * accidentally set the TLS slot twice (it's not atomic). - */ - success = VThreadBaseSetBase(base) && VThreadBaseSetID(base->id); - firstTime = TRUE; - } else { - success = TRUE; - firstTime = FALSE; - } - NO_ASYNC_SIGNALS_END; - /* Try not to ASSERT while signals are blocked */ - VERIFY(success); - ASSERT(!firstTime || (base == VThreadBaseGetBase())); - - if (firstTime) { - Atomic_Inc(&vthreadBaseGlobals.numThreads); - } else { - VThreadBaseData *realBase = VThreadBaseGetBase(); - - /* - * If this happens, it means: - * 1) A thread was created w/o lib/thread but caller tried to initialize - * it with a specific VThreadID. This shouldn't happen: callers - * should either use lib/thread or not try to initialize VThreadIDs. - * Or, - * 2) An asynchronous signal interrupted the process of assigning - * a VThreadID and resulted in multiple allocation. This either - * means the cooking function is broken - it should block signals - or - * an ASSERT triggered while setting up the VThreadID. - */ - Log("VThreadBase reinitialization, old: %" FMT64 "d, new: %" FMT64 "d.\n", - realBase->id, base->id); + if (vmx86_debug) { + Log("Forgetting VThreadID %" FMT64 "d (\"%s\").\n", + VThread_CurID(), VThread_CurName()); } - return firstTime; -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseSafeDeleteTLS -- - * - * Safely delete the TLS slot. Called when manually "forgetting" a thread, - * or (for Posix) at TLS destruction (so we can forget the pthread_t). - * - * Some of the cleanups have to be performed with a valid TLS slot so - * e.g. AllocTrack knows the current thread. Note that the argument is - * 'void *' only so this can serve as a TLS destructor. - * - * Results: - * None. - * - * Side effects: - * Temporarily sets TLS, and restores it before returning. - * - *----------------------------------------------------------------------------- - */ - -static void -VThreadBaseForgetNameRaw(void) -{ + /* + * The VThreadID is fixed (see StableID above). + * Only the name needs clearing. + */ #if defined VMW_HAVE_TLS memset(vthreadName, '\0', sizeof vthreadName); #else @@ -1059,67 +522,6 @@ VThreadBaseForgetNameRaw(void) #endif } -static void -VThreadBaseSafeDeleteTLS(void *tlsData) -{ - VThreadBaseData *data = tlsData; - - if (data != NULL) { - if (vthreadBaseGlobals.freeIDFunc != NULL) { - Bool success; - VThreadBaseData tmpData = *data; - - /* - * Cleanup routines (specifically, Log()) need to be called with - * valid TLS, so switch to a stack-based TLS slot containing just - * enough for the VThreadBase services, clean up, then clear the - * TLS slot. - */ - success = VThreadBaseSetBase(&tmpData); - VERIFY(success); - - if (vmx86_debug) { - Log("Forgetting VThreadID %" FMT64 "d (\"%s\").\n", - data->id, VThread_CurName()); - } - (*vthreadBaseGlobals.freeIDFunc)(data); - - success = VThreadBaseSetBase(NULL) && - VThreadBaseSetID(VTHREAD_INVALID_ID); - VERIFY(success); - } - VThreadBaseForgetNameRaw(); - Atomic_Dec(&vthreadBaseGlobals.numThreads); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBase_ForgetSelf -- - * - * Forget the TLS parts of a thread. - * - * If not intending to reallocate TLS, avoid querying the thread's - * VThread_CurID or VThread_CurName between this call and thread - * destruction. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -VThreadBase_ForgetSelf(void) -{ - VThreadBaseSafeDeleteTLS(VThreadBaseGetBase()); -} - /* *----------------------------------------------------------------------------- @@ -1144,9 +546,7 @@ VThreadBase_ForgetSelf(void) void VThreadBase_SetName(const char *name) // IN: new name { - (void) VThreadBaseGetAndInitBase(); // Side effect of getting a VThreadID - - ASSERT(name); + ASSERT(name != NULL); if (vmx86_debug && strlen(name) >= VTHREADBASE_MAX_NAME) { Warning("%s: thread name (%s) exceeds maximum length (%u)\n", @@ -1182,306 +582,6 @@ VThreadBase_SetName(const char *name) // IN: new name } -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseGetNative -- - * - * Gets a native representation of the thread ID, which can be stored - * in a pointer. - * - * Results: - * Native representation of a thread ID. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static INLINE void * -VThreadBaseGetNative(void) -{ - /* All thread types must fit into a uintptr_t so we can set them atomically. */ -#ifdef _WIN32 - /* - * On Windows, use a ThreadId instead of the thread handle, to avoid - * holding a reference that is hard to clean up. - */ - ASSERT_ON_COMPILE(sizeof (DWORD) <= sizeof (void*)); - return (void *)(uintptr_t)GetCurrentThreadId(); -#else - ASSERT_ON_COMPILE(sizeof (pthread_t) <= sizeof (void*)); - return (void *)(uintptr_t)pthread_self(); -#endif -} - - -#ifdef _WIN32 -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseNativeIsAlive -- - * - * Determine if the thread described by the native thread ID is alive. - * - * The algorithm is not perfect - native thread IDs can be reused - * by the host OS. But in such cases we will simply fail to reclaim - * VThreadIDs, and such cases are rare. - * - * Results: - * TRUE if alive, may have (rare) false positives. - * FALSE if definitely dead. - * - * Side effects: - * Makes OS calls to determine thread liveliness. - * - *----------------------------------------------------------------------------- - */ - -static Bool -VThreadBaseNativeIsAlive(void *native) -{ -#if defined(VM_WIN_UWP) - /* OpenThread is not support on UWP */ - NOT_IMPLEMENTED(); - - return FALSE; -#else - - // Different access level due to impersonation see PR#780775 - HANDLE hThread = OpenThread(Hostinfo_OpenThreadBits(), FALSE, - (DWORD)(uintptr_t)native); - - if (hThread == NULL) { - /* - * An access denied error tells us that the process is alive despite - * the inability of accessing its information. Commonly, access denied - * occurs when a process is trying to completely protect itself (e.g. - * a virus checker). - */ - - return (GetLastError() == ERROR_ACCESS_DENIED) ? TRUE : FALSE; - } else { - DWORD exitCode; - BOOL success = GetExitCodeThread(hThread, &exitCode); - - ASSERT(success); /* No known ways GetExitCodeThread can fail */ - CloseHandle(hThread); - - return exitCode == STILL_ACTIVE; - } -#endif // VM_WIN_UWP -} -#endif - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseSimpleNoID -- - * - * Default implementation of a function that is called whenever a thread - * is found that contains no VThreadID. - * - * This implementation recycles VThreadIDs of dead threads, trying to keep - * the VThreadID namespace compacted as much as possible. - * - * Results: - * None. - * - * Side effects: - * After running, this thread has a VThreadID with a default name. - * (Or Panics before returning.) - * - *----------------------------------------------------------------------------- - */ - -static void -VThreadBaseSimpleNoID(void) -{ - VThreadID newID; - Bool reused = FALSE; - Bool result; - void *newNative = VThreadBaseGetNative(); - HashTable *ht = VThreadBaseGetNativeHash(); - VThreadBaseData *base; - - /* Require key allocation before TLS read */ - ASSERT(VThreadBaseAreKeysInited()); - - /* Before allocating a new ID, try to reclaim any old IDs. */ - for (newID = 0; - newID < Atomic_Read(&vthreadBaseGlobals.dynamicID); - newID++) { - void *newKey = (void *)(uintptr_t)newID; - - /* - * Windows: any entry that is found and not (alive or NULL) - * is reclaimable. The check is slightly racy, but the race - * would only cause missing a reclaim which isn't a problem. - * Posix: thread exit is hooked (via TLS destructor) and sets - * entries to NULL, so any entry that is NULL is reclaimable. - * UWP: There is no permission to check thread status with the - * thread id. There are only few threads in UWP client, ignore - * the reclaimation. - */ -#if defined(_WIN32) && !defined(VM_WIN_UWP) - void *oldNative; - reused = HashTable_Lookup(ht, newKey, &oldNative) && - (oldNative == NULL || - !VThreadBaseNativeIsAlive(oldNative)) && - HashTable_ReplaceIfEqual(ht, newKey, oldNative, newNative); -#else - reused = HashTable_ReplaceIfEqual(ht, newKey, NULL, newNative); -#endif - if (reused) { - break; - } - } - - if (!reused) { - void *newKey; - - newID = Atomic_ReadInc32(&vthreadBaseGlobals.dynamicID); - /* - * Detect VThreadID overflow (~0 is used as a sentinel). - * Leave a space of ~10 IDs, since the increment and bounds-check - * are not atomic. - */ - VERIFY(newID < VTHREAD_INVALID_ID - 10); - - newKey = (void *)(uintptr_t)newID; - result = HashTable_Insert(ht, newKey, newNative); - VERIFY(result); - } - - /* ID picked. Now do the important stuff. */ - base = Util_SafeCalloc(1, sizeof *base); - base->id = newID; - - result = VThreadBase_InitWithTLS(base); - ASSERT(result); - - if (vmx86_debug && reused) { - Log("VThreadBase reused VThreadID %" FMT64 "u.\n", newID); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseSimpleFreeID -- - * - * Default TLS storage destructor. - * - * The SimpleNoID function uses malloc'd memory to allow an unlimited - * number of VThreads in the process, and uses a hash table to track - * live VThreadIDs so to allow VThreadID recycling. Both of these - * require cleanup. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static void -VThreadBaseSimpleFreeID(void *tlsData) -{ - HashTable *ht = VThreadBaseGetNativeHash(); - VThreadBaseData *data = tlsData; - const void *hashKey = (const void *)(uintptr_t)data->id; - - HashTable_ReplaceOrInsert(ht, hashKey, NULL); - free(data); -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBase_SetNoIDFunc -- - * - * Sets the hook function to be called when a thread is found with - * no VThreadID. The hook function is expected to call - * VThreadBase_InitWithTLS() with a valid new ID. - * - * On Posix, this callback is called with signals masked to prevent - * accidental double-allocation of IDs. On Windows, the constraint is - * that the callback cannot service an APC between allocating an ID and - * initializing the thread with that ID, as such is likely - * to accidentally double-allocate IDs. - * - * An optional destructor can be supplied to clean up memory. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -void -VThreadBase_SetNoIDFunc(void (*hookFunc)(void), // IN: new hook function - void (*destroyFunc)(void *)) // IN/OPT: new TLS destructor -{ - ASSERT(hookFunc); - - /* - * The hook function can only be set once, and must be set before - * any VThreadIDs are allocated so that the hook can control the VThreadID - * namespace. - * - * If the process has had only a single thread, that thread can be forgotten - * via VThreadBase_ForgetSelf() and this function safely called. - */ - ASSERT(vthreadBaseGlobals.noIDFunc == VThreadBaseSimpleNoID && - Atomic_Read(&vthreadBaseGlobals.numThreads) == 0); - - vthreadBaseGlobals.noIDFunc = hookFunc; - vthreadBaseGlobals.freeIDFunc = destroyFunc; -} - - -/* - *----------------------------------------------------------------------------- - * - * VThreadBaseInit -- - * - * Performs basic initialization of this thread including setting - * up the thread local storage and assigning a thread id. - * - *----------------------------------------------------------------------------- - */ - -static void -VThreadBaseInit(void) -{ - VThreadBaseInitKeys(); - - /* - * The code between the last pthread_getspecific and the eventual - * call to pthread_setspecific either needs to run with async signals - * blocked or tolerate reentrancy. Simpler to just block the signals. - * See bugs 295686 & 477318. Here, the problem is that we could allocate - * two VThreadIDs (via a signal during the NoID callback). - */ - - NO_ASYNC_SIGNALS_START; - if (VThreadBaseGetBase() == NULL) { - (*vthreadBaseGlobals.noIDFunc)(); - } - NO_ASYNC_SIGNALS_END; -} - - #if !defined _WIN32 /* *-----------------------------------------------------------------------------