summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/misc/thread.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Runtime/common/misc/thread.cpp
parentInitial commit. (diff)
downloadvirtualbox-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.cpp1602
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