VirtualBox

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

Last change on this file since 78402 was 77982, checked in by vboxsync, 6 years ago

IPRT: Addressed todo in rtSchedNativeCheckThreadTypes (sched-linux.cpp) about limiting the transition checking to thread types that are actually in use. bugref:7929

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