diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Runtime/r3/posix/sched-posix.cpp | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/posix/sched-posix.cpp b/src/VBox/Runtime/r3/posix/sched-posix.cpp new file mode 100644 index 00000000..343d6d6e --- /dev/null +++ b/src/VBox/Runtime/r3/posix/sched-posix.cpp @@ -0,0 +1,826 @@ +/* $Id: sched-posix.cpp $ */ +/** @file + * IPRT - Scheduling, POSIX. + */ + +/* + * 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. + */ + +/* + * !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 <errno.h> +#include <pthread.h> +#include <sched.h> +#include <unistd.h> +#include <sys/resource.h> + +#include <iprt/thread.h> +#include <iprt/process.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/err.h> +#include "internal/sched.h" +#include "internal/thread.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** Array scheduler attributes corresponding to each of the thread types. */ +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. + */ +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 + */ +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; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Thread level priorities based on a 0..31 priority range + * as specified as the minimum for SCHED_RR/FIFO. FreeBSD + * seems to be using this (needs more research to be + * certain). + */ +static const PROCPRIORITYTYPE g_aTypesThread[RTTHREADTYPE_END] = +{ + { RTTHREADTYPE_INVALID, -999999999 }, + { RTTHREADTYPE_INFREQUENT_POLLER, 5 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, 12 }, + { RTTHREADTYPE_EMULATION, 14 }, + { RTTHREADTYPE_DEFAULT, 15 }, + { RTTHREADTYPE_GUI, 16 }, + { RTTHREADTYPE_MAIN_WORKER, 18 }, + { RTTHREADTYPE_VRDP_IO, 24 }, + { RTTHREADTYPE_DEBUGGER, 28 }, + { RTTHREADTYPE_MSG_PUMP, 29 }, + { RTTHREADTYPE_IO, 30 }, + { RTTHREADTYPE_TIMER, 31 } +}; + +static const PROCPRIORITYTYPE g_aTypesThreadFlat[RTTHREADTYPE_END] = +{ + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, 15 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, 15 }, + { RTTHREADTYPE_EMULATION, 15 }, + { RTTHREADTYPE_DEFAULT, 15 }, + { RTTHREADTYPE_GUI, 15 }, + { RTTHREADTYPE_MAIN_WORKER, 15 }, + { RTTHREADTYPE_VRDP_IO, 15 }, + { RTTHREADTYPE_DEBUGGER, 15 }, + { RTTHREADTYPE_MSG_PUMP, 15 }, + { RTTHREADTYPE_IO, 15 }, + { RTTHREADTYPE_TIMER, 15 } +}; + +/** + * Process and thread level priority, full access at thread level. + */ +static const PROCPRIORITY g_aProcessAndThread[] = +{ + { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesThreadFlat }, + { RTPROCPRIORITY_LOW, "Low", 9, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 11, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 15, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 17, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 19, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 7, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 5, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 3, 0, g_aTypesThread }, + { RTPROCPRIORITY_LOW, "Low", 1, 0, g_aTypesThread }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesThread }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesThreadFlat }, + { RTPROCPRIORITY_HIGH, "High", -9, 0, g_aTypesThread }, + { RTPROCPRIORITY_HIGH, "High", -7, 0, g_aTypesThread }, + { RTPROCPRIORITY_HIGH, "High", -5, 0, g_aTypesThread }, + { RTPROCPRIORITY_HIGH, "High", -3, 0, g_aTypesThread }, + { RTPROCPRIORITY_HIGH, "High", -1, 0, g_aTypesThread }, + { RTPROCPRIORITY_HIGH, "High", -9, 0, g_aTypesThreadFlat }, + { RTPROCPRIORITY_HIGH, "High", -1, 0, g_aTypesThreadFlat } +}; + +/** + * Deltas for a process in which we are not restricted + * to only be lowering the priority. + */ +static const PROCPRIORITYTYPE g_aTypesUnixFree[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 + * to only be lowering the priority. + */ +static const PROCPRIORITYTYPE g_aTypesUnixRestricted[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 } +}; + +/** + * Deltas for a process in which we are restricted + * to only be lowering the priority. + */ +static const PROCPRIORITYTYPE g_aTypesUnixFlat[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_aTypesUnixFlat }, + { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixFree }, + { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixFlat }, + { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixFree }, + { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixFlat }, + { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixFree }, + { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixFlat }, + { RTPROCPRIORITY_LOW, "Low", 19, 19, g_aTypesUnixFlat }, + { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixFree }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixFlat }, + { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixFree }, + { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixFree }, + { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixFree }, + { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixFree }, + { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixFree }, + { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixRestricted }, + { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixFlat }, + { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixFlat }, + { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixFlat }, + { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixFlat }, + { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixFlat } +}; + +/** + * 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_aTypesUnixRestricted +}; + +/** Pointer to the current priority configuration. */ +static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority; + + +/** Set to what kind of scheduling priority support the host + * OS seems to be offering. Determined at runtime. + */ +static enum +{ + OSPRIOSUP_UNDETERMINED = 0, + /** An excellent combination of process and thread level + * I.e. setpriority() works on process level, one have to be supervisor + * to raise priority as is the custom in unix. While pthread_setschedparam() + * works on thread level and we can raise the priority just like we want. + * + * I think this is what FreeBSD offers. (It is certainly analogous to what + * NT offers if you wondered.) Linux on the other hand doesn't provide this + * for processes with SCHED_OTHER policy, and I'm not sure if we want to + * play around with using the real-time SCHED_RR and SCHED_FIFO which would + * require special privileges anyway. + */ + OSPRIOSUP_PROCESS_AND_THREAD_LEVEL, + /** A rough thread level priority only. + * setpriority() is the only real game in town, and it works on thread level. + */ + OSPRIOSUP_THREAD_LEVEL +} volatile g_enmOsPrioSup = OSPRIOSUP_UNDETERMINED; + +/** Set if we figure we have nice capability, meaning we can use setpriority + * to raise the priority. */ +bool g_fCanNice = 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); +} + + +/** + * 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 rtSchedCreateThread(void *(*pfnThread)(void *pvArg), void *pvArg) +{ + /* + * Setup thread attributes. + */ + pthread_attr_t ThreadAttr; + int rc = pthread_attr_init(&ThreadAttr); + if (!rc) + { + rc = pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_JOINABLE); + if (!rc) + { + rc = pthread_attr_setstacksize(&ThreadAttr, 128*1024); + if (!rc) + { + /* + * Create the thread. + */ + pthread_t Thread; + rc = pthread_create(&Thread, &ThreadAttr, pfnThread, pvArg); + if (!rc) + { + pthread_attr_destroy(&ThreadAttr); + /* + * 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; + } + } + } + pthread_attr_destroy(&ThreadAttr); + } + return RTErrConvertFromErrno(rc); +} + + +static void rtSchedDumpPriority(void) +{ +#ifdef THREAD_LOGGING + Log(("Priority: g_fCanNice=%d g_enmOsPrioSup=%d\n", g_fCanNice, g_enmOsPrioSup)); + 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 +} + + +/** + * 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) +{ + SAVEDPRIORITY SavedPriority; + rtSchedNativeSave(&SavedPriority); + + /* + * Let's first try and see what we get on a thread level. + */ + int iMax = sched_get_priority_max(SavedPriority.iPthreadPolicy); + int iMin = sched_get_priority_min(SavedPriority.iPthreadPolicy); + if (iMax - iMin >= 32) + { + pthread_t Self = pthread_self(); + int i = iMin; + while (i <= iMax) + { + struct sched_param SchedParam = SavedPriority.PthreadSchedParam; + SchedParam.sched_priority = i; + if (pthread_setschedparam(Self, SavedPriority.iPthreadPolicy, &SchedParam)) + break; + i++; + } + if (i == iMax) + g_enmOsPrioSup = OSPRIOSUP_PROCESS_AND_THREAD_LEVEL; + } + + /* + * Ok, we didn't have the good stuff, so let's fall back on the unix stuff. + */ + if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED) + g_enmOsPrioSup = OSPRIOSUP_THREAD_LEVEL; + + /* + * 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 unix priority is -19 to 19. I know there are defines + * for this, but I don't remember which and if I'm awake enough to make sense + * of them from any SuS spec. + */ + int iStart = getpriority(PRIO_PROCESS, 0); + int i = iStart; + while (i-- > -19) + { + if (setpriority(PRIO_PROCESS, 0, i)) + break; + } + if (getpriority(PRIO_PROCESS, 0) != iStart) + g_fCanNice = true; + else + g_fCanNice = 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 supported by the OS. + */ + if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED) + { + int iPriority = getpriority(PRIO_PROCESS, 0); + int rc = rtSchedCreateThread(rtSchedNativeProberThread, NULL); + if (RT_FAILURE(rc)) + return rc; + if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED) + g_enmOsPrioSup = OSPRIOSUP_THREAD_LEVEL; + Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority); + } + + /* + * Now let's see what we can do... + */ + int iPriority = getpriority(PRIO_PROCESS, 0); + switch (g_enmOsPrioSup) + { + case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL: + { + g_aDefaultPriority.iNice = iPriority; + g_aDefaultPriority.iDelta = 0; + g_aDefaultPriority.paTypes = g_aTypesThread; + Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType); + break; + } + + case OSPRIOSUP_THREAD_LEVEL: + { + if (g_fCanNice) + g_aDefaultPriority.paTypes = g_aTypesUnixFree; + else + g_aDefaultPriority.paTypes = g_aTypesUnixRestricted; + Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType); + g_aDefaultPriority.iNice = iPriority - g_aDefaultPriority.paTypes[enmType].iPriority; + g_aDefaultPriority.iDelta = g_aDefaultPriority.iNice; + break; + } + + default: + AssertFailed(); + break; + } + rtSchedDumpPriority(); + return VINF_SUCCESS; +} + + +/** + * The validator 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 *rtSchedNativeValidatorThread(void *pvUser) +{ + const PROCPRIORITY *pCfg = (const PROCPRIORITY *)pvUser; + SAVEDPRIORITY SavedPriority; + rtSchedNativeSave(&SavedPriority); + + int rc = VINF_SUCCESS; + switch (g_enmOsPrioSup) + { + /* + * Try set the specified process priority and then try + * out all the thread priorities which are used. + */ + case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL: + { + if (!setpriority(PRIO_PROCESS, 0, pCfg->iNice)) + { + int iMin = sched_get_priority_min(SavedPriority.iPolicy); + pthread_t Self = pthread_self(); + for (int i = RTTHREADTYPE_INVALID + 1; i < RTTHREADTYPE_END; i++) + { + struct sched_param SchedParam = SavedPriority.PthreadSchedParam; + SchedParam.sched_priority = pCfg->paTypes[i].iPriority + + pCfg->iDelta + iMin; + rc = pthread_setschedparam(Self, SavedPriority.iPthreadPolicy, &SchedParam); + if (rc) + { + rc = RTErrConvertFromErrno(rc); + break; + } + } + } + else + rc = RTErrConvertFromErrno(errno); + break; + } + + /* + * Try out the priorities from the top and down. + */ + case OSPRIOSUP_THREAD_LEVEL: + { + int i = RTTHREADTYPE_END; + while (--i > RTTHREADTYPE_INVALID) + { + int iPriority = pCfg->paTypes[i].iPriority + pCfg->iDelta; + if (setpriority(PRIO_PROCESS, 0, iPriority)) + { + rc = RTErrConvertFromErrno(errno); + break; + } + } + break; + } + + default: + AssertFailed(); + break; + } + + /* done */ + rtSchedNativeRestore(&SavedPriority); + return (void *)rc; +} + + +/** + * Validates and sets the process priority. + * This will check that all rtThreadNativeSetPriority() will success for all the + * thread types when applied to the current thread. + * + * @returns iprt status code. + * @param enmPriority The priority to validate and set. + */ +DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority) +{ + Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST); + + int rc = VINF_SUCCESS; + if (enmPriority == RTPROCPRIORITY_DEFAULT) + g_pProcessPriority = &g_aDefaultPriority; + else + { + /* + * Select the array to search. + */ + const PROCPRIORITY *pa; + unsigned c; + switch (g_enmOsPrioSup) + { + case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL: + pa = g_aProcessAndThread; + c = RT_ELEMENTS(g_aProcessAndThread); + break; + case OSPRIOSUP_THREAD_LEVEL: + pa = g_aUnixConfigs; + c = RT_ELEMENTS(g_aUnixConfigs); + break; + default: + pa = NULL; + c = 0; + break; + } + + /* + * Search the array. + */ + rc = VERR_FILE_NOT_FOUND; + unsigned i; + for (i = 0; i < c; i++) + { + if (pa[i].enmPriority == enmPriority) + { + /* + * Validate it. + */ + int iPriority = getpriority(PRIO_PROCESS, 0); + int rc3 = rtSchedCreateThread(rtSchedNativeValidatorThread, (void *)&pa[i]); + Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority); + if (RT_SUCCESS(rc)) + rc = rc3; + if (RT_SUCCESS(rc)) + break; + } + } + + /* + * Did we get lucky? + * If so update process priority and globals. + */ + if (RT_SUCCESS(rc)) + { + switch (g_enmOsPrioSup) + { + case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL: + if (setpriority(PRIO_PROCESS, 0, pa[i].iNice)) + { + rc = RTErrConvertFromErrno(errno); + AssertMsgFailed(("setpriority(,,%d) -> errno=%d rc=%Rrc\n", pa[i].iNice, errno, rc)); + } + break; + + default: + break; + } + + if (RT_SUCCESS(rc)) + g_pProcessPriority = &pa[i]; + } + } + +#ifdef THREAD_LOGGING + LogFlow(("rtProcNativeSetPriority: returns %Rrc enmPriority=%d\n", rc, enmPriority)); + rtSchedDumpPriority(); +#endif + return rc; +} + + +/** + * Sets the priority of the thread according to the thread type + * and current process priority. + * + * The RTTHREADINT::enmType member has not yet been updated and will be updated by + * the caller on a successful return. + * + * @returns iprt status code. + * @param Thread The thread in question. + * @param enmType The thread type. + */ +DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType) +{ + Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); + Assert(enmType == g_pProcessPriority->paTypes[enmType].enmType); + Assert((pthread_t)pThread->Core.Key == pthread_self()); + + int rc = VINF_SUCCESS; + switch (g_enmOsPrioSup) + { + case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL: + { + struct sched_param SchedParam = {-9999999}; + int iPolicy = -7777777; + pthread_t Self = pthread_self(); + rc = pthread_getschedparam(Self, &iPolicy, &SchedParam); + if (!rc) + { + SchedParam.sched_priority = g_pProcessPriority->paTypes[enmType].iPriority + + g_pProcessPriority->iDelta + + sched_get_priority_min(iPolicy); + rc = pthread_setschedparam(Self, iPolicy, &SchedParam); + if (!rc) + { +#ifdef THREAD_LOGGING + Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPolicy=%d sched_priority=%d pid=%d\n", + pThread->Core.Key, enmType, iPolicy, SchedParam.sched_priority, getpid())); +#endif + break; + } + } + + int rcNative = rc; + rc = RTErrConvertFromErrno(rc); + AssertMsgFailed(("pthread_[gs]etschedparam(%p, %d, {%d}) -> rcNative=%d rc=%Rrc\n", + (void *)Self, iPolicy, SchedParam.sched_priority, rcNative, rc)); NOREF(rcNative); + break; + } + + case OSPRIOSUP_THREAD_LEVEL: + { + int iPriority = g_pProcessPriority->paTypes[enmType].iPriority + g_pProcessPriority->iDelta; + if (!setpriority(PRIO_PROCESS, 0, iPriority)) + { + AssertMsg(iPriority == getpriority(PRIO_PROCESS, 0), ("iPriority=%d getpriority()=%d\n", iPriority, getpriority(PRIO_PROCESS, 0))); +#ifdef THREAD_LOGGING + Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPriority=%d pid=%d\n", pThread->Core.Key, enmType, iPriority, getpid())); +#endif + } + else + { +#if 0 + rc = RTErrConvertFromErrno(errno); + AssertMsgFailed(("setpriority(,, %d) -> errno=%d rc=%Rrc\n", iPriority, errno, rc)); +#else + /** @todo + * Just keep quiet about failures now - we'll fail here because we're not + * allowed to raise our own priority. This is a problem when starting the + * threads with higher priority from EMT (i.e. most threads it starts). + * This is apparently inherited from the parent in some cases and not + * in other cases. I guess this would come down to which kind of pthread + * implementation is actually in use, and how many sensible patches which + * are installed. + * I need to find a system where this problem shows up in order to come up + * with a proper fix. There's an pthread_create attribute for not inheriting + * scheduler stuff I think... + */ + rc = VINF_SUCCESS; +#endif + } + break; + } + + /* + * Any thread created before we determine the default config, remains unchanged! + * The prober thread above is one of those. + */ + default: + break; + } + + return rc; +} + |