/* $Id: thread-posix.cpp 90392 2021-07-29 08:25:50Z vboxsync $ */ /** @file * IPRT - Threads, POSIX. */ /* * Copyright (C) 2006-2020 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 #include #include #include #if defined(RT_OS_LINUX) # include # include #endif #if defined(RT_OS_SOLARIS) # include # include #endif #if defined(RT_OS_DARWIN) # include # include # include # include # include #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 #endif #if defined(RT_OS_HAIKU) # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #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. */ static pthread_key_t g_SelfKey; #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); 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) { PRTTHREADINT pThread = (PRTTHREADINT)pthread_getspecific(g_SelfKey); /** @todo import alien threads? */ return pThread; } #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; } #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 }