From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Runtime/r3/linux/sched-linux.cpp | 707 ++++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 src/VBox/Runtime/r3/linux/sched-linux.cpp (limited to 'src/VBox/Runtime/r3/linux/sched-linux.cpp') diff --git a/src/VBox/Runtime/r3/linux/sched-linux.cpp b/src/VBox/Runtime/r3/linux/sched-linux.cpp new file mode 100644 index 00000000..0b7370e0 --- /dev/null +++ b/src/VBox/Runtime/r3/linux/sched-linux.cpp @@ -0,0 +1,707 @@ +/* $Id: sched-linux.cpp $ */ +/** @file + * IPRT - Scheduling, 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 . + * + * 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 + */ + +/* + * !WARNING! + * + * When talking about lowering and raising priority, we do *NOT* refer to + * the common direction priority values takes on unix systems (lower means + * higher). So, when we raise the priority of a linux thread the nice + * value will decrease, and when we lower the priority the nice value + * will increase. Confusing, right? + * + * !WARNING! + */ + + + +/** @def THREAD_LOGGING + * Be very careful with enabling this, it may cause deadlocks when combined + * with the 'thread' logging prefix. + */ +#ifdef DOXYGEN_RUNNING +# define THREAD_LOGGING +#endif + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_THREAD +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "internal/sched.h" +#include "internal/thread.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** Array scheduler attributes corresponding to each of the thread types. + * @internal */ +typedef struct PROCPRIORITYTYPE +{ + /** For sanity include the array index. */ + RTTHREADTYPE enmType; + /** The thread priority or nice delta - depends on which priority type. */ + int iPriority; +} PROCPRIORITYTYPE; + + +/** + * Configuration of one priority. + * @internal + */ +typedef struct +{ + /** The priority. */ + RTPROCPRIORITY enmPriority; + /** The name of this priority. */ + const char *pszName; + /** The process nice value. */ + int iNice; + /** The delta applied to the iPriority value. */ + int iDelta; + /** Array scheduler attributes corresponding to each of the thread types. */ + const PROCPRIORITYTYPE *paTypes; +} PROCPRIORITY; + + +/** + * Saved priority settings + * @internal + */ +typedef struct +{ + /** Process priority. */ + int iPriority; + /** Process level. */ + struct sched_param SchedParam; + /** Process level. */ + int iPolicy; + /** pthread level. */ + struct sched_param PthreadSchedParam; + /** pthread level. */ + int iPthreadPolicy; +} SAVEDPRIORITY, *PSAVEDPRIORITY; + + +/** + * Priorities for checking by separate thread + * @internal + */ +typedef struct +{ + /** The current thread priority to assume first. */ + int iCurrent; + /** The thread priority to try set afterwards. */ + int iNew; +} VALIDATORPRIORITYPAIR, *PVALIDATORPRIORITYPAIR; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Deltas for a process in which we are not restricted + * to only be lowering the priority. + */ +static const PROCPRIORITYTYPE g_aTypesLinuxFree[RTTHREADTYPE_END] = +{ + { RTTHREADTYPE_INVALID, -999999999 }, + { RTTHREADTYPE_INFREQUENT_POLLER, +3 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 }, + { RTTHREADTYPE_EMULATION, +1 }, + { RTTHREADTYPE_DEFAULT, 0 }, + { RTTHREADTYPE_GUI, 0 }, + { RTTHREADTYPE_MAIN_WORKER, 0 }, + { RTTHREADTYPE_VRDP_IO, -1 }, + { RTTHREADTYPE_DEBUGGER, -1 }, + { RTTHREADTYPE_MSG_PUMP, -2 }, + { RTTHREADTYPE_IO, -3 }, + { RTTHREADTYPE_TIMER, -4 } +}; + +/** + * Deltas for a process in which we are restricted and can only lower the priority. + */ +static const PROCPRIORITYTYPE g_aTypesLinuxRestricted[RTTHREADTYPE_END] = +{ + { RTTHREADTYPE_INVALID, -999999999 }, + { RTTHREADTYPE_INFREQUENT_POLLER, +3 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 }, + { RTTHREADTYPE_EMULATION, +1 }, + { RTTHREADTYPE_DEFAULT, 0 }, + { RTTHREADTYPE_GUI, 0 }, + { RTTHREADTYPE_MAIN_WORKER, 0 }, + { RTTHREADTYPE_VRDP_IO, 0 }, + { RTTHREADTYPE_DEBUGGER, 0 }, + { RTTHREADTYPE_MSG_PUMP, 0 }, + { RTTHREADTYPE_IO, 0 }, + { RTTHREADTYPE_TIMER, 0 } +}; + +/** + * All threads have the same priority. + * + * This is typically chosen when we find that we can't raise the priority + * to the process default of a thread created by a low priority thread. + */ +static const PROCPRIORITYTYPE g_aTypesLinuxFlat[RTTHREADTYPE_END] = +{ + { RTTHREADTYPE_INVALID, -999999999 }, + { RTTHREADTYPE_INFREQUENT_POLLER, 0 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, 0 }, + { RTTHREADTYPE_EMULATION, 0 }, + { RTTHREADTYPE_DEFAULT, 0 }, + { RTTHREADTYPE_GUI, 0 }, + { RTTHREADTYPE_MAIN_WORKER, 0 }, + { RTTHREADTYPE_VRDP_IO, 0 }, + { RTTHREADTYPE_DEBUGGER, 0 }, + { RTTHREADTYPE_MSG_PUMP, 0 }, + { RTTHREADTYPE_IO, 0 }, + { RTTHREADTYPE_TIMER, 0 } +}; + +/** + * Process and thread level priority, full access at thread level. + */ +static const PROCPRIORITY g_aUnixConfigs[] = +{ + { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxFree }, + { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxFree }, + { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxFree }, + { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_LOW, "Low", 19, 19, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxFree }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxFree }, + { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxFree }, + { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxFree }, + { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxFree }, + { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxFree }, + { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxRestricted }, + { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxFlat }, + { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxFlat } +}; + +/** + * The dynamic default priority configuration. + * + * This will be recalulated at runtime depending on what the + * system allow us to do and what the current priority is. + */ +static PROCPRIORITY g_aDefaultPriority = +{ + RTPROCPRIORITY_LOW, "Default", 0, 0, g_aTypesLinuxRestricted +}; + +/** Pointer to the current priority configuration. */ +static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority; + +/** Set if we can raise the priority of a thread beyond the default. + * + * It might mean we have the CAP_SYS_NICE capability or that the + * process's RLIMIT_NICE is higher than the priority of the thread + * calculating the defaults. + */ +static bool g_fCanRaisePriority = false; + +/** Set if we can restore the priority after having temporarily lowered or raised it. */ +static bool g_fCanRestorePriority = false; + +/** Set if we can NOT raise the priority to the process default in a thread + * created by a thread running below the process default. + */ +static bool g_fScrewedUpMaxPriorityLimitInheritance = true; + +/** The highest priority we can set. */ +static int g_iMaxPriority = 0; + +/** The lower priority we can set. */ +static int g_iMinPriority = 19; + +/** Set when we've successfully determined the capabilities of the process and kernel. */ +static bool g_fInitialized = false; + + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + + +/** + * Saves all the scheduling attributes we can think of. + */ +static void rtSchedNativeSave(PSAVEDPRIORITY pSave) +{ + memset(pSave, 0xff, sizeof(*pSave)); + + errno = 0; + pSave->iPriority = getpriority(PRIO_PROCESS, 0 /* current process */); + Assert(errno == 0); + + errno = 0; + sched_getparam(0 /* current process */, &pSave->SchedParam); + Assert(errno == 0); + + errno = 0; + pSave->iPolicy = sched_getscheduler(0 /* current process */); + Assert(errno == 0); + + int rc = pthread_getschedparam(pthread_self(), &pSave->iPthreadPolicy, &pSave->PthreadSchedParam); + Assert(rc == 0); NOREF(rc); +} + + +/** + * Restores scheduling attributes. + * Most of this won't work right, but anyway... + */ +static void rtSchedNativeRestore(PSAVEDPRIORITY pSave) +{ + setpriority(PRIO_PROCESS, 0, pSave->iPriority); + sched_setscheduler(0, pSave->iPolicy, &pSave->SchedParam); + sched_setparam(0, &pSave->SchedParam); + pthread_setschedparam(pthread_self(), pSave->iPthreadPolicy, &pSave->PthreadSchedParam); +} + + +/** + * Called on the priority proxy thread if requested running, otherwise + * rtSchedRunThread() calls it directly. + */ +static DECLCALLBACK(int) rtSchedRunThreadCallback(pthread_t *pThread, void *(*pfnThread)(void *pvArg), void *pvArg) +{ + int rc = pthread_create(pThread, NULL, pfnThread, pvArg); + if (!rc) + return VINF_SUCCESS; + return RTErrConvertFromErrno(rc); +} + + +/** + * Starts a worker thread and wait for it to complete. + * + * We cannot use RTThreadCreate since we're already owner of the RW lock. + */ +static int rtSchedRunThread(void *(*pfnThread)(void *pvArg), void *pvArg, bool fUsePriorityProxy) +{ + /* + * Create the thread. + */ + pthread_t Thread; + int rc; +#ifndef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY + RT_NOREF(fUsePriorityProxy); +#else + if ( fUsePriorityProxy + && rtThreadPosixPriorityProxyStart()) + rc = rtThreadPosixPriorityProxyCall(NULL, (PFNRT)rtSchedRunThreadCallback, 3, &Thread, pfnThread, pvArg); + else +#endif + rc = rtSchedRunThreadCallback(&Thread, pfnThread, pvArg); + if (RT_SUCCESS(rc)) + { + /* + * Wait for the thread to finish. + */ + void *pvRet = (void *)-1; + do + { + rc = pthread_join(Thread, &pvRet); + } while (rc == EINTR); + if (rc) + return RTErrConvertFromErrno(rc); + return (int)(uintptr_t)pvRet; + } + return rc; +} + + +static void rtSchedDumpPriority(void) +{ +#ifdef THREAD_LOGGING + Log(("Priority: g_fCanRaisePriority=%RTbool g_fCanRestorePriority=%RTbool g_fScrewedUpMaxPriorityLimitInheritance=%RTbool\n", + g_fCanRaisePriority, g_fCanRestorePriority, g_fScrewedUpMaxPriorityLimitInheritance)); + Log(("Priority: g_iMaxPriority=%d g_iMinPriority=%d\n", g_iMaxPriority, g_iMinPriority)); + Log(("Priority: enmPriority=%d \"%s\" iNice=%d iDelta=%d\n", + g_pProcessPriority->enmPriority, + g_pProcessPriority->pszName, + g_pProcessPriority->iNice, + g_pProcessPriority->iDelta)); + Log(("Priority: %2d INFREQUENT_POLLER = %d\n", RTTHREADTYPE_INFREQUENT_POLLER, g_pProcessPriority->paTypes[RTTHREADTYPE_INFREQUENT_POLLER].iPriority)); + Log(("Priority: %2d MAIN_HEAVY_WORKER = %d\n", RTTHREADTYPE_MAIN_HEAVY_WORKER, g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_HEAVY_WORKER].iPriority)); + Log(("Priority: %2d EMULATION = %d\n", RTTHREADTYPE_EMULATION , g_pProcessPriority->paTypes[RTTHREADTYPE_EMULATION ].iPriority)); + Log(("Priority: %2d DEFAULT = %d\n", RTTHREADTYPE_DEFAULT , g_pProcessPriority->paTypes[RTTHREADTYPE_DEFAULT ].iPriority)); + Log(("Priority: %2d GUI = %d\n", RTTHREADTYPE_GUI , g_pProcessPriority->paTypes[RTTHREADTYPE_GUI ].iPriority)); + Log(("Priority: %2d MAIN_WORKER = %d\n", RTTHREADTYPE_MAIN_WORKER , g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_WORKER ].iPriority)); + Log(("Priority: %2d VRDP_IO = %d\n", RTTHREADTYPE_VRDP_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_VRDP_IO ].iPriority)); + Log(("Priority: %2d DEBUGGER = %d\n", RTTHREADTYPE_DEBUGGER , g_pProcessPriority->paTypes[RTTHREADTYPE_DEBUGGER ].iPriority)); + Log(("Priority: %2d MSG_PUMP = %d\n", RTTHREADTYPE_MSG_PUMP , g_pProcessPriority->paTypes[RTTHREADTYPE_MSG_PUMP ].iPriority)); + Log(("Priority: %2d IO = %d\n", RTTHREADTYPE_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_IO ].iPriority)); + Log(("Priority: %2d TIMER = %d\n", RTTHREADTYPE_TIMER , g_pProcessPriority->paTypes[RTTHREADTYPE_TIMER ].iPriority)); +#endif +} + + +/** + * This just checks if it can raise the priority after having been + * created by a thread with a low priority. + * + * @returns zero on success, non-zero on failure. + * @param pvUser The priority of the parent before it was lowered (cast to int). + */ +static void *rtSchedNativeSubProberThread(void *pvUser) +{ + int iPriority = getpriority(PRIO_PROCESS, 0); + Assert(iPriority == g_iMinPriority); + + if (setpriority(PRIO_PROCESS, 0, iPriority + 1)) + return (void *)-1; + if (setpriority(PRIO_PROCESS, 0, (int)(intptr_t)pvUser)) + return (void *)-1; + return (void *)0; +} + + +/** + * The prober thread. + * We don't want to mess with the priority of the calling thread. + * + * @remark This is pretty presumptive stuff, but if it works on Linux and + * FreeBSD it does what I want. + */ +static void *rtSchedNativeProberThread(void *pvUser) +{ + NOREF(pvUser); + SAVEDPRIORITY SavedPriority; + rtSchedNativeSave(&SavedPriority); + + /* + * Check if we can get higher priority (typically only root can do this). + * (Won't work right if our priority is -19 to start with, but what the heck.) + * + * We assume that the priority range is -19 to 19. Should probably find the right + * define for this. + */ + int iStart = getpriority(PRIO_PROCESS, 0); + int i = iStart; + while (i-- > -20) + if (setpriority(PRIO_PROCESS, 0, i)) + break; + g_iMaxPriority = getpriority(PRIO_PROCESS, 0); + g_fCanRaisePriority = g_iMaxPriority < iStart; + g_fCanRestorePriority = setpriority(PRIO_PROCESS, 0, iStart) == 0; + + /* + * Check if we temporarily lower the thread priority. + * Again, we assume we're not at the extreme end of the priority scale. + */ + iStart = getpriority(PRIO_PROCESS, 0); + i = iStart; + while (i++ < 19) + if (setpriority(PRIO_PROCESS, 0, i)) + break; + g_iMinPriority = getpriority(PRIO_PROCESS, 0); + if ( setpriority(PRIO_PROCESS, 0, iStart) + || getpriority(PRIO_PROCESS, 0) != iStart) + g_fCanRestorePriority = false; + if (g_iMinPriority == g_iMaxPriority) + g_fCanRestorePriority = g_fCanRaisePriority = false; + + /* + * Check what happens to child threads when the parent lowers the + * priority when it's being created. + */ + iStart = getpriority(PRIO_PROCESS, 0); + g_fScrewedUpMaxPriorityLimitInheritance = true; + if ( g_fCanRestorePriority + && !setpriority(PRIO_PROCESS, 0, g_iMinPriority) + && iStart != g_iMinPriority) + { + if (rtSchedRunThread(rtSchedNativeSubProberThread, (void *)(intptr_t)iStart, false /*fUsePriorityProxy*/) == 0) + g_fScrewedUpMaxPriorityLimitInheritance = false; + } + + /* done */ + rtSchedNativeRestore(&SavedPriority); + return (void *)VINF_SUCCESS; +} + + +/** + * Calculate the scheduling properties for all the threads in the default + * process priority, assuming the current thread have the type enmType. + * + * @returns iprt status code. + * @param enmType The thread type to be assumed for the current thread. + */ +DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType) +{ + Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); + + /* + * First figure out what's we're allowed to do in this process. + */ + if (!g_fInitialized) + { + int iPriority = getpriority(PRIO_PROCESS, 0); +#ifdef RLIMIT_RTPRIO + /** @todo */ +#endif + int rc = rtSchedRunThread(rtSchedNativeProberThread, NULL, false /*fUsePriorityProxy*/); + if (RT_FAILURE(rc)) + return rc; + Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority); + g_fInitialized = true; + } + + /* + * Select the right priority type table and update the default + * process priority structure. + */ + if (g_fCanRaisePriority && g_fCanRestorePriority && !g_fScrewedUpMaxPriorityLimitInheritance) + g_aDefaultPriority.paTypes = &g_aTypesLinuxFree[0]; + else if (!g_fCanRaisePriority && g_fCanRestorePriority && !g_fScrewedUpMaxPriorityLimitInheritance) + g_aDefaultPriority.paTypes = &g_aTypesLinuxRestricted[0]; + else + g_aDefaultPriority.paTypes = &g_aTypesLinuxFlat[0]; + Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType); + + int iPriority = getpriority(PRIO_PROCESS, 0 /* current process */); + g_aDefaultPriority.iNice = iPriority - g_aDefaultPriority.paTypes[enmType].iPriority; + g_aDefaultPriority.iDelta = g_aDefaultPriority.iNice; + + rtSchedDumpPriority(); + return VINF_SUCCESS; +} + + +/** + * The process priority validator thread. + * (We don't want to mess with the priority of the calling thread.) + */ +static void *rtSchedNativeValidatorThread(void *pvUser) +{ + PVALIDATORPRIORITYPAIR pPrioPair = (PVALIDATORPRIORITYPAIR)pvUser; + SAVEDPRIORITY SavedPriority; + rtSchedNativeSave(&SavedPriority); + + int rc = VINF_SUCCESS; + + /* + * Set the priority to the current value for specified thread type, but + * only if we have any threads of this type (caller checked - INT_MAX). + */ + if (pPrioPair->iCurrent != INT_MAX) + if (setpriority(PRIO_PROCESS, 0, pPrioPair->iCurrent)) + rc = RTErrConvertFromErrno(errno); + + /* + * Try set the new priority. + */ + if (RT_SUCCESS(rc) && setpriority(PRIO_PROCESS, 0, pPrioPair->iNew)) + rc = RTErrConvertFromErrno(errno); + + /* done */ + rtSchedNativeRestore(&SavedPriority); + return (void *)(intptr_t)rc; +} + + +/** + * Validates the ability to apply suggested priority scheme. + * + * The function checks that we're able to apply all the thread types in the + * suggested priority scheme. + * + * @returns iprt status code. + * @param pCfg The priority scheme to validate. + * @param fHavePriorityProxy Set if we've got a priority proxy thread, + * otherwise clear. + */ +static int rtSchedNativeCheckThreadTypes(const PROCPRIORITY *pCfg, bool fHavePriorityProxy) +{ + int i = RTTHREADTYPE_END; + while (--i > RTTHREADTYPE_INVALID) + { + VALIDATORPRIORITYPAIR PrioPair; + PrioPair.iCurrent = g_pProcessPriority->paTypes[i].iPriority + g_pProcessPriority->iDelta; + PrioPair.iNew = pCfg->paTypes[i].iPriority + pCfg->iDelta; + if (g_acRTThreadTypeStats[i] == 0) + PrioPair.iCurrent = INT_MAX; + +#ifdef RT_STRICT + int const iPriority = getpriority(PRIO_PROCESS, 0); +#endif + int rc = rtSchedRunThread(rtSchedNativeValidatorThread, &PrioPair, fHavePriorityProxy /*fUsePriorityProxy*/); + Assert(getpriority(PRIO_PROCESS, 0) == iPriority); + + if (RT_FAILURE(rc)) + return rc; + } + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority) +{ + Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST); + +#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY + /* + * Make sure the proxy creation thread is started so we don't 'lose' our + * initial priority if it's lowered. + */ + bool const fHavePriorityProxy = rtThreadPosixPriorityProxyStart(); +#else + bool const fHavePriorityProxy = false; +#endif + + int rc; + if (enmPriority == RTPROCPRIORITY_DEFAULT) + { + /* + * If we've lowered priority since the process started, it may be impossible + * to raise it again for existing thread (new threads will work fine). + */ + rc = rtSchedNativeCheckThreadTypes(&g_aDefaultPriority, fHavePriorityProxy); + if (RT_SUCCESS(rc)) + g_pProcessPriority = &g_aDefaultPriority; + } + else + { + /* + * Find a configuration which matches and can be applied. + */ + rc = VERR_NOT_FOUND; + for (unsigned i = 0; i < RT_ELEMENTS(g_aUnixConfigs); i++) + if (g_aUnixConfigs[i].enmPriority == enmPriority) + { + int rc2 = rtSchedNativeCheckThreadTypes(&g_aUnixConfigs[i], fHavePriorityProxy); + if (RT_SUCCESS(rc2)) + { + g_pProcessPriority = &g_aUnixConfigs[i]; + rc = VINF_SUCCESS; + break; + } + if (rc == VERR_NOT_FOUND || rc == VERR_ACCESS_DENIED) + rc = rc2; + } + } + +#ifdef THREAD_LOGGING + LogFlow(("rtProcNativeSetPriority: returns %Rrc enmPriority=%d\n", rc, enmPriority)); + rtSchedDumpPriority(); +#endif + return rc; +} + + +/** + * Called on the priority proxy thread if it's running, otherwise + * rtThreadNativeSetPriority calls it directly. + */ +static DECLCALLBACK(int) rtThreadLinuxSetPriorityCallback(PRTTHREADINT pThread, int iPriority) +{ + if (!setpriority(PRIO_PROCESS, pThread->tid, iPriority)) + { + AssertMsg(iPriority == getpriority(PRIO_PROCESS, pThread->tid), + ("iPriority=%d getpriority()=%d\n", iPriority, getpriority(PRIO_PROCESS, pThread->tid))); +#ifdef THREAD_LOGGING + Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPriority=%d pid=%d tid=%d\n", + pThread->Core.Key, enmType, iPriority, getpid(), pThread->tid)); +#endif + return VINF_SUCCESS; + } + AssertMsgFailed(("setpriority(,, %d) -> errno=%d rc=%Rrc\n", iPriority, errno, RTErrConvertFromErrno(errno))); + return VINF_SUCCESS; //non-fatal for now. +} + + +DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType) +{ + /* sanity */ + Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); + Assert(enmType == g_pProcessPriority->paTypes[enmType].enmType); + + /* + * The thread ID is zero for alien threads, so skip these or we'd risk + * modifying our own priority. + */ + if (!pThread->tid) + return VINF_SUCCESS; + + /* + * Calculate the thread priority and apply it, preferrably via the priority proxy thread. + */ + int const iPriority = g_pProcessPriority->paTypes[enmType].iPriority + g_pProcessPriority->iDelta; +#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY + if (rtThreadPosixPriorityProxyStart()) + return rtThreadPosixPriorityProxyCall(pThread, (PFNRT)rtThreadLinuxSetPriorityCallback, 2, pThread, iPriority); +#endif + return rtThreadLinuxSetPriorityCallback(pThread, iPriority); +} + -- cgit v1.2.3