VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/sched-linux.cpp@ 106061

Last change on this file since 106061 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 25.9 KB
Line 
1/* $Id: sched-linux.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Scheduling, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37/*
38 * !WARNING!
39 *
40 * When talking about lowering and raising priority, we do *NOT* refer to
41 * the common direction priority values takes on unix systems (lower means
42 * higher). So, when we raise the priority of a linux thread the nice
43 * value will decrease, and when we lower the priority the nice value
44 * will increase. Confusing, right?
45 *
46 * !WARNING!
47 */
48
49
50
51/** @def THREAD_LOGGING
52 * Be very careful with enabling this, it may cause deadlocks when combined
53 * with the 'thread' logging prefix.
54 */
55#ifdef DOXYGEN_RUNNING
56# define THREAD_LOGGING
57#endif
58
59
60/*********************************************************************************************************************************
61* Header Files *
62*********************************************************************************************************************************/
63#define LOG_GROUP RTLOGGROUP_THREAD
64#include <errno.h>
65#include <pthread.h>
66#include <limits.h>
67#include <sched.h>
68#include <unistd.h>
69#include <sys/resource.h>
70
71#include <iprt/thread.h>
72#include <iprt/process.h>
73#include <iprt/semaphore.h>
74#include <iprt/string.h>
75#include <iprt/assert.h>
76#include <iprt/log.h>
77#include <iprt/errcore.h>
78#include "internal/sched.h"
79#include "internal/thread.h"
80
81
82/*********************************************************************************************************************************
83* Structures and Typedefs *
84*********************************************************************************************************************************/
85
86/** Array scheduler attributes corresponding to each of the thread types.
87 * @internal */
88typedef struct PROCPRIORITYTYPE
89{
90 /** For sanity include the array index. */
91 RTTHREADTYPE enmType;
92 /** The thread priority or nice delta - depends on which priority type. */
93 int iPriority;
94} PROCPRIORITYTYPE;
95
96
97/**
98 * Configuration of one priority.
99 * @internal
100 */
101typedef struct
102{
103 /** The priority. */
104 RTPROCPRIORITY enmPriority;
105 /** The name of this priority. */
106 const char *pszName;
107 /** The process nice value. */
108 int iNice;
109 /** The delta applied to the iPriority value. */
110 int iDelta;
111 /** Array scheduler attributes corresponding to each of the thread types. */
112 const PROCPRIORITYTYPE *paTypes;
113} PROCPRIORITY;
114
115
116/**
117 * Saved priority settings
118 * @internal
119 */
120typedef struct
121{
122 /** Process priority. */
123 int iPriority;
124 /** Process level. */
125 struct sched_param SchedParam;
126 /** Process level. */
127 int iPolicy;
128 /** pthread level. */
129 struct sched_param PthreadSchedParam;
130 /** pthread level. */
131 int iPthreadPolicy;
132} SAVEDPRIORITY, *PSAVEDPRIORITY;
133
134
135/**
136 * Priorities for checking by separate thread
137 * @internal
138 */
139typedef struct
140{
141 /** The current thread priority to assume first. */
142 int iCurrent;
143 /** The thread priority to try set afterwards. */
144 int iNew;
145} VALIDATORPRIORITYPAIR, *PVALIDATORPRIORITYPAIR;
146
147
148/*********************************************************************************************************************************
149* Global Variables *
150*********************************************************************************************************************************/
151/**
152 * Deltas for a process in which we are not restricted
153 * to only be lowering the priority.
154 */
155static const PROCPRIORITYTYPE g_aTypesLinuxFree[RTTHREADTYPE_END] =
156{
157 { RTTHREADTYPE_INVALID, -999999999 },
158 { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
159 { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
160 { RTTHREADTYPE_EMULATION, +1 },
161 { RTTHREADTYPE_DEFAULT, 0 },
162 { RTTHREADTYPE_GUI, 0 },
163 { RTTHREADTYPE_MAIN_WORKER, 0 },
164 { RTTHREADTYPE_VRDP_IO, -1 },
165 { RTTHREADTYPE_DEBUGGER, -1 },
166 { RTTHREADTYPE_MSG_PUMP, -2 },
167 { RTTHREADTYPE_IO, -3 },
168 { RTTHREADTYPE_TIMER, -4 }
169};
170
171/**
172 * Deltas for a process in which we are restricted and can only lower the priority.
173 */
174static const PROCPRIORITYTYPE g_aTypesLinuxRestricted[RTTHREADTYPE_END] =
175{
176 { RTTHREADTYPE_INVALID, -999999999 },
177 { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
178 { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
179 { RTTHREADTYPE_EMULATION, +1 },
180 { RTTHREADTYPE_DEFAULT, 0 },
181 { RTTHREADTYPE_GUI, 0 },
182 { RTTHREADTYPE_MAIN_WORKER, 0 },
183 { RTTHREADTYPE_VRDP_IO, 0 },
184 { RTTHREADTYPE_DEBUGGER, 0 },
185 { RTTHREADTYPE_MSG_PUMP, 0 },
186 { RTTHREADTYPE_IO, 0 },
187 { RTTHREADTYPE_TIMER, 0 }
188};
189
190/**
191 * All threads have the same priority.
192 *
193 * This is typically chosen when we find that we can't raise the priority
194 * to the process default of a thread created by a low priority thread.
195 */
196static const PROCPRIORITYTYPE g_aTypesLinuxFlat[RTTHREADTYPE_END] =
197{
198 { RTTHREADTYPE_INVALID, -999999999 },
199 { RTTHREADTYPE_INFREQUENT_POLLER, 0 },
200 { RTTHREADTYPE_MAIN_HEAVY_WORKER, 0 },
201 { RTTHREADTYPE_EMULATION, 0 },
202 { RTTHREADTYPE_DEFAULT, 0 },
203 { RTTHREADTYPE_GUI, 0 },
204 { RTTHREADTYPE_MAIN_WORKER, 0 },
205 { RTTHREADTYPE_VRDP_IO, 0 },
206 { RTTHREADTYPE_DEBUGGER, 0 },
207 { RTTHREADTYPE_MSG_PUMP, 0 },
208 { RTTHREADTYPE_IO, 0 },
209 { RTTHREADTYPE_TIMER, 0 }
210};
211
212/**
213 * Process and thread level priority, full access at thread level.
214 */
215static const PROCPRIORITY g_aUnixConfigs[] =
216{
217 { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesLinuxFlat },
218 { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxFree },
219 { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxFlat },
220 { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxFree },
221 { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxFlat },
222 { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxFree },
223 { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxFlat },
224 { RTPROCPRIORITY_LOW, "Low", 19, 19, g_aTypesLinuxFlat },
225 { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxRestricted },
226 { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxRestricted },
227 { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxRestricted },
228 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxFree },
229 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxRestricted },
230 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxFlat },
231 { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxFree },
232 { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxFree },
233 { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxFree },
234 { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxFree },
235 { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxFree },
236 { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxRestricted },
237 { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxRestricted },
238 { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxRestricted },
239 { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxRestricted },
240 { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxRestricted },
241 { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxFlat },
242 { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxFlat },
243 { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxFlat },
244 { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxFlat },
245 { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxFlat }
246};
247
248/**
249 * The dynamic default priority configuration.
250 *
251 * This will be recalulated at runtime depending on what the
252 * system allow us to do and what the current priority is.
253 */
254static PROCPRIORITY g_aDefaultPriority =
255{
256 RTPROCPRIORITY_LOW, "Default", 0, 0, g_aTypesLinuxRestricted
257};
258
259/** Pointer to the current priority configuration. */
260static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
261
262/** Set if we can raise the priority of a thread beyond the default.
263 *
264 * It might mean we have the CAP_SYS_NICE capability or that the
265 * process's RLIMIT_NICE is higher than the priority of the thread
266 * calculating the defaults.
267 */
268static bool g_fCanRaisePriority = false;
269
270/** Set if we can restore the priority after having temporarily lowered or raised it. */
271static bool g_fCanRestorePriority = false;
272
273/** Set if we can NOT raise the priority to the process default in a thread
274 * created by a thread running below the process default.
275 */
276static bool g_fScrewedUpMaxPriorityLimitInheritance = true;
277
278/** The highest priority we can set. */
279static int g_iMaxPriority = 0;
280
281/** The lower priority we can set. */
282static int g_iMinPriority = 19;
283
284/** Set when we've successfully determined the capabilities of the process and kernel. */
285static bool g_fInitialized = false;
286
287
288
289/*********************************************************************************************************************************
290* Internal Functions *
291*********************************************************************************************************************************/
292
293
294/**
295 * Saves all the scheduling attributes we can think of.
296 */
297static void rtSchedNativeSave(PSAVEDPRIORITY pSave)
298{
299 memset(pSave, 0xff, sizeof(*pSave));
300
301 errno = 0;
302 pSave->iPriority = getpriority(PRIO_PROCESS, 0 /* current process */);
303 Assert(errno == 0);
304
305 errno = 0;
306 sched_getparam(0 /* current process */, &pSave->SchedParam);
307 Assert(errno == 0);
308
309 errno = 0;
310 pSave->iPolicy = sched_getscheduler(0 /* current process */);
311 Assert(errno == 0);
312
313 int rc = pthread_getschedparam(pthread_self(), &pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
314 Assert(rc == 0); NOREF(rc);
315}
316
317
318/**
319 * Restores scheduling attributes.
320 * Most of this won't work right, but anyway...
321 */
322static void rtSchedNativeRestore(PSAVEDPRIORITY pSave)
323{
324 setpriority(PRIO_PROCESS, 0, pSave->iPriority);
325 sched_setscheduler(0, pSave->iPolicy, &pSave->SchedParam);
326 sched_setparam(0, &pSave->SchedParam);
327 pthread_setschedparam(pthread_self(), pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
328}
329
330
331/**
332 * Called on the priority proxy thread if requested running, otherwise
333 * rtSchedRunThread() calls it directly.
334 */
335static DECLCALLBACK(int) rtSchedRunThreadCallback(pthread_t *pThread, void *(*pfnThread)(void *pvArg), void *pvArg)
336{
337 int rc = pthread_create(pThread, NULL, pfnThread, pvArg);
338 if (!rc)
339 return VINF_SUCCESS;
340 return RTErrConvertFromErrno(rc);
341}
342
343
344/**
345 * Starts a worker thread and wait for it to complete.
346 *
347 * We cannot use RTThreadCreate since we're already owner of the RW lock.
348 */
349static int rtSchedRunThread(void *(*pfnThread)(void *pvArg), void *pvArg, bool fUsePriorityProxy)
350{
351 /*
352 * Create the thread.
353 */
354 pthread_t Thread;
355 int rc;
356#ifndef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
357 RT_NOREF(fUsePriorityProxy);
358#else
359 if ( fUsePriorityProxy
360 && rtThreadPosixPriorityProxyStart())
361 rc = rtThreadPosixPriorityProxyCall(NULL, (PFNRT)rtSchedRunThreadCallback, 3, &Thread, pfnThread, pvArg);
362 else
363#endif
364 rc = rtSchedRunThreadCallback(&Thread, pfnThread, pvArg);
365 if (RT_SUCCESS(rc))
366 {
367 /*
368 * Wait for the thread to finish.
369 */
370 void *pvRet = (void *)-1;
371 do
372 {
373 rc = pthread_join(Thread, &pvRet);
374 } while (rc == EINTR);
375 if (rc)
376 return RTErrConvertFromErrno(rc);
377 return (int)(uintptr_t)pvRet;
378 }
379 return rc;
380}
381
382
383static void rtSchedDumpPriority(void)
384{
385#ifdef THREAD_LOGGING
386 Log(("Priority: g_fCanRaisePriority=%RTbool g_fCanRestorePriority=%RTbool g_fScrewedUpMaxPriorityLimitInheritance=%RTbool\n",
387 g_fCanRaisePriority, g_fCanRestorePriority, g_fScrewedUpMaxPriorityLimitInheritance));
388 Log(("Priority: g_iMaxPriority=%d g_iMinPriority=%d\n", g_iMaxPriority, g_iMinPriority));
389 Log(("Priority: enmPriority=%d \"%s\" iNice=%d iDelta=%d\n",
390 g_pProcessPriority->enmPriority,
391 g_pProcessPriority->pszName,
392 g_pProcessPriority->iNice,
393 g_pProcessPriority->iDelta));
394 Log(("Priority: %2d INFREQUENT_POLLER = %d\n", RTTHREADTYPE_INFREQUENT_POLLER, g_pProcessPriority->paTypes[RTTHREADTYPE_INFREQUENT_POLLER].iPriority));
395 Log(("Priority: %2d MAIN_HEAVY_WORKER = %d\n", RTTHREADTYPE_MAIN_HEAVY_WORKER, g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_HEAVY_WORKER].iPriority));
396 Log(("Priority: %2d EMULATION = %d\n", RTTHREADTYPE_EMULATION , g_pProcessPriority->paTypes[RTTHREADTYPE_EMULATION ].iPriority));
397 Log(("Priority: %2d DEFAULT = %d\n", RTTHREADTYPE_DEFAULT , g_pProcessPriority->paTypes[RTTHREADTYPE_DEFAULT ].iPriority));
398 Log(("Priority: %2d GUI = %d\n", RTTHREADTYPE_GUI , g_pProcessPriority->paTypes[RTTHREADTYPE_GUI ].iPriority));
399 Log(("Priority: %2d MAIN_WORKER = %d\n", RTTHREADTYPE_MAIN_WORKER , g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_WORKER ].iPriority));
400 Log(("Priority: %2d VRDP_IO = %d\n", RTTHREADTYPE_VRDP_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_VRDP_IO ].iPriority));
401 Log(("Priority: %2d DEBUGGER = %d\n", RTTHREADTYPE_DEBUGGER , g_pProcessPriority->paTypes[RTTHREADTYPE_DEBUGGER ].iPriority));
402 Log(("Priority: %2d MSG_PUMP = %d\n", RTTHREADTYPE_MSG_PUMP , g_pProcessPriority->paTypes[RTTHREADTYPE_MSG_PUMP ].iPriority));
403 Log(("Priority: %2d IO = %d\n", RTTHREADTYPE_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_IO ].iPriority));
404 Log(("Priority: %2d TIMER = %d\n", RTTHREADTYPE_TIMER , g_pProcessPriority->paTypes[RTTHREADTYPE_TIMER ].iPriority));
405#endif
406}
407
408
409/**
410 * This just checks if it can raise the priority after having been
411 * created by a thread with a low priority.
412 *
413 * @returns zero on success, non-zero on failure.
414 * @param pvUser The priority of the parent before it was lowered (cast to int).
415 */
416static void *rtSchedNativeSubProberThread(void *pvUser)
417{
418 int iPriority = getpriority(PRIO_PROCESS, 0);
419 Assert(iPriority == g_iMinPriority);
420
421 if (setpriority(PRIO_PROCESS, 0, iPriority + 1))
422 return (void *)-1;
423 if (setpriority(PRIO_PROCESS, 0, (int)(intptr_t)pvUser))
424 return (void *)-1;
425 return (void *)0;
426}
427
428
429/**
430 * The prober thread.
431 * We don't want to mess with the priority of the calling thread.
432 *
433 * @remark This is pretty presumptive stuff, but if it works on Linux and
434 * FreeBSD it does what I want.
435 */
436static void *rtSchedNativeProberThread(void *pvUser)
437{
438 NOREF(pvUser);
439 SAVEDPRIORITY SavedPriority;
440 rtSchedNativeSave(&SavedPriority);
441
442 /*
443 * Check if we can get higher priority (typically only root can do this).
444 * (Won't work right if our priority is -19 to start with, but what the heck.)
445 *
446 * We assume that the priority range is -19 to 19. Should probably find the right
447 * define for this.
448 */
449 int iStart = getpriority(PRIO_PROCESS, 0);
450 int i = iStart;
451 while (i-- > -20)
452 if (setpriority(PRIO_PROCESS, 0, i))
453 break;
454 g_iMaxPriority = getpriority(PRIO_PROCESS, 0);
455 g_fCanRaisePriority = g_iMaxPriority < iStart;
456 g_fCanRestorePriority = setpriority(PRIO_PROCESS, 0, iStart) == 0;
457
458 /*
459 * Check if we temporarily lower the thread priority.
460 * Again, we assume we're not at the extreme end of the priority scale.
461 */
462 iStart = getpriority(PRIO_PROCESS, 0);
463 i = iStart;
464 while (i++ < 19)
465 if (setpriority(PRIO_PROCESS, 0, i))
466 break;
467 g_iMinPriority = getpriority(PRIO_PROCESS, 0);
468 if ( setpriority(PRIO_PROCESS, 0, iStart)
469 || getpriority(PRIO_PROCESS, 0) != iStart)
470 g_fCanRestorePriority = false;
471 if (g_iMinPriority == g_iMaxPriority)
472 g_fCanRestorePriority = g_fCanRaisePriority = false;
473
474 /*
475 * Check what happens to child threads when the parent lowers the
476 * priority when it's being created.
477 */
478 iStart = getpriority(PRIO_PROCESS, 0);
479 g_fScrewedUpMaxPriorityLimitInheritance = true;
480 if ( g_fCanRestorePriority
481 && !setpriority(PRIO_PROCESS, 0, g_iMinPriority)
482 && iStart != g_iMinPriority)
483 {
484 if (rtSchedRunThread(rtSchedNativeSubProberThread, (void *)(intptr_t)iStart, false /*fUsePriorityProxy*/) == 0)
485 g_fScrewedUpMaxPriorityLimitInheritance = false;
486 }
487
488 /* done */
489 rtSchedNativeRestore(&SavedPriority);
490 return (void *)VINF_SUCCESS;
491}
492
493
494/**
495 * Calculate the scheduling properties for all the threads in the default
496 * process priority, assuming the current thread have the type enmType.
497 *
498 * @returns iprt status code.
499 * @param enmType The thread type to be assumed for the current thread.
500 */
501DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
502{
503 Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
504
505 /*
506 * First figure out what's we're allowed to do in this process.
507 */
508 if (!g_fInitialized)
509 {
510 int iPriority = getpriority(PRIO_PROCESS, 0);
511#ifdef RLIMIT_RTPRIO
512 /** @todo */
513#endif
514 int rc = rtSchedRunThread(rtSchedNativeProberThread, NULL, false /*fUsePriorityProxy*/);
515 if (RT_FAILURE(rc))
516 return rc;
517 Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority);
518 g_fInitialized = true;
519 }
520
521 /*
522 * Select the right priority type table and update the default
523 * process priority structure.
524 */
525 if (g_fCanRaisePriority && g_fCanRestorePriority && !g_fScrewedUpMaxPriorityLimitInheritance)
526 g_aDefaultPriority.paTypes = &g_aTypesLinuxFree[0];
527 else if (!g_fCanRaisePriority && g_fCanRestorePriority && !g_fScrewedUpMaxPriorityLimitInheritance)
528 g_aDefaultPriority.paTypes = &g_aTypesLinuxRestricted[0];
529 else
530 g_aDefaultPriority.paTypes = &g_aTypesLinuxFlat[0];
531 Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType);
532
533 int iPriority = getpriority(PRIO_PROCESS, 0 /* current process */);
534 g_aDefaultPriority.iNice = iPriority - g_aDefaultPriority.paTypes[enmType].iPriority;
535 g_aDefaultPriority.iDelta = g_aDefaultPriority.iNice;
536
537 rtSchedDumpPriority();
538 return VINF_SUCCESS;
539}
540
541
542/**
543 * The process priority validator thread.
544 * (We don't want to mess with the priority of the calling thread.)
545 */
546static void *rtSchedNativeValidatorThread(void *pvUser)
547{
548 PVALIDATORPRIORITYPAIR pPrioPair = (PVALIDATORPRIORITYPAIR)pvUser;
549 SAVEDPRIORITY SavedPriority;
550 rtSchedNativeSave(&SavedPriority);
551
552 int rc = VINF_SUCCESS;
553
554 /*
555 * Set the priority to the current value for specified thread type, but
556 * only if we have any threads of this type (caller checked - INT_MAX).
557 */
558 if (pPrioPair->iCurrent != INT_MAX)
559 if (setpriority(PRIO_PROCESS, 0, pPrioPair->iCurrent))
560 rc = RTErrConvertFromErrno(errno);
561
562 /*
563 * Try set the new priority.
564 */
565 if (RT_SUCCESS(rc) && setpriority(PRIO_PROCESS, 0, pPrioPair->iNew))
566 rc = RTErrConvertFromErrno(errno);
567
568 /* done */
569 rtSchedNativeRestore(&SavedPriority);
570 return (void *)(intptr_t)rc;
571}
572
573
574/**
575 * Validates the ability to apply suggested priority scheme.
576 *
577 * The function checks that we're able to apply all the thread types in the
578 * suggested priority scheme.
579 *
580 * @returns iprt status code.
581 * @param pCfg The priority scheme to validate.
582 * @param fHavePriorityProxy Set if we've got a priority proxy thread,
583 * otherwise clear.
584 */
585static int rtSchedNativeCheckThreadTypes(const PROCPRIORITY *pCfg, bool fHavePriorityProxy)
586{
587 int i = RTTHREADTYPE_END;
588 while (--i > RTTHREADTYPE_INVALID)
589 {
590 VALIDATORPRIORITYPAIR PrioPair;
591 PrioPair.iCurrent = g_pProcessPriority->paTypes[i].iPriority + g_pProcessPriority->iDelta;
592 PrioPair.iNew = pCfg->paTypes[i].iPriority + pCfg->iDelta;
593 if (g_acRTThreadTypeStats[i] == 0)
594 PrioPair.iCurrent = INT_MAX;
595
596#ifdef RT_STRICT
597 int const iPriority = getpriority(PRIO_PROCESS, 0);
598#endif
599 int rc = rtSchedRunThread(rtSchedNativeValidatorThread, &PrioPair, fHavePriorityProxy /*fUsePriorityProxy*/);
600 Assert(getpriority(PRIO_PROCESS, 0) == iPriority);
601
602 if (RT_FAILURE(rc))
603 return rc;
604 }
605 return VINF_SUCCESS;
606}
607
608
609DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
610{
611 Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
612
613#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
614 /*
615 * Make sure the proxy creation thread is started so we don't 'lose' our
616 * initial priority if it's lowered.
617 */
618 bool const fHavePriorityProxy = rtThreadPosixPriorityProxyStart();
619#else
620 bool const fHavePriorityProxy = false;
621#endif
622
623 int rc;
624 if (enmPriority == RTPROCPRIORITY_DEFAULT)
625 {
626 /*
627 * If we've lowered priority since the process started, it may be impossible
628 * to raise it again for existing thread (new threads will work fine).
629 */
630 rc = rtSchedNativeCheckThreadTypes(&g_aDefaultPriority, fHavePriorityProxy);
631 if (RT_SUCCESS(rc))
632 g_pProcessPriority = &g_aDefaultPriority;
633 }
634 else
635 {
636 /*
637 * Find a configuration which matches and can be applied.
638 */
639 rc = VERR_NOT_FOUND;
640 for (unsigned i = 0; i < RT_ELEMENTS(g_aUnixConfigs); i++)
641 if (g_aUnixConfigs[i].enmPriority == enmPriority)
642 {
643 int rc2 = rtSchedNativeCheckThreadTypes(&g_aUnixConfigs[i], fHavePriorityProxy);
644 if (RT_SUCCESS(rc2))
645 {
646 g_pProcessPriority = &g_aUnixConfigs[i];
647 rc = VINF_SUCCESS;
648 break;
649 }
650 if (rc == VERR_NOT_FOUND || rc == VERR_ACCESS_DENIED)
651 rc = rc2;
652 }
653 }
654
655#ifdef THREAD_LOGGING
656 LogFlow(("rtProcNativeSetPriority: returns %Rrc enmPriority=%d\n", rc, enmPriority));
657 rtSchedDumpPriority();
658#endif
659 return rc;
660}
661
662
663/**
664 * Called on the priority proxy thread if it's running, otherwise
665 * rtThreadNativeSetPriority calls it directly.
666 */
667static DECLCALLBACK(int) rtThreadLinuxSetPriorityCallback(PRTTHREADINT pThread, int iPriority)
668{
669 if (!setpriority(PRIO_PROCESS, pThread->tid, iPriority))
670 {
671 AssertMsg(iPriority == getpriority(PRIO_PROCESS, pThread->tid),
672 ("iPriority=%d getpriority()=%d\n", iPriority, getpriority(PRIO_PROCESS, pThread->tid)));
673#ifdef THREAD_LOGGING
674 Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPriority=%d pid=%d tid=%d\n",
675 pThread->Core.Key, enmType, iPriority, getpid(), pThread->tid));
676#endif
677 return VINF_SUCCESS;
678 }
679 AssertMsgFailed(("setpriority(,, %d) -> errno=%d rc=%Rrc\n", iPriority, errno, RTErrConvertFromErrno(errno)));
680 return VINF_SUCCESS; //non-fatal for now.
681}
682
683
684DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
685{
686 /* sanity */
687 Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
688 Assert(enmType == g_pProcessPriority->paTypes[enmType].enmType);
689
690 /*
691 * The thread ID is zero for alien threads, so skip these or we'd risk
692 * modifying our own priority.
693 */
694 if (!pThread->tid)
695 return VINF_SUCCESS;
696
697 /*
698 * Calculate the thread priority and apply it, preferrably via the priority proxy thread.
699 */
700 int const iPriority = g_pProcessPriority->paTypes[enmType].iPriority + g_pProcessPriority->iDelta;
701#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
702 if (rtThreadPosixPriorityProxyStart())
703 return rtThreadPosixPriorityProxyCall(pThread, (PFNRT)rtThreadLinuxSetPriorityCallback, 2, pThread, iPriority);
704#endif
705 return rtThreadLinuxSetPriorityCallback(pThread, iPriority);
706}
707
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette