summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3/posix/thread-posix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/r3/posix/thread-posix.cpp')
-rw-r--r--src/VBox/Runtime/r3/posix/thread-posix.cpp780
1 files changed, 780 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/posix/thread-posix.cpp b/src/VBox/Runtime/r3/posix/thread-posix.cpp
new file mode 100644
index 00000000..cd621ce0
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/thread-posix.cpp
@@ -0,0 +1,780 @@
+/* $Id: thread-posix.cpp $ */
+/** @file
+ * IPRT - Threads, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#if defined(RT_OS_LINUX)
+# include <unistd.h>
+# include <sys/syscall.h>
+#endif
+#if defined(RT_OS_SOLARIS)
+# include <sched.h>
+# include <sys/resource.h>
+#endif
+#if defined(RT_OS_DARWIN)
+# include <mach/thread_act.h>
+# include <mach/thread_info.h>
+# include <mach/host_info.h>
+# include <mach/mach_init.h>
+# include <mach/mach_host.h>
+#endif
+#if defined(RT_OS_DARWIN) /*|| defined(RT_OS_FREEBSD) - later */ \
+ || (defined(RT_OS_LINUX) && !defined(IN_RT_STATIC) /* static + dlsym = trouble */) \
+ || defined(IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP)
+# define IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+# include <dlfcn.h>
+#endif
+#if defined(RT_OS_HAIKU)
+# include <OS.h>
+#endif
+#if defined(RT_OS_DARWIN)
+# define sigprocmask pthread_sigmask /* On xnu sigprocmask works on the process, not the calling thread as elsewhere. */
+#endif
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/list.h>
+#include <iprt/once.h>
+#include <iprt/critsect.h>
+#include <iprt/req.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*#ifndef IN_GUEST - shouldn't need to exclude this now with the non-obtrusive init option. */
+/** Includes RTThreadPoke. */
+# define RTTHREAD_POSIX_WITH_POKE
+/*#endif*/
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The pthread key in which we store the pointer to our own PRTTHREAD structure.
+ * @note There is a defined NIL value here, nor can we really assume this is an
+ * integer. However, zero is a valid key on Linux, so we get into trouble
+ * if we accidentally use it uninitialized.
+ *
+ * So, we ASSUME it's a integer value and the valid range is in approx 0
+ * to PTHREAD_KEYS_MAX. Solaris has at least one negative value (-1)
+ * defined. Thus, we go for 16 MAX values below zero and keep our fingers
+ * cross that it will always be an invalid key value everywhere...
+ *
+ * See also NIL_RTTLS, which is -1.
+ */
+static pthread_key_t g_SelfKey = (pthread_key_t)(-PTHREAD_KEYS_MAX * 16);
+#ifdef RTTHREAD_POSIX_WITH_POKE
+/** The signal we use for poking threads.
+ * This is set to -1 if no available signal was found. */
+static int volatile g_iSigPokeThread = -1;
+#endif
+
+#ifdef IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+# if defined(RT_OS_DARWIN)
+/**
+ * The Mac OS X (10.6 and later) variant of pthread_setname_np.
+ *
+ * @returns errno.h
+ * @param pszName The new thread name.
+ */
+typedef int (*PFNPTHREADSETNAME)(const char *pszName);
+# else
+/**
+ * The variant of pthread_setname_np most other unix-like systems implement.
+ *
+ * @returns errno.h
+ * @param hThread The thread.
+ * @param pszName The new thread name.
+ */
+typedef int (*PFNPTHREADSETNAME)(pthread_t hThread, const char *pszName);
+# endif
+
+/** Pointer to pthread_setname_np if found. */
+static PFNPTHREADSETNAME g_pfnThreadSetName = NULL;
+#endif /* IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP */
+
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+/** Atomic indicator of whether the priority proxy thread has been (attempted) started.
+ *
+ * The priority proxy thread is started under these circumstances:
+ * - RTThreadCreate
+ * - RTThreadSetType
+ * - RTProcSetPriority
+ *
+ * Which means that we'll be single threaded when this is modified.
+ *
+ * Speical values:
+ * - VERR_TRY_AGAIN: Not yet started.
+ * - VERR_WRONG_ORDER: Starting.
+ * - VINF_SUCCESS: Started successfully.
+ * - VERR_PROCESS_NOT_FOUND: Stopping or stopped
+ * - Other error status if failed to start.
+ *
+ * @note We could potentially optimize this by only start it when we lower the
+ * priority of ourselves, the process, or a newly created thread. But
+ * that would means we would need to take multi-threading into account, so
+ * let's not do that for now.
+ */
+static int32_t volatile g_rcPriorityProxyThreadStart = VERR_TRY_AGAIN;
+/** The IPRT thread handle for the priority proxy. */
+static RTTHREAD g_hRTThreadPosixPriorityProxyThread = NIL_RTTHREAD;
+/** The priority proxy queue. */
+static RTREQQUEUE g_hRTThreadPosixPriorityProxyQueue = NIL_RTREQQUEUE;
+#endif /* RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void *rtThreadNativeMain(void *pvArgs);
+static void rtThreadKeyDestruct(void *pvValue);
+#ifdef RTTHREAD_POSIX_WITH_POKE
+static void rtThreadPosixPokeSignal(int iSignal);
+#endif
+
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+/**
+ * Try register the dummy signal handler for RTThreadPoke.
+ */
+static void rtThreadPosixSelectPokeSignal(void)
+{
+ /*
+ * Note! Avoid SIGRTMIN thru SIGRTMIN+2 because of LinuxThreads.
+ */
+# if !defined(RT_OS_LINUX) && !defined(RT_OS_SOLARIS) /* glibc defines SIGRTMAX to __libc_current_sigrtmax() and Solaris libc defines it relying on _sysconf(), causing compiler to deploy serialization here. */
+ static
+# endif
+ const int s_aiSigCandidates[] =
+ {
+# ifdef SIGRTMAX
+ SIGRTMAX-3,
+ SIGRTMAX-2,
+ SIGRTMAX-1,
+# endif
+# ifndef RT_OS_SOLARIS
+ SIGUSR2,
+# endif
+ SIGWINCH
+ };
+
+ g_iSigPokeThread = -1;
+ if (!RTR3InitIsUnobtrusive())
+ {
+ for (unsigned iSig = 0; iSig < RT_ELEMENTS(s_aiSigCandidates); iSig++)
+ {
+ struct sigaction SigActOld;
+ if (!sigaction(s_aiSigCandidates[iSig], NULL, &SigActOld))
+ {
+ if ( SigActOld.sa_handler == SIG_DFL
+ || SigActOld.sa_handler == rtThreadPosixPokeSignal)
+ {
+ struct sigaction SigAct;
+ RT_ZERO(SigAct);
+ SigAct.sa_handler = rtThreadPosixPokeSignal;
+ SigAct.sa_flags = 0; /* no SA_RESTART! */
+ sigfillset(&SigAct.sa_mask);
+
+ /* ASSUMES no sigaction race... (lazy bird) */
+ if (!sigaction(s_aiSigCandidates[iSig], &SigAct, NULL))
+ {
+ g_iSigPokeThread = s_aiSigCandidates[iSig];
+ break;
+ }
+ AssertMsgFailed(("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno));
+ }
+ }
+ else
+ AssertMsgFailed(("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno));
+ }
+ }
+}
+#endif /* RTTHREAD_POSIX_WITH_POKE */
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ /*
+ * Allocate the TLS (key in posix terms) where we store the pointer to
+ * a threads RTTHREADINT structure.
+ */
+ int rc = pthread_key_create(&g_SelfKey, rtThreadKeyDestruct);
+ if (rc)
+ return VERR_NO_TLS_FOR_SELF;
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+ rtThreadPosixSelectPokeSignal();
+#endif
+
+#ifdef IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+ if (RT_SUCCESS(rc))
+ g_pfnThreadSetName = (PFNPTHREADSETNAME)(uintptr_t)dlsym(RTLD_DEFAULT, "pthread_setname_np");
+#endif
+ return rc;
+}
+
+static void rtThreadPosixBlockSignals(PRTTHREADINT pThread)
+{
+ /*
+ * Mask all signals, including the poke one, if requested.
+ */
+ if ( pThread
+ && (pThread->fFlags & RTTHREADFLAGS_NO_SIGNALS))
+ {
+ sigset_t SigSet;
+ sigfillset(&SigSet);
+ sigdelset(&SigSet, SIGILL); /* On the m1 we end up spinning on UDF ... */
+ sigdelset(&SigSet, SIGTRAP); /* ... and BRK instruction if these signals are masked. */
+ sigdelset(&SigSet, SIGFPE); /* Just adding the rest here to be on the safe side. */
+ sigdelset(&SigSet, SIGBUS);
+ sigdelset(&SigSet, SIGSEGV);
+ int rc = sigprocmask(SIG_BLOCK, &SigSet, NULL);
+ AssertMsg(rc == 0, ("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno)); RT_NOREF(rc);
+ }
+ /*
+ * Block SIGALRM - required for timer-posix.cpp.
+ * This is done to limit harm done by OSes which doesn't do special SIGALRM scheduling.
+ * It will not help much if someone creates threads directly using pthread_create. :/
+ */
+ else if (!RTR3InitIsUnobtrusive())
+ {
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, SIGALRM);
+ sigprocmask(SIG_BLOCK, &SigSet, NULL);
+ }
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+ /*
+ * bird 2020-10-28: Not entirely sure why we do this, but it makes sure the signal works
+ * on the new thread. Probably some pre-NPTL linux reasons.
+ */
+ if (g_iSigPokeThread != -1)
+ {
+# if 1 /* siginterrupt() is typically implemented as two sigaction calls, this should be faster and w/o deprecations: */
+ struct sigaction SigActOld;
+ RT_ZERO(SigActOld);
+
+ struct sigaction SigAct;
+ RT_ZERO(SigAct);
+ SigAct.sa_handler = rtThreadPosixPokeSignal;
+ SigAct.sa_flags = 0; /* no SA_RESTART! */
+ sigfillset(&SigAct.sa_mask);
+
+ int rc = sigaction(g_iSigPokeThread, &SigAct, &SigActOld);
+ AssertMsg(rc == 0, ("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno)); RT_NOREF(rc);
+ AssertMsg(rc || SigActOld.sa_handler == rtThreadPosixPokeSignal, ("%p\n", SigActOld.sa_handler));
+# else
+ siginterrupt(g_iSigPokeThread, 1);
+# endif
+ }
+#endif
+}
+
+DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void)
+{
+#ifdef RTTHREAD_POSIX_WITH_POKE
+ Assert(!RTR3InitIsUnobtrusive());
+ rtThreadPosixSelectPokeSignal();
+#endif
+ rtThreadPosixBlockSignals(NULL);
+}
+
+
+/**
+ * Destructor called when a thread terminates.
+ * @param pvValue The key value. PRTTHREAD in our case.
+ */
+static void rtThreadKeyDestruct(void *pvValue)
+{
+ /*
+ * Deal with alien threads.
+ */
+ PRTTHREADINT pThread = (PRTTHREADINT)pvValue;
+ if (pThread->fIntFlags & RTTHREADINT_FLAGS_ALIEN)
+ {
+ pthread_setspecific(g_SelfKey, pThread);
+ rtThreadTerminate(pThread, 0);
+ pthread_setspecific(g_SelfKey, NULL);
+ }
+}
+
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+/**
+ * Dummy signal handler for the poke signal.
+ *
+ * @param iSignal The signal number.
+ */
+static void rtThreadPosixPokeSignal(int iSignal)
+{
+ Assert(iSignal == g_iSigPokeThread);
+ NOREF(iSignal);
+}
+#endif
+
+
+/**
+ * Adopts a thread, this is called immediately after allocating the
+ * thread structure.
+ *
+ * @param pThread Pointer to the thread structure.
+ */
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ rtThreadPosixBlockSignals(pThread);
+
+ int rc = pthread_setspecific(g_SelfKey, pThread);
+ if (!rc)
+ return VINF_SUCCESS;
+ return VERR_FAILED_TO_SET_SELF_TLS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ if (pThread == (PRTTHREADINT)pthread_getspecific(g_SelfKey))
+ pthread_setspecific(g_SelfKey, NULL);
+}
+
+
+/**
+ * Wrapper which unpacks the params and calls thread function.
+ */
+static void *rtThreadNativeMain(void *pvArgs)
+{
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArgs;
+ pthread_t Self = pthread_self();
+#if !defined(RT_OS_SOLARIS) /* On Solaris sizeof(pthread_t) = 4 and sizeof(NIL_RTNATIVETHREAD) = 8 */
+ Assert((uintptr_t)Self != NIL_RTNATIVETHREAD);
+#endif
+ Assert(Self == (pthread_t)(RTNATIVETHREAD)Self);
+
+#if defined(RT_OS_LINUX)
+ /*
+ * Set the TID.
+ */
+ pThread->tid = syscall(__NR_gettid);
+ ASMMemoryFence();
+#endif
+
+ rtThreadPosixBlockSignals(pThread);
+
+ /*
+ * Set the TLS entry and, if possible, the thread name.
+ */
+ int rc = pthread_setspecific(g_SelfKey, pThread);
+ AssertReleaseMsg(!rc, ("failed to set self TLS. rc=%d thread '%s'\n", rc, pThread->szName));
+
+#ifdef IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+ if (g_pfnThreadSetName)
+# ifdef RT_OS_DARWIN
+ g_pfnThreadSetName(pThread->szName);
+# else
+ g_pfnThreadSetName(Self, pThread->szName);
+# endif
+#endif
+
+ /*
+ * Call common main.
+ */
+ rc = rtThreadMain(pThread, (uintptr_t)Self, &pThread->szName[0]);
+
+ pthread_setspecific(g_SelfKey, NULL);
+ pthread_exit((void *)(intptr_t)rc);
+ return (void *)(intptr_t)rc;
+}
+
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+
+/**
+ * @callback_method_impl{FNRTTHREAD,
+ * Priority proxy thread that services g_hRTThreadPosixPriorityProxyQueue.}
+ */
+static DECLCALLBACK(int) rtThreadPosixPriorityProxyThread(PRTTHREADINT, void *)
+{
+ for (;;)
+ {
+ RTREQQUEUE hReqQueue = g_hRTThreadPosixPriorityProxyQueue;
+ if (hReqQueue != NIL_RTREQQUEUE)
+ RTReqQueueProcess(hReqQueue, RT_INDEFINITE_WAIT);
+ else
+ break;
+
+ int32_t rc = ASMAtomicUoReadS32(&g_rcPriorityProxyThreadStart);
+ if (rc != VINF_SUCCESS && rc != VERR_WRONG_ORDER)
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Just returns a non-success status codes to force the thread to re-evaluate
+ * the global shutdown variable.
+ */
+static DECLCALLBACK(int) rtThreadPosixPriorityProxyStopper(void)
+{
+ return VERR_CANCELLED;
+}
+
+
+/**
+ * An atexit() callback that stops the proxy creation/priority thread.
+ */
+static void rtThreadStopProxyThread(void)
+{
+ /*
+ * Signal to the thread that it's time to shut down.
+ */
+ int32_t rc = ASMAtomicXchgS32(&g_rcPriorityProxyThreadStart, VERR_PROCESS_NOT_FOUND);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Grab the associated handles.
+ */
+ RTTHREAD hThread = g_hRTThreadPosixPriorityProxyThread;
+ RTREQQUEUE hQueue = g_hRTThreadPosixPriorityProxyQueue;
+ g_hRTThreadPosixPriorityProxyQueue = NIL_RTREQQUEUE;
+ g_hRTThreadPosixPriorityProxyThread = NIL_RTTHREAD;
+ ASMCompilerBarrier(); /* paranoia */
+
+ AssertReturnVoid(hThread != NIL_RTTHREAD);
+ AssertReturnVoid(hQueue != NIL_RTREQQUEUE);
+
+ /*
+ * Kick the thread so it gets out of any pending RTReqQueueProcess call ASAP.
+ */
+ rc = RTReqQueueCallEx(hQueue, NULL, 0 /*cMillies*/, RTREQFLAGS_IPRT_STATUS | RTREQFLAGS_NO_WAIT,
+ (PFNRT)rtThreadPosixPriorityProxyStopper, 0);
+
+ /*
+ * Wait for the thread to complete.
+ */
+ rc = RTThreadWait(hThread, RT_SUCCESS(rc) ? RT_MS_1SEC * 5 : 32, NULL);
+ if (RT_SUCCESS(rc))
+ RTReqQueueDestroy(hQueue);
+ /* else: just leak the stuff, we're exitting, so nobody cares... */
+ }
+}
+
+
+/**
+ * Ensure that the proxy priority proxy thread has been started.
+ *
+ * Since we will always start a proxy thread when asked to create a thread,
+ * there is no need for serialization here.
+ *
+ * @retval true if started
+ * @retval false if it failed to start (caller must handle this scenario).
+ */
+DECLHIDDEN(bool) rtThreadPosixPriorityProxyStart(void)
+{
+ /*
+ * Read the result.
+ */
+ int rc = ASMAtomicUoReadS32(&g_rcPriorityProxyThreadStart);
+ if (rc != VERR_TRY_AGAIN)
+ return RT_SUCCESS(rc);
+
+ /* If this triggers then there is a very unexpected race somewhere. It
+ should be harmless though. */
+ AssertReturn(ASMAtomicCmpXchgS32(&g_rcPriorityProxyThreadStart, VERR_WRONG_ORDER, VERR_TRY_AGAIN), false);
+
+ /*
+ * Not yet started, so do that.
+ */
+ rc = RTReqQueueCreate(&g_hRTThreadPosixPriorityProxyQueue);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&g_hRTThreadPosixPriorityProxyThread, rtThreadPosixPriorityProxyThread, NULL, 0 /*cbStack*/,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "RTThrdPP");
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicWriteS32(&g_rcPriorityProxyThreadStart, VINF_SUCCESS);
+
+ atexit(rtThreadStopProxyThread);
+ return true;
+ }
+ RTReqQueueCreate(&g_hRTThreadPosixPriorityProxyQueue);
+ }
+ ASMAtomicWriteS32(&g_rcPriorityProxyThreadStart, rc != VERR_WRONG_ORDER ? rc : VERR_PROCESS_NOT_FOUND);
+ return false;
+}
+
+
+/**
+ * Calls @a pfnFunction from the priority proxy thread.
+ *
+ * Caller must have called rtThreadPosixStartProxy() to check that the priority
+ * proxy thread is running.
+ *
+ * @returns
+ * @param pTargetThread The target thread, NULL if not applicable. This is
+ * so we can skip calls pertaining to the priority
+ * proxy thread itself.
+ * @param pfnFunction The function to call. Must return IPRT status code.
+ * @param cArgs Number of arguments (see also RTReqQueueCall).
+ * @param ... Arguments (see also RTReqQueueCall).
+ */
+DECLHIDDEN(int) rtThreadPosixPriorityProxyCall(PRTTHREADINT pTargetThread, PFNRT pfnFunction, int cArgs, ...)
+{
+ int rc;
+ if ( !pTargetThread
+ || pTargetThread->pfnThread != rtThreadPosixPriorityProxyThread)
+ {
+ va_list va;
+ va_start(va, cArgs);
+ PRTREQ pReq;
+ rc = RTReqQueueCallV(g_hRTThreadPosixPriorityProxyQueue, &pReq, RT_INDEFINITE_WAIT, RTREQFLAGS_IPRT_STATUS,
+ pfnFunction, cArgs, va);
+ va_end(va);
+ RTReqRelease(pReq);
+ }
+ else
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+#endif /* !RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY */
+
+/**
+ * Worker for rtThreadNativeCreate that's either called on the priority proxy
+ * thread or directly on the calling thread depending on the proxy state.
+ */
+static DECLCALLBACK(int) rtThreadNativeInternalCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+ /*
+ * Set the default stack size.
+ */
+ if (!pThread->cbStack)
+ pThread->cbStack = 512*1024;
+
+#ifdef RT_OS_LINUX
+ pThread->tid = -1;
+#endif
+
+ /*
+ * Setup thread attributes.
+ */
+ pthread_attr_t ThreadAttr;
+ int rc = pthread_attr_init(&ThreadAttr);
+ if (!rc)
+ {
+ rc = pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_DETACHED);
+ if (!rc)
+ {
+ rc = pthread_attr_setstacksize(&ThreadAttr, pThread->cbStack);
+ if (!rc)
+ {
+ /*
+ * Create the thread.
+ */
+ pthread_t ThreadId;
+ rc = pthread_create(&ThreadId, &ThreadAttr, rtThreadNativeMain, pThread);
+ if (!rc)
+ {
+ pthread_attr_destroy(&ThreadAttr);
+ *pNativeThread = (uintptr_t)ThreadId;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ pthread_attr_destroy(&ThreadAttr);
+ }
+ return RTErrConvertFromErrno(rc);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+ /*
+ * If we have a priority proxy thread, use it. Make sure to ignore the
+ * staring of the proxy thread itself.
+ */
+ if ( pThread->pfnThread != rtThreadPosixPriorityProxyThread
+ && rtThreadPosixPriorityProxyStart())
+ {
+ PRTREQ pReq;
+ int rc = RTReqQueueCall(g_hRTThreadPosixPriorityProxyQueue, &pReq, RT_INDEFINITE_WAIT,
+ (PFNRT)rtThreadNativeInternalCreate, 2, pThread, pNativeThread);
+ RTReqRelease(pReq);
+ return rc;
+ }
+
+ /*
+ * Fall back on creating it directly without regard to priority proxying.
+ */
+#endif
+ return rtThreadNativeInternalCreate(pThread, pNativeThread);
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ /** @todo import alien threads? */
+#if defined(RT_OS_DARWIN)
+ /* On darwin, there seems to be input checking with pthread_getspecific.
+ So, we must prevent using g_SelfKey before rtThreadNativeInit has run,
+ otherwise we might crash or starting working with total garbage pointer
+ values here (see _os_tsd_get_direct in znu/libsyscall/os/tsd.h).
+
+ Now, since the init value is a "negative" one, we just have to check
+ that it's positive or zero before calling the API. */
+ if (RT_LIKELY((intptr_t)g_SelfKey >= 0))
+ return (PRTTHREADINT)pthread_getspecific(g_SelfKey);
+ return NIL_RTTHREAD;
+#else
+ return (PRTTHREADINT)pthread_getspecific(g_SelfKey);
+#endif
+}
+
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+
+RTDECL(int) RTThreadPoke(RTTHREAD hThread)
+{
+ AssertReturn(hThread != RTThreadSelf(), VERR_INVALID_PARAMETER);
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ AssertReturn(pThread, VERR_INVALID_HANDLE);
+
+ int rc;
+ if (g_iSigPokeThread != -1)
+ {
+ rc = pthread_kill((pthread_t)(uintptr_t)pThread->Core.Key, g_iSigPokeThread);
+ rc = RTErrConvertFromErrno(rc);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ rtThreadRelease(pThread);
+ return rc;
+}
+
+
+RTDECL(int) RTThreadControlPokeSignal(RTTHREAD hThread, bool fEnable)
+{
+ AssertReturn(hThread == RTThreadSelf() && hThread != NIL_RTTHREAD, VERR_INVALID_PARAMETER);
+ int rc;
+ if (g_iSigPokeThread != -1)
+ {
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, g_iSigPokeThread);
+
+ int rc2 = sigprocmask(fEnable ? SIG_UNBLOCK : SIG_BLOCK, &SigSet, NULL);
+ if (rc2 == 0)
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("rc=%Rrc errno=%d (rc2=%d)\n", rc, errno, rc2));
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
+
+#endif
+
+/** @todo move this into platform specific files. */
+RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime)
+{
+#if defined(RT_OS_SOLARIS)
+ struct rusage ts;
+ int rc = getrusage(RUSAGE_LWP, &ts);
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+
+ *pKernelTime = ts.ru_stime.tv_sec * 1000 + ts.ru_stime.tv_usec / 1000;
+ *pUserTime = ts.ru_utime.tv_sec * 1000 + ts.ru_utime.tv_usec / 1000;
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
+ /* on Linux, getrusage(RUSAGE_THREAD, ...) is available since 2.6.26 */
+ struct timespec ts;
+ int rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+
+ *pKernelTime = 0;
+ *pUserTime = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_DARWIN)
+ thread_basic_info ThreadInfo;
+ mach_msg_type_number_t Count = THREAD_BASIC_INFO_COUNT;
+ kern_return_t krc = thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)&ThreadInfo, &Count);
+ AssertReturn(krc == KERN_SUCCESS, RTErrConvertFromDarwinKern(krc));
+
+ *pKernelTime = ThreadInfo.system_time.seconds * 1000 + ThreadInfo.system_time.microseconds / 1000;
+ *pUserTime = ThreadInfo.user_time.seconds * 1000 + ThreadInfo.user_time.microseconds / 1000;
+
+ return VINF_SUCCESS;
+#elif defined(RT_OS_HAIKU)
+ thread_info ThreadInfo;
+ status_t status = get_thread_info(find_thread(NULL), &ThreadInfo);
+ AssertReturn(status == B_OK, RTErrConvertFromErrno(status));
+
+ *pKernelTime = ThreadInfo.kernel_time / 1000;
+ *pUserTime = ThreadInfo.user_time / 1000;
+
+ return VINF_SUCCESS;
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+