diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Runtime/common/misc/thread.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/misc/thread.cpp')
-rw-r--r-- | src/VBox/Runtime/common/misc/thread.cpp | 1602 |
1 files changed, 1602 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/misc/thread.cpp b/src/VBox/Runtime/common/misc/thread.cpp new file mode 100644 index 00000000..89fc02b7 --- /dev/null +++ b/src/VBox/Runtime/common/misc/thread.cpp @@ -0,0 +1,1602 @@ +/* $Id: thread.cpp $ */ +/** @file + * IPRT - Threads, common routines. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_THREAD +#include <iprt/thread.h> +#include "internal/iprt.h" + +#include <iprt/log.h> +#include <iprt/avl.h> +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/lockvalidator.h> +#include <iprt/semaphore.h> +#ifdef IN_RING0 +# include <iprt/spinlock.h> +#endif +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include "internal/magics.h" +#include "internal/thread.h" +#include "internal/sched.h" +#include "internal/process.h" +#ifdef RT_WITH_ICONV_CACHE +# include "internal/string.h" +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#ifdef IN_RING0 +# define RT_THREAD_LOCK_RW() RTSpinlockAcquire(g_ThreadSpinlock) +# define RT_THREAD_UNLOCK_RW() RTSpinlockRelease(g_ThreadSpinlock) +# define RT_THREAD_LOCK_RD() RTSpinlockAcquire(g_ThreadSpinlock) +# define RT_THREAD_UNLOCK_RD() RTSpinlockRelease(g_ThreadSpinlock) +#else +# define RT_THREAD_LOCK_RW() rtThreadLockRW() +# define RT_THREAD_UNLOCK_RW() rtThreadUnLockRW() +# define RT_THREAD_LOCK_RD() rtThreadLockRD() +# define RT_THREAD_UNLOCK_RD() rtThreadUnLockRD() +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The AVL thread containing the threads. */ +static PAVLPVNODECORE g_ThreadTree; +/** The number of threads in the tree (for ring-0 termination kludge). */ +static uint32_t volatile g_cThreadInTree; +#ifdef IN_RING3 +/** The RW lock protecting the tree. */ +static RTSEMRW g_ThreadRWSem = NIL_RTSEMRW; +#else +/** The spinlocks protecting the tree. */ +static RTSPINLOCK g_ThreadSpinlock = NIL_RTSPINLOCK; +#endif +/** Indicates whether we've been initialized or not. */ +static bool g_frtThreadInitialized; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void rtThreadDestroy(PRTTHREADINT pThread); +#ifdef IN_RING3 +static int rtThreadAdopt(RTTHREADTYPE enmType, unsigned fFlags, uint32_t fIntFlags, const char *pszName); +#endif +static void rtThreadRemoveLocked(PRTTHREADINT pThread); +static PRTTHREADINT rtThreadAlloc(RTTHREADTYPE enmType, unsigned fFlags, uint32_t fIntFlags, const char *pszName); + + +/** @page pg_rt_thread IPRT Thread Internals + * + * IPRT provides interface to whatever native threading that the host provides, + * preferably using a CRT level interface to better integrate with other libraries. + * + * Internally IPRT keeps track of threads by means of the RTTHREADINT structure. + * All the RTTHREADINT structures are kept in a AVL tree which is protected by a + * read/write lock for efficient access. A thread is inserted into the tree in + * three places in the code. The main thread is 'adopted' by IPRT on rtR3Init() + * by rtThreadAdopt(). When creating a new thread there the child and the parent + * race inserting the thread, this is rtThreadMain() and RTThreadCreate. + * + * RTTHREADINT objects are using reference counting as a mean of sticking around + * till no-one needs them any longer. Waitable threads is created with one extra + * reference so they won't go away until they are waited on. This introduces a + * major problem if we use the host thread identifier as key in the AVL tree - the + * host may reuse the thread identifier before the thread was waited on. So, on + * most platforms we are using the RTTHREADINT pointer as key and not the + * thread id. RTThreadSelf() then have to be implemented using a pointer stored + * in thread local storage (TLS). + * + * In Ring-0 we only try keep track of kernel threads created by RTThreadCreate + * at the moment. There we really only need the 'join' feature, but doing things + * the same way allow us to name threads and similar stuff. + */ + + +/** + * Initializes the thread database. + * + * @returns iprt status code. + */ +DECLHIDDEN(int) rtThreadInit(void) +{ +#ifdef IN_RING3 + int rc = VINF_ALREADY_INITIALIZED; + if (g_ThreadRWSem == NIL_RTSEMRW) + { + /* + * We assume the caller is the 1st thread, which we'll call 'main'. + * But first, we'll create the semaphore. + */ + rc = RTSemRWCreateEx(&g_ThreadRWSem, RTSEMRW_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL); + if (RT_SUCCESS(rc)) + { + rc = rtThreadNativeInit(); + if (RT_SUCCESS(rc)) + rc = rtThreadAdopt(RTTHREADTYPE_DEFAULT, 0, RTTHREADINT_FLAGS_MAIN, "main"); + if (RT_SUCCESS(rc)) + rc = rtSchedNativeCalcDefaultPriority(RTTHREADTYPE_DEFAULT); + if (RT_SUCCESS(rc)) + { + g_frtThreadInitialized = true; + return VINF_SUCCESS; + } + + /* failed, clear out */ + RTSemRWDestroy(g_ThreadRWSem); + g_ThreadRWSem = NIL_RTSEMRW; + } + } + +#elif defined(IN_RING0) + int rc; + /* + * Create the spinlock and to native init. + */ + Assert(g_ThreadSpinlock == NIL_RTSPINLOCK); + rc = RTSpinlockCreate(&g_ThreadSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTThread"); + if (RT_SUCCESS(rc)) + { + rc = rtThreadNativeInit(); + if (RT_SUCCESS(rc)) + { + g_frtThreadInitialized = true; + return VINF_SUCCESS; + } + + /* failed, clear out */ + RTSpinlockDestroy(g_ThreadSpinlock); + g_ThreadSpinlock = NIL_RTSPINLOCK; + } +#else +# error "!IN_RING0 && !IN_RING3" +#endif + return rc; +} + + +#ifdef IN_RING3 +/** + * Called when IPRT was first initialized in unobtrusive mode and later changed + * to obtrustive. + * + * This is only applicable in ring-3. + */ +DECLHIDDEN(void) rtThreadReInitObtrusive(void) +{ + rtThreadNativeReInitObtrusive(); +} +#endif + + +/** + * Terminates the thread database. + */ +DECLHIDDEN(void) rtThreadTerm(void) +{ +#ifdef IN_RING3 + /* we don't cleanup here yet */ + +#elif defined(IN_RING0) + /* just destroy the spinlock and assume the thread is fine... */ + RTSpinlockDestroy(g_ThreadSpinlock); + g_ThreadSpinlock = NIL_RTSPINLOCK; + if (g_ThreadTree != NULL) + RTAssertMsg2Weak("WARNING: g_ThreadTree=%p\n", g_ThreadTree); +#endif +} + + +#ifdef IN_RING3 + +DECLINLINE(void) rtThreadLockRW(void) +{ + if (g_ThreadRWSem == NIL_RTSEMRW) + rtThreadInit(); + int rc = RTSemRWRequestWrite(g_ThreadRWSem, RT_INDEFINITE_WAIT); + AssertReleaseRC(rc); +} + + +DECLINLINE(void) rtThreadLockRD(void) +{ + if (g_ThreadRWSem == NIL_RTSEMRW) + rtThreadInit(); + int rc = RTSemRWRequestRead(g_ThreadRWSem, RT_INDEFINITE_WAIT); + AssertReleaseRC(rc); +} + + +DECLINLINE(void) rtThreadUnLockRW(void) +{ + int rc = RTSemRWReleaseWrite(g_ThreadRWSem); + AssertReleaseRC(rc); +} + + +DECLINLINE(void) rtThreadUnLockRD(void) +{ + int rc = RTSemRWReleaseRead(g_ThreadRWSem); + AssertReleaseRC(rc); +} + + +/** + * Adopts the calling thread. + * No locks are taken or released by this function. + */ +static int rtThreadAdopt(RTTHREADTYPE enmType, unsigned fFlags, uint32_t fIntFlags, const char *pszName) +{ + int rc; + PRTTHREADINT pThread; + Assert(!(fFlags & RTTHREADFLAGS_WAITABLE)); + fFlags &= ~RTTHREADFLAGS_WAITABLE; + + /* + * Allocate and insert the thread. + * (It is vital that rtThreadNativeAdopt updates the TLS before + * we try inserting the thread because of locking.) + */ + rc = VERR_NO_MEMORY; + pThread = rtThreadAlloc(enmType, fFlags, RTTHREADINT_FLAGS_ALIEN | fIntFlags, pszName); + if (pThread) + { + RTNATIVETHREAD NativeThread = RTThreadNativeSelf(); + rc = rtThreadNativeAdopt(pThread); + if (RT_SUCCESS(rc)) + { + rtThreadInsert(pThread, NativeThread); + rtThreadSetState(pThread, RTTHREADSTATE_RUNNING); + rtThreadRelease(pThread); + } + else + rtThreadDestroy(pThread); + } + return rc; +} + +/** + * Adopts a non-IPRT thread. + * + * @returns IPRT status code. + * @param enmType The thread type. + * @param fFlags The thread flags. RTTHREADFLAGS_WAITABLE is not currently allowed. + * @param pszName The thread name. Optional. + * @param pThread Where to store the thread handle. Optional. + */ +RTDECL(int) RTThreadAdopt(RTTHREADTYPE enmType, unsigned fFlags, const char *pszName, PRTTHREAD pThread) +{ + int rc; + RTTHREAD Thread; + + AssertReturn(!(fFlags & RTTHREADFLAGS_WAITABLE), VERR_INVALID_PARAMETER); + AssertReturn(!pszName || VALID_PTR(pszName), VERR_INVALID_POINTER); + AssertReturn(!pThread || VALID_PTR(pThread), VERR_INVALID_POINTER); + + rc = VINF_SUCCESS; + Thread = RTThreadSelf(); + if (Thread == NIL_RTTHREAD) + { + /* generate a name if none was given. */ + char szName[RTTHREAD_NAME_LEN]; + if (!pszName || !*pszName) + { + static uint32_t s_i32AlienId = 0; + uint32_t i32Id = ASMAtomicIncU32(&s_i32AlienId); + RTStrPrintf(szName, sizeof(szName), "ALIEN-%RX32", i32Id); + pszName = szName; + } + + /* try adopt it */ + rc = rtThreadAdopt(enmType, fFlags, 0, pszName); + Thread = RTThreadSelf(); + Log(("RTThreadAdopt: %RTthrd %RTnthrd '%s' enmType=%d fFlags=%#x rc=%Rrc\n", + Thread, RTThreadNativeSelf(), pszName, enmType, fFlags, rc)); + } + else + Log(("RTThreadAdopt: %RTthrd %RTnthrd '%s' enmType=%d fFlags=%#x - already adopted!\n", + Thread, RTThreadNativeSelf(), pszName, enmType, fFlags)); + + if (pThread) + *pThread = Thread; + return rc; +} +RT_EXPORT_SYMBOL(RTThreadAdopt); + + +/** + * Get the thread handle of the current thread, automatically adopting alien + * threads. + * + * @returns Thread handle. + */ +RTDECL(RTTHREAD) RTThreadSelfAutoAdopt(void) +{ + RTTHREAD hSelf = RTThreadSelf(); + if (RT_UNLIKELY(hSelf == NIL_RTTHREAD)) + RTThreadAdopt(RTTHREADTYPE_DEFAULT, 0, NULL, &hSelf); + return hSelf; +} +RT_EXPORT_SYMBOL(RTThreadSelfAutoAdopt); + +#endif /* IN_RING3 */ + +/** + * Allocates a per thread data structure and initializes the basic fields. + * + * @returns Pointer to per thread data structure. + * This is reference once. + * @returns NULL on failure. + * @param enmType The thread type. + * @param fFlags The thread flags. + * @param fIntFlags The internal thread flags. + * @param pszName Pointer to the thread name. + */ +PRTTHREADINT rtThreadAlloc(RTTHREADTYPE enmType, unsigned fFlags, uint32_t fIntFlags, const char *pszName) +{ + PRTTHREADINT pThread = (PRTTHREADINT)RTMemAllocZ(sizeof(RTTHREADINT)); + if (pThread) + { + size_t cchName; + int rc; + + pThread->Core.Key = (void*)NIL_RTTHREAD; + pThread->u32Magic = RTTHREADINT_MAGIC; + cchName = strlen(pszName); + if (cchName >= RTTHREAD_NAME_LEN) + cchName = RTTHREAD_NAME_LEN - 1; + memcpy(pThread->szName, pszName, cchName); + pThread->szName[cchName] = '\0'; + pThread->cRefs = 2 + !!(fFlags & RTTHREADFLAGS_WAITABLE); /* And extra reference if waitable. */ + pThread->rc = VERR_PROCESS_RUNNING; /** @todo get a better error code! */ + pThread->enmType = enmType; + pThread->fFlags = fFlags; + pThread->fIntFlags = fIntFlags; + pThread->enmState = RTTHREADSTATE_INITIALIZING; + pThread->fReallySleeping = false; +#ifdef IN_RING3 + rtLockValidatorInitPerThread(&pThread->LockValidator); +#endif +#ifdef RT_WITH_ICONV_CACHE + rtStrIconvCacheInit(pThread); +#endif + rc = RTSemEventMultiCreate(&pThread->EventUser); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventMultiCreate(&pThread->EventTerminated); + if (RT_SUCCESS(rc)) + return pThread; + RTSemEventMultiDestroy(pThread->EventUser); + } + RTMemFree(pThread); + } + return NULL; +} + + +/** + * Insert the per thread data structure into the tree. + * + * This can be called from both the thread it self and the parent, + * thus it must handle insertion failures in a nice manner. + * + * @param pThread Pointer to thread structure allocated by rtThreadAlloc(). + * @param NativeThread The native thread id. + */ +DECLHIDDEN(void) rtThreadInsert(PRTTHREADINT pThread, RTNATIVETHREAD NativeThread) +{ + Assert(pThread); + Assert(pThread->u32Magic == RTTHREADINT_MAGIC); + + { + RT_THREAD_LOCK_RW(); + + /* + * Do not insert a terminated thread. + * + * This may happen if the thread finishes before the RTThreadCreate call + * gets this far. Since the OS may quickly reuse the native thread ID + * it should not be reinserted at this point. + */ + if (rtThreadGetState(pThread) != RTTHREADSTATE_TERMINATED) + { + /* + * Before inserting we must check if there is a thread with this id + * in the tree already. We're racing parent and child on insert here + * so that the handle is valid in both ends when they return / start. + * + * If it's not ourself we find, it's a dead alien thread and we will + * unlink it from the tree. Alien threads will be released at this point. + */ + PRTTHREADINT pThreadOther = (PRTTHREADINT)RTAvlPVGet(&g_ThreadTree, (void *)NativeThread); + if (pThreadOther != pThread) + { + bool fRc; + /* remove dead alien if any */ + if (pThreadOther) + { + AssertMsg(pThreadOther->fIntFlags & RTTHREADINT_FLAGS_ALIEN, ("%p:%s; %p:%s\n", pThread, pThread->szName, pThreadOther, pThreadOther->szName)); + ASMAtomicBitClear(&pThread->fIntFlags, RTTHREADINT_FLAG_IN_TREE_BIT); + rtThreadRemoveLocked(pThreadOther); + if (pThreadOther->fIntFlags & RTTHREADINT_FLAGS_ALIEN) + rtThreadRelease(pThreadOther); + } + + /* insert the thread */ + ASMAtomicWritePtr(&pThread->Core.Key, (void *)NativeThread); + fRc = RTAvlPVInsert(&g_ThreadTree, &pThread->Core); + ASMAtomicOrU32(&pThread->fIntFlags, RTTHREADINT_FLAG_IN_TREE); + if (fRc) + ASMAtomicIncU32(&g_cThreadInTree); + + AssertReleaseMsg(fRc, ("Lock problem? %p (%RTnthrd) %s\n", pThread, NativeThread, pThread->szName)); + NOREF(fRc); + } + } + + RT_THREAD_UNLOCK_RW(); + } +} + + +/** + * Removes the thread from the AVL tree, call owns the tree lock + * and has cleared the RTTHREADINT_FLAG_IN_TREE bit. + * + * @param pThread The thread to remove. + */ +static void rtThreadRemoveLocked(PRTTHREADINT pThread) +{ + PRTTHREADINT pThread2 = (PRTTHREADINT)RTAvlPVRemove(&g_ThreadTree, pThread->Core.Key); +#if !defined(RT_OS_OS2) /** @todo this asserts for threads created by NSPR */ + AssertMsg(pThread2 == pThread, ("%p(%s) != %p (%p/%s)\n", pThread2, pThread2 ? pThread2->szName : "<null>", + pThread, pThread->Core.Key, pThread->szName)); +#endif + if (pThread2) + ASMAtomicDecU32(&g_cThreadInTree); +} + + +/** + * Removes the thread from the AVL tree. + * + * @param pThread The thread to remove. + */ +static void rtThreadRemove(PRTTHREADINT pThread) +{ + RT_THREAD_LOCK_RW(); + if (ASMAtomicBitTestAndClear(&pThread->fIntFlags, RTTHREADINT_FLAG_IN_TREE_BIT)) + rtThreadRemoveLocked(pThread); + RT_THREAD_UNLOCK_RW(); +} + + +/** + * Checks if a thread is alive or not. + * + * @returns true if the thread is alive (or we don't really know). + * @returns false if the thread has surely terminate. + */ +DECLINLINE(bool) rtThreadIsAlive(PRTTHREADINT pThread) +{ + return !(pThread->fIntFlags & RTTHREADINT_FLAGS_TERMINATED); +} + + +/** + * Gets a thread by it's native ID. + * + * @returns pointer to the thread structure. + * @returns NULL if not a thread IPRT knows. + * @param NativeThread The native thread id. + */ +DECLHIDDEN(PRTTHREADINT) rtThreadGetByNative(RTNATIVETHREAD NativeThread) +{ + PRTTHREADINT pThread; + /* + * Simple tree lookup. + */ + RT_THREAD_LOCK_RD(); + pThread = (PRTTHREADINT)RTAvlPVGet(&g_ThreadTree, (void *)NativeThread); + RT_THREAD_UNLOCK_RD(); + return pThread; +} + + +/** + * Gets the per thread data structure for a thread handle. + * + * @returns Pointer to the per thread data structure for Thread. + * The caller must release the thread using rtThreadRelease(). + * @returns NULL if Thread was not found. + * @param Thread Thread id which structure is to be returned. + */ +DECLHIDDEN(PRTTHREADINT) rtThreadGet(RTTHREAD Thread) +{ + if ( Thread != NIL_RTTHREAD + && VALID_PTR(Thread)) + { + PRTTHREADINT pThread = (PRTTHREADINT)Thread; + if ( pThread->u32Magic == RTTHREADINT_MAGIC + && pThread->cRefs > 0) + { + ASMAtomicIncU32(&pThread->cRefs); + return pThread; + } + } + + AssertMsgFailed(("Thread=%RTthrd\n", Thread)); + return NULL; +} + +/** + * Release a per thread data structure. + * + * @returns New reference count. + * @param pThread The thread structure to release. + */ +DECLHIDDEN(uint32_t) rtThreadRelease(PRTTHREADINT pThread) +{ + uint32_t cRefs; + + Assert(pThread); + if (pThread->cRefs >= 1) + { + cRefs = ASMAtomicDecU32(&pThread->cRefs); + if (!cRefs) + rtThreadDestroy(pThread); + } + else + { + cRefs = 0; + AssertFailed(); + } + return cRefs; +} + + +/** + * Destroys the per thread data. + * + * @param pThread The thread to destroy. + */ +static void rtThreadDestroy(PRTTHREADINT pThread) +{ + RTSEMEVENTMULTI hEvt1, hEvt2; + /* + * Remove it from the tree and mark it as dead. + * + * Threads that has seen rtThreadTerminate and should already have been + * removed from the tree. There is probably no thread that should + * require removing here. However, be careful making sure that cRefs + * isn't 0 if we do or we'll blow up because the strict locking code + * will be calling us back. + */ + if (ASMBitTest(&pThread->fIntFlags, RTTHREADINT_FLAG_IN_TREE_BIT)) + { + ASMAtomicIncU32(&pThread->cRefs); + rtThreadRemove(pThread); + ASMAtomicDecU32(&pThread->cRefs); + } + + /* + * Invalidate the thread structure. + */ +#ifdef IN_RING3 + rtLockValidatorSerializeDestructEnter(); + + rtLockValidatorDeletePerThread(&pThread->LockValidator); +#endif +#ifdef RT_WITH_ICONV_CACHE + rtStrIconvCacheDestroy(pThread); +#endif + ASMAtomicXchgU32(&pThread->u32Magic, RTTHREADINT_MAGIC_DEAD); + ASMAtomicWritePtr(&pThread->Core.Key, (void *)NIL_RTTHREAD); + pThread->enmType = RTTHREADTYPE_INVALID; + hEvt1 = pThread->EventUser; + pThread->EventUser = NIL_RTSEMEVENTMULTI; + hEvt2 = pThread->EventTerminated; + pThread->EventTerminated = NIL_RTSEMEVENTMULTI; + +#ifdef IN_RING3 + rtLockValidatorSerializeDestructLeave(); +#endif + + /* + * Destroy semaphore resources and free the bugger. + */ + RTSemEventMultiDestroy(hEvt1); + if (hEvt2 != NIL_RTSEMEVENTMULTI) + RTSemEventMultiDestroy(hEvt2); + + rtThreadNativeDestroy(pThread); + RTMemFree(pThread); +} + + +/** + * Terminates the thread. + * Called by the thread wrapper function when the thread terminates. + * + * @param pThread The thread structure. + * @param rc The thread result code. + */ +DECLHIDDEN(void) rtThreadTerminate(PRTTHREADINT pThread, int rc) +{ + Assert(pThread->cRefs >= 1); + +#ifdef IPRT_WITH_GENERIC_TLS + /* + * Destroy TLS entries. + */ + rtThreadTlsDestruction(pThread); +#endif /* IPRT_WITH_GENERIC_TLS */ + + /* + * Set the rc, mark it terminated and signal anyone waiting. + */ + pThread->rc = rc; + rtThreadSetState(pThread, RTTHREADSTATE_TERMINATED); + ASMAtomicOrU32(&pThread->fIntFlags, RTTHREADINT_FLAGS_TERMINATED); + if (pThread->EventTerminated != NIL_RTSEMEVENTMULTI) + RTSemEventMultiSignal(pThread->EventTerminated); + + /* + * Remove the thread from the tree so that there will be no + * key clashes in the AVL tree and release our reference to ourself. + */ + rtThreadRemove(pThread); + rtThreadRelease(pThread); +} + + +/** + * The common thread main function. + * This is called by rtThreadNativeMain(). + * + * @returns The status code of the thread. + * pThread is dereference by the thread before returning! + * @param pThread The thread structure. + * @param NativeThread The native thread id. + * @param pszThreadName The name of the thread (purely a dummy for backtrace). + */ +DECLCALLBACK(DECLHIDDEN(int)) rtThreadMain(PRTTHREADINT pThread, RTNATIVETHREAD NativeThread, const char *pszThreadName) +{ + int rc; + NOREF(pszThreadName); + rtThreadInsert(pThread, NativeThread); + Log(("rtThreadMain: Starting: pThread=%p NativeThread=%RTnthrd Name=%s pfnThread=%p pvUser=%p\n", + pThread, NativeThread, pThread->szName, pThread->pfnThread, pThread->pvUser)); + + /* + * Change the priority. + */ + rc = rtThreadNativeSetPriority(pThread, pThread->enmType); +#ifdef IN_RING3 + AssertMsgRC(rc, ("Failed to set priority of thread %p (%RTnthrd / %s) to enmType=%d enmPriority=%d rc=%Rrc\n", + pThread, NativeThread, pThread->szName, pThread->enmType, g_enmProcessPriority, rc)); +#else + AssertMsgRC(rc, ("Failed to set priority of thread %p (%RTnthrd / %s) to enmType=%d rc=%Rrc\n", + pThread, NativeThread, pThread->szName, pThread->enmType, rc)); +#endif + + /* + * Call thread function and terminate when it returns. + */ + rtThreadSetState(pThread, RTTHREADSTATE_RUNNING); + rc = pThread->pfnThread(pThread, pThread->pvUser); + + /* + * Paranoia checks for leftover resources. + */ +#ifdef RTSEMRW_STRICT + int32_t cWrite = ASMAtomicReadS32(&pThread->cWriteLocks); + Assert(!cWrite); + int32_t cRead = ASMAtomicReadS32(&pThread->cReadLocks); + Assert(!cRead); +#endif + + Log(("rtThreadMain: Terminating: rc=%d pThread=%p NativeThread=%RTnthrd Name=%s pfnThread=%p pvUser=%p\n", + rc, pThread, NativeThread, pThread->szName, pThread->pfnThread, pThread->pvUser)); + rtThreadTerminate(pThread, rc); + return rc; +} + + +/** + * Create a new thread. + * + * @returns iprt status code. + * @param pThread Where to store the thread handle to the new thread. (optional) + * @param pfnThread The thread function. + * @param pvUser User argument. + * @param cbStack The size of the stack for the new thread. + * Use 0 for the default stack size. + * @param enmType The thread type. Used for deciding scheduling attributes + * of the thread. + * @param fFlags Flags of the RTTHREADFLAGS type (ORed together). + * @param pszName Thread name. + */ +RTDECL(int) RTThreadCreate(PRTTHREAD pThread, PFNRTTHREAD pfnThread, void *pvUser, size_t cbStack, + RTTHREADTYPE enmType, unsigned fFlags, const char *pszName) +{ + int rc; + PRTTHREADINT pThreadInt; + + LogFlow(("RTThreadCreate: pThread=%p pfnThread=%p pvUser=%p cbStack=%#x enmType=%d fFlags=%#x pszName=%p:{%s}\n", + pThread, pfnThread, pvUser, cbStack, enmType, fFlags, pszName, pszName)); + + /* + * Validate input. + */ + if (!VALID_PTR(pThread) && pThread) + { + Assert(VALID_PTR(pThread)); + return VERR_INVALID_PARAMETER; + } + if (!VALID_PTR(pfnThread)) + { + Assert(VALID_PTR(pfnThread)); + return VERR_INVALID_PARAMETER; + } + if (!pszName || !*pszName || strlen(pszName) >= RTTHREAD_NAME_LEN) + { + AssertMsgFailed(("pszName=%s (max len is %d because of logging)\n", pszName, RTTHREAD_NAME_LEN - 1)); + return VERR_INVALID_PARAMETER; + } + if (fFlags & ~RTTHREADFLAGS_MASK) + { + AssertMsgFailed(("fFlags=%#x\n", fFlags)); + return VERR_INVALID_PARAMETER; + } + + /* + * Allocate thread argument. + */ + pThreadInt = rtThreadAlloc(enmType, fFlags, 0, pszName); + if (pThreadInt) + { + RTNATIVETHREAD NativeThread; + + pThreadInt->pfnThread = pfnThread; + pThreadInt->pvUser = pvUser; + pThreadInt->cbStack = cbStack; + + rc = rtThreadNativeCreate(pThreadInt, &NativeThread); + if (RT_SUCCESS(rc)) + { + rtThreadInsert(pThreadInt, NativeThread); + rtThreadRelease(pThreadInt); + Log(("RTThreadCreate: Created thread %p (%p) %s\n", pThreadInt, NativeThread, pszName)); + if (pThread) + *pThread = pThreadInt; + return VINF_SUCCESS; + } + + pThreadInt->cRefs = 1; + rtThreadRelease(pThreadInt); + } + else + rc = VERR_NO_TMP_MEMORY; + LogFlow(("RTThreadCreate: Failed to create thread, rc=%Rrc\n", rc)); + AssertReleaseRC(rc); + return rc; +} +RT_EXPORT_SYMBOL(RTThreadCreate); + + +/** + * Create a new thread. + * + * Same as RTThreadCreate except the name is given in the RTStrPrintfV form. + * + * @returns iprt status code. + * @param pThread See RTThreadCreate. + * @param pfnThread See RTThreadCreate. + * @param pvUser See RTThreadCreate. + * @param cbStack See RTThreadCreate. + * @param enmType See RTThreadCreate. + * @param fFlags See RTThreadCreate. + * @param pszNameFmt Thread name format. + * @param va Format arguments. + */ +RTDECL(int) RTThreadCreateV(PRTTHREAD pThread, PFNRTTHREAD pfnThread, void *pvUser, size_t cbStack, + RTTHREADTYPE enmType, uint32_t fFlags, const char *pszNameFmt, va_list va) +{ + char szName[RTTHREAD_NAME_LEN * 2]; + RTStrPrintfV(szName, sizeof(szName), pszNameFmt, va); + return RTThreadCreate(pThread, pfnThread, pvUser, cbStack, enmType, fFlags, szName); +} +RT_EXPORT_SYMBOL(RTThreadCreateV); + + +/** + * Create a new thread. + * + * Same as RTThreadCreate except the name is given in the RTStrPrintf form. + * + * @returns iprt status code. + * @param pThread See RTThreadCreate. + * @param pfnThread See RTThreadCreate. + * @param pvUser See RTThreadCreate. + * @param cbStack See RTThreadCreate. + * @param enmType See RTThreadCreate. + * @param fFlags See RTThreadCreate. + * @param pszNameFmt Thread name format. + * @param ... Format arguments. + */ +RTDECL(int) RTThreadCreateF(PRTTHREAD pThread, PFNRTTHREAD pfnThread, void *pvUser, size_t cbStack, + RTTHREADTYPE enmType, uint32_t fFlags, const char *pszNameFmt, ...) +{ + va_list va; + int rc; + va_start(va, pszNameFmt); + rc = RTThreadCreateV(pThread, pfnThread, pvUser, cbStack, enmType, fFlags, pszNameFmt, va); + va_end(va); + return rc; +} +RT_EXPORT_SYMBOL(RTThreadCreateF); + + +/** + * Gets the native thread id of a IPRT thread. + * + * @returns The native thread id. + * @param Thread The IPRT thread. + */ +RTDECL(RTNATIVETHREAD) RTThreadGetNative(RTTHREAD Thread) +{ + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + RTNATIVETHREAD NativeThread = (RTNATIVETHREAD)pThread->Core.Key; + rtThreadRelease(pThread); + return NativeThread; + } + return NIL_RTNATIVETHREAD; +} +RT_EXPORT_SYMBOL(RTThreadGetNative); + + +/** + * Gets the IPRT thread of a native thread. + * + * @returns The IPRT thread handle + * @returns NIL_RTTHREAD if not a thread known to IPRT. + * @param NativeThread The native thread handle/id. + */ +RTDECL(RTTHREAD) RTThreadFromNative(RTNATIVETHREAD NativeThread) +{ + PRTTHREADINT pThread = rtThreadGetByNative(NativeThread); + if (pThread) + return pThread; + return NIL_RTTHREAD; +} +RT_EXPORT_SYMBOL(RTThreadFromNative); + + +/** + * Gets the name of the current thread thread. + * + * @returns Pointer to readonly name string. + * @returns NULL on failure. + */ +RTDECL(const char *) RTThreadSelfName(void) +{ + RTTHREAD Thread = RTThreadSelf(); + if (Thread != NIL_RTTHREAD) + { + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + const char *szName = pThread->szName; + rtThreadRelease(pThread); + return szName; + } + } + return NULL; +} +RT_EXPORT_SYMBOL(RTThreadSelfName); + + +/** + * Gets the name of a thread. + * + * @returns Pointer to readonly name string. + * @returns NULL on failure. + * @param Thread Thread handle of the thread to query the name of. + */ +RTDECL(const char *) RTThreadGetName(RTTHREAD Thread) +{ + PRTTHREADINT pThread; + if (Thread == NIL_RTTHREAD) + return NULL; + pThread = rtThreadGet(Thread); + if (pThread) + { + const char *szName = pThread->szName; + rtThreadRelease(pThread); + return szName; + } + return NULL; +} +RT_EXPORT_SYMBOL(RTThreadGetName); + + +/** + * Sets the name of a thread. + * + * @returns iprt status code. + * @param Thread Thread handle of the thread to query the name of. + * @param pszName The thread name. + */ +RTDECL(int) RTThreadSetName(RTTHREAD Thread, const char *pszName) +{ + /* + * Validate input. + */ + PRTTHREADINT pThread; + size_t cchName = strlen(pszName); + if (cchName >= RTTHREAD_NAME_LEN) + { + AssertMsgFailed(("pszName=%s is too long, max is %d\n", pszName, RTTHREAD_NAME_LEN - 1)); + return VERR_INVALID_PARAMETER; + } + pThread = rtThreadGet(Thread); + if (!pThread) + return VERR_INVALID_HANDLE; + + /* + * Update the name. + */ + pThread->szName[cchName] = '\0'; /* paranoia */ + memcpy(pThread->szName, pszName, cchName); + rtThreadRelease(pThread); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTThreadSetName); + + +/** + * Checks if the specified thread is the main thread. + * + * @returns true if it is, false if it isn't. + * + * @param hThread The thread handle. + * + * @remarks This function may not return the correct value when rtR3Init was + * called on a thread of the than the main one. This could for + * instance happen when the DLL/DYLIB/SO containing IPRT is dynamically + * loaded at run time by a different thread. + */ +RTDECL(bool) RTThreadIsMain(RTTHREAD hThread) +{ + PRTTHREADINT pThread = rtThreadGet(hThread); + if (pThread) + { + bool fRc = !!(pThread->fIntFlags & RTTHREADINT_FLAGS_MAIN); + rtThreadRelease(pThread); + return fRc; + } + return false; +} +RT_EXPORT_SYMBOL(RTThreadIsMain); + + +RTDECL(bool) RTThreadIsSelfAlive(void) +{ + if (g_frtThreadInitialized) + { + RTTHREAD hSelf = RTThreadSelf(); + if (hSelf != NIL_RTTHREAD) + { + /* + * Inspect the thread state. ASSUMES thread state order. + */ + RTTHREADSTATE enmState = rtThreadGetState(hSelf); + if ( enmState >= RTTHREADSTATE_RUNNING + && enmState <= RTTHREADSTATE_END) + return true; + } + } + return false; +} +RT_EXPORT_SYMBOL(RTThreadIsSelfAlive); + + +RTDECL(bool) RTThreadIsSelfKnown(void) +{ + if (g_frtThreadInitialized) + { + RTTHREAD hSelf = RTThreadSelf(); + if (hSelf != NIL_RTTHREAD) + return true; + } + return false; +} +RT_EXPORT_SYMBOL(RTThreadIsSelfKnown); + + +RTDECL(bool) RTThreadIsInitialized(void) +{ + return g_frtThreadInitialized; +} +RT_EXPORT_SYMBOL(RTThreadIsInitialized); + + +/** + * Signal the user event. + * + * @returns iprt status code. + */ +RTDECL(int) RTThreadUserSignal(RTTHREAD Thread) +{ + int rc; + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + rc = RTSemEventMultiSignal(pThread->EventUser); + rtThreadRelease(pThread); + } + else + rc = VERR_INVALID_HANDLE; + return rc; +} +RT_EXPORT_SYMBOL(RTThreadUserSignal); + + +/** + * Wait for the user event, resume on interruption. + * + * @returns iprt status code. + * @param Thread The thread to wait for. + * @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for + * an indefinite wait. + */ +RTDECL(int) RTThreadUserWait(RTTHREAD Thread, RTMSINTERVAL cMillies) +{ + int rc; + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + rc = RTSemEventMultiWait(pThread->EventUser, cMillies); + rtThreadRelease(pThread); + } + else + rc = VERR_INVALID_HANDLE; + return rc; +} +RT_EXPORT_SYMBOL(RTThreadUserWait); + + +/** + * Wait for the user event, return on interruption. + * + * @returns iprt status code. + * @param Thread The thread to wait for. + * @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for + * an indefinite wait. + */ +RTDECL(int) RTThreadUserWaitNoResume(RTTHREAD Thread, RTMSINTERVAL cMillies) +{ + int rc; + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + rc = RTSemEventMultiWaitNoResume(pThread->EventUser, cMillies); + rtThreadRelease(pThread); + } + else + rc = VERR_INVALID_HANDLE; + return rc; +} +RT_EXPORT_SYMBOL(RTThreadUserWaitNoResume); + + +/** + * Reset the user event. + * + * @returns iprt status code. + * @param Thread The thread to reset. + */ +RTDECL(int) RTThreadUserReset(RTTHREAD Thread) +{ + int rc; + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + rc = RTSemEventMultiReset(pThread->EventUser); + rtThreadRelease(pThread); + } + else + rc = VERR_INVALID_HANDLE; + return rc; +} +RT_EXPORT_SYMBOL(RTThreadUserReset); + + +/** + * Wait for the thread to terminate. + * + * @returns iprt status code. + * @param Thread The thread to wait for. + * @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for + * an indefinite wait. + * @param prc Where to store the return code of the thread. Optional. + * @param fAutoResume Whether or not to resume the wait on VERR_INTERRUPTED. + */ +static int rtThreadWait(RTTHREAD Thread, RTMSINTERVAL cMillies, int *prc, bool fAutoResume) +{ + int rc = VERR_INVALID_HANDLE; + if (Thread != NIL_RTTHREAD) + { + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + if (pThread->fFlags & RTTHREADFLAGS_WAITABLE) + { +#if defined(IN_RING3) && defined(RT_OS_WINDOWS) + if (RT_LIKELY(rtThreadNativeIsAliveKludge(pThread))) +#endif + { + if (fAutoResume) + rc = RTSemEventMultiWait(pThread->EventTerminated, cMillies); + else + rc = RTSemEventMultiWaitNoResume(pThread->EventTerminated, cMillies); + } +#if defined(IN_RING3) && defined(RT_OS_WINDOWS) + else + { + rc = VINF_SUCCESS; + if (pThread->rc == VERR_PROCESS_RUNNING) + pThread->rc = VERR_THREAD_IS_DEAD; + } +#endif + if (RT_SUCCESS(rc)) + { + if (prc) + *prc = pThread->rc; + + /* + * If the thread is marked as waitable, we'll do one additional + * release in order to free up the thread structure (see how we + * init cRef in rtThreadAlloc()). + */ + if (ASMAtomicBitTestAndClear(&pThread->fFlags, RTTHREADFLAGS_WAITABLE_BIT)) + { + rtThreadRelease(pThread); +#ifdef IN_RING0 + /* + * IPRT termination kludge. Call native code to make sure + * the last thread is really out of IPRT to prevent it from + * crashing after we destroyed the spinlock in rtThreadTerm. + */ + if ( ASMAtomicReadU32(&g_cThreadInTree) == 1 + && ASMAtomicReadU32(&pThread->cRefs) > 1) + rtThreadNativeWaitKludge(pThread); +#endif + } + } + } + else + { + rc = VERR_THREAD_NOT_WAITABLE; + AssertRC(rc); + } + rtThreadRelease(pThread); + } + } + return rc; +} + + +/** + * Wait for the thread to terminate, resume on interruption. + * + * @returns iprt status code. + * Will not return VERR_INTERRUPTED. + * @param Thread The thread to wait for. + * @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for + * an indefinite wait. + * @param prc Where to store the return code of the thread. Optional. + */ +RTDECL(int) RTThreadWait(RTTHREAD Thread, RTMSINTERVAL cMillies, int *prc) +{ + int rc = rtThreadWait(Thread, cMillies, prc, true); + Assert(rc != VERR_INTERRUPTED); + return rc; +} +RT_EXPORT_SYMBOL(RTThreadWait); + + +/** + * Wait for the thread to terminate, return on interruption. + * + * @returns iprt status code. + * @param Thread The thread to wait for. + * @param cMillies The number of milliseconds to wait. Use RT_INDEFINITE_WAIT for + * an indefinite wait. + * @param prc Where to store the return code of the thread. Optional. + */ +RTDECL(int) RTThreadWaitNoResume(RTTHREAD Thread, RTMSINTERVAL cMillies, int *prc) +{ + return rtThreadWait(Thread, cMillies, prc, false); +} +RT_EXPORT_SYMBOL(RTThreadWaitNoResume); + + +/** + * Changes the type of the specified thread. + * + * @returns iprt status code. + * @param Thread The thread which type should be changed. + * @param enmType The new thread type. + */ +RTDECL(int) RTThreadSetType(RTTHREAD Thread, RTTHREADTYPE enmType) +{ + /* + * Validate input. + */ + int rc; + if ( enmType > RTTHREADTYPE_INVALID + && enmType < RTTHREADTYPE_END) + { + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + if (rtThreadIsAlive(pThread)) + { + /* + * Do the job. + */ + RT_THREAD_LOCK_RW(); + rc = rtThreadNativeSetPriority(pThread, enmType); + if (RT_SUCCESS(rc)) + ASMAtomicXchgSize(&pThread->enmType, enmType); + RT_THREAD_UNLOCK_RW(); + if (RT_FAILURE(rc)) + Log(("RTThreadSetType: failed on thread %p (%s), rc=%Rrc!!!\n", Thread, pThread->szName, rc)); + } + else + rc = VERR_THREAD_IS_DEAD; + rtThreadRelease(pThread); + } + else + rc = VERR_INVALID_HANDLE; + } + else + { + AssertMsgFailed(("enmType=%d\n", enmType)); + rc = VERR_INVALID_PARAMETER; + } + return rc; +} +RT_EXPORT_SYMBOL(RTThreadSetType); + + +/** + * Gets the type of the specified thread. + * + * @returns The thread type. + * @returns RTTHREADTYPE_INVALID if the thread handle is invalid. + * @param Thread The thread in question. + */ +RTDECL(RTTHREADTYPE) RTThreadGetType(RTTHREAD Thread) +{ + RTTHREADTYPE enmType = RTTHREADTYPE_INVALID; + PRTTHREADINT pThread = rtThreadGet(Thread); + if (pThread) + { + enmType = pThread->enmType; + rtThreadRelease(pThread); + } + return enmType; +} +RT_EXPORT_SYMBOL(RTThreadGetType); + +#ifdef IN_RING3 + +/** + * Recalculates scheduling attributes for the default process + * priority using the specified priority type for the calling thread. + * + * The scheduling attributes are targeted at threads and they are protected + * by the thread read-write semaphore, that's why RTProc is forwarding the + * operation to RTThread. + * + * @returns iprt status code. + * @remarks Will only work for strict builds. + */ +int rtThreadDoCalcDefaultPriority(RTTHREADTYPE enmType) +{ + RT_THREAD_LOCK_RW(); + int rc = rtSchedNativeCalcDefaultPriority(enmType); + RT_THREAD_UNLOCK_RW(); + return rc; +} + + +/** + * Thread enumerator - sets the priority of one thread. + * + * @returns 0 to continue. + * @returns !0 to stop. In our case a VERR_ code. + * @param pNode The thread node. + * @param pvUser The new priority. + */ +static DECLCALLBACK(int) rtThreadSetPriorityOne(PAVLPVNODECORE pNode, void *pvUser) +{ + PRTTHREADINT pThread = (PRTTHREADINT)pNode; + if (!rtThreadIsAlive(pThread)) + return VINF_SUCCESS; + int rc = rtThreadNativeSetPriority(pThread, pThread->enmType); + if (RT_SUCCESS(rc)) /* hide any warnings */ + return VINF_SUCCESS; + NOREF(pvUser); + return rc; +} + + +/** + * Attempts to alter the priority of the current process. + * + * The scheduling attributes are targeted at threads and they are protected + * by the thread read-write semaphore, that's why RTProc is forwarding the + * operation to RTThread. This operation also involves updating all thread + * which is much faster done from RTThread. + * + * @returns iprt status code. + * @param enmPriority The new priority. + */ +DECLHIDDEN(int) rtThreadDoSetProcPriority(RTPROCPRIORITY enmPriority) +{ + LogFlow(("rtThreadDoSetProcPriority: enmPriority=%d\n", enmPriority)); + + /* + * First validate that we're allowed by the OS to use all the + * scheduling attributes defined by the specified process priority. + */ + RT_THREAD_LOCK_RW(); + int rc = rtProcNativeSetPriority(enmPriority); + if (RT_SUCCESS(rc)) + { + /* + * Update the priority of existing thread. + */ + rc = RTAvlPVDoWithAll(&g_ThreadTree, true, rtThreadSetPriorityOne, NULL); + if (RT_SUCCESS(rc)) + ASMAtomicXchgSize(&g_enmProcessPriority, enmPriority); + else + { + /* + * Failed, restore the priority. + */ + rtProcNativeSetPriority(g_enmProcessPriority); + RTAvlPVDoWithAll(&g_ThreadTree, true, rtThreadSetPriorityOne, NULL); + } + } + RT_THREAD_UNLOCK_RW(); + LogFlow(("rtThreadDoSetProcPriority: returns %Rrc\n", rc)); + return rc; +} + + +/** + * Change the thread state to blocking. + * + * @param hThread The current thread. + * @param enmState The sleep state. + * @param fReallySleeping Really going to sleep now. + */ +RTDECL(void) RTThreadBlocking(RTTHREAD hThread, RTTHREADSTATE enmState, bool fReallySleeping) +{ + Assert(RTTHREAD_IS_SLEEPING(enmState)); + PRTTHREADINT pThread = hThread; + if (pThread != NIL_RTTHREAD) + { + Assert(pThread == RTThreadSelf()); + if (rtThreadGetState(pThread) == RTTHREADSTATE_RUNNING) + rtThreadSetState(pThread, enmState); + ASMAtomicWriteBool(&pThread->fReallySleeping, fReallySleeping); + } +} +RT_EXPORT_SYMBOL(RTThreadBlocking); + + +/** + * Unblocks a thread. + * + * This function is paired with rtThreadBlocking. + * + * @param hThread The current thread. + * @param enmCurState The current state, used to check for nested blocking. + * The new state will be running. + */ +RTDECL(void) RTThreadUnblocked(RTTHREAD hThread, RTTHREADSTATE enmCurState) +{ + PRTTHREADINT pThread = hThread; + if (pThread != NIL_RTTHREAD) + { + Assert(pThread == RTThreadSelf()); + ASMAtomicWriteBool(&pThread->fReallySleeping, false); + + RTTHREADSTATE enmActualState = rtThreadGetState(pThread); + if (enmActualState == enmCurState) + { + rtThreadSetState(pThread, RTTHREADSTATE_RUNNING); + if ( pThread->LockValidator.pRec + && pThread->LockValidator.enmRecState == enmCurState) + ASMAtomicWriteNullPtr(&pThread->LockValidator.pRec); + } + /* This is a bit ugly... :-/ */ + else if ( ( enmActualState == RTTHREADSTATE_TERMINATED + || enmActualState == RTTHREADSTATE_INITIALIZING) + && pThread->LockValidator.pRec) + ASMAtomicWriteNullPtr(&pThread->LockValidator.pRec); + Assert( pThread->LockValidator.pRec == NULL + || RTTHREAD_IS_SLEEPING(enmActualState)); + } +} +RT_EXPORT_SYMBOL(RTThreadUnblocked); + + +/** + * Get the current thread state. + * + * @returns The thread state. + * @param hThread The thread. + */ +RTDECL(RTTHREADSTATE) RTThreadGetState(RTTHREAD hThread) +{ + RTTHREADSTATE enmState = RTTHREADSTATE_INVALID; + PRTTHREADINT pThread = rtThreadGet(hThread); + if (pThread) + { + enmState = rtThreadGetState(pThread); + rtThreadRelease(pThread); + } + return enmState; +} +RT_EXPORT_SYMBOL(RTThreadGetState); + + +RTDECL(RTTHREADSTATE) RTThreadGetReallySleeping(RTTHREAD hThread) +{ + RTTHREADSTATE enmState = RTTHREADSTATE_INVALID; + PRTTHREADINT pThread = rtThreadGet(hThread); + if (pThread) + { + enmState = rtThreadGetState(pThread); + if (!ASMAtomicUoReadBool(&pThread->fReallySleeping)) + enmState = RTTHREADSTATE_RUNNING; + rtThreadRelease(pThread); + } + return enmState; +} +RT_EXPORT_SYMBOL(RTThreadGetReallySleeping); + + +/** + * Translate a thread state into a string. + * + * @returns Pointer to a read-only string containing the state name. + * @param enmState The state. + */ +RTDECL(const char *) RTThreadStateName(RTTHREADSTATE enmState) +{ + switch (enmState) + { + case RTTHREADSTATE_INVALID: return "INVALID"; + case RTTHREADSTATE_INITIALIZING: return "INITIALIZING"; + case RTTHREADSTATE_TERMINATED: return "TERMINATED"; + case RTTHREADSTATE_RUNNING: return "RUNNING"; + case RTTHREADSTATE_CRITSECT: return "CRITSECT"; + case RTTHREADSTATE_EVENT: return "EVENT"; + case RTTHREADSTATE_EVENT_MULTI: return "EVENT_MULTI"; + case RTTHREADSTATE_FAST_MUTEX: return "FAST_MUTEX"; + case RTTHREADSTATE_MUTEX: return "MUTEX"; + case RTTHREADSTATE_RW_READ: return "RW_READ"; + case RTTHREADSTATE_RW_WRITE: return "RW_WRITE"; + case RTTHREADSTATE_SLEEP: return "SLEEP"; + case RTTHREADSTATE_SPIN_MUTEX: return "SPIN_MUTEX"; + default: return "UnknownThreadState"; + } +} +RT_EXPORT_SYMBOL(RTThreadStateName); + +#endif /* IN_RING3 */ +#ifdef IPRT_WITH_GENERIC_TLS + +/** + * Thread enumerator - clears a TLS entry. + * + * @returns 0. + * @param pNode The thread node. + * @param pvUser The TLS index. + */ +static DECLCALLBACK(int) rtThreadClearTlsEntryCallback(PAVLPVNODECORE pNode, void *pvUser) +{ + PRTTHREADINT pThread = (PRTTHREADINT)pNode; + RTTLS iTls = (RTTLS)(uintptr_t)pvUser; + ASMAtomicWriteNullPtr(&pThread->apvTlsEntries[iTls]); + return 0; +} + + +/** + * Helper for the generic TLS implementation that clears a given TLS + * entry on all threads. + * + * @param iTls The TLS entry. (valid) + */ +DECLHIDDEN(void) rtThreadClearTlsEntry(RTTLS iTls) +{ + RT_THREAD_LOCK_RD(); + RTAvlPVDoWithAll(&g_ThreadTree, true /* fFromLeft*/, rtThreadClearTlsEntryCallback, (void *)(uintptr_t)iTls); + RT_THREAD_UNLOCK_RD(); +} + +#endif /* IPRT_WITH_GENERIC_TLS */ + + +#if defined(RT_OS_WINDOWS) && defined(IN_RING3) + +/** + * Thread enumeration callback for RTThreadNameThreads + */ +static DECLCALLBACK(int) rtThreadNameThreadCallback(PAVLPVNODECORE pNode, void *pvUser) +{ + PRTTHREADINT pThread = (PRTTHREADINT)pNode; + rtThreadNativeInformDebugger(pThread); + RT_NOREF_PV(pvUser); + return 0; +} + +/** + * A function that can be called from the windows debugger to get the names of + * all threads when attaching to a process. + * + * Usage: .call VBoxRT!RTThreadNameThreads() + * + * @returns 0 + * @remarks Do not call from source code as it skips locks. + */ +extern "C" RTDECL(int) RTThreadNameThreads(void); +RTDECL(int) RTThreadNameThreads(void) +{ + return RTAvlPVDoWithAll(&g_ThreadTree, true /* fFromLeft*/, rtThreadNameThreadCallback, NULL); +} + +#endif |