VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/sched-posix.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 31.4 KB
Line 
1/* $Id: sched-posix.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Scheduling, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 <sched.h>
67#include <unistd.h>
68#include <sys/resource.h>
69
70#include <iprt/thread.h>
71#include <iprt/process.h>
72#include <iprt/semaphore.h>
73#include <iprt/string.h>
74#include <iprt/assert.h>
75#include <iprt/log.h>
76#include <iprt/err.h>
77#include "internal/sched.h"
78#include "internal/thread.h"
79
80
81/*********************************************************************************************************************************
82* Structures and Typedefs *
83*********************************************************************************************************************************/
84
85/** Array scheduler attributes corresponding to each of the thread types. */
86typedef struct PROCPRIORITYTYPE
87{
88 /** For sanity include the array index. */
89 RTTHREADTYPE enmType;
90 /** The thread priority or nice delta - depends on which priority type. */
91 int iPriority;
92} PROCPRIORITYTYPE;
93
94
95/**
96 * Configuration of one priority.
97 */
98typedef struct
99{
100 /** The priority. */
101 RTPROCPRIORITY enmPriority;
102 /** The name of this priority. */
103 const char *pszName;
104 /** The process nice value. */
105 int iNice;
106 /** The delta applied to the iPriority value. */
107 int iDelta;
108 /** Array scheduler attributes corresponding to each of the thread types. */
109 const PROCPRIORITYTYPE *paTypes;
110} PROCPRIORITY;
111
112
113/**
114 * Saved priority settings
115 */
116typedef struct
117{
118 /** Process priority. */
119 int iPriority;
120 /** Process level. */
121 struct sched_param SchedParam;
122 /** Process level. */
123 int iPolicy;
124 /** pthread level. */
125 struct sched_param PthreadSchedParam;
126 /** pthread level. */
127 int iPthreadPolicy;
128} SAVEDPRIORITY, *PSAVEDPRIORITY;
129
130
131/*********************************************************************************************************************************
132* Global Variables *
133*********************************************************************************************************************************/
134/**
135 * Thread level priorities based on a 0..31 priority range
136 * as specified as the minimum for SCHED_RR/FIFO. FreeBSD
137 * seems to be using this (needs more research to be
138 * certain).
139 */
140static const PROCPRIORITYTYPE g_aTypesThread[RTTHREADTYPE_END] =
141{
142 { RTTHREADTYPE_INVALID, -999999999 },
143 { RTTHREADTYPE_INFREQUENT_POLLER, 5 },
144 { RTTHREADTYPE_MAIN_HEAVY_WORKER, 12 },
145 { RTTHREADTYPE_EMULATION, 14 },
146 { RTTHREADTYPE_DEFAULT, 15 },
147 { RTTHREADTYPE_GUI, 16 },
148 { RTTHREADTYPE_MAIN_WORKER, 18 },
149 { RTTHREADTYPE_VRDP_IO, 24 },
150 { RTTHREADTYPE_DEBUGGER, 28 },
151 { RTTHREADTYPE_MSG_PUMP, 29 },
152 { RTTHREADTYPE_IO, 30 },
153 { RTTHREADTYPE_TIMER, 31 }
154};
155
156static const PROCPRIORITYTYPE g_aTypesThreadFlat[RTTHREADTYPE_END] =
157{
158 { RTTHREADTYPE_INVALID, ~0 },
159 { RTTHREADTYPE_INFREQUENT_POLLER, 15 },
160 { RTTHREADTYPE_MAIN_HEAVY_WORKER, 15 },
161 { RTTHREADTYPE_EMULATION, 15 },
162 { RTTHREADTYPE_DEFAULT, 15 },
163 { RTTHREADTYPE_GUI, 15 },
164 { RTTHREADTYPE_MAIN_WORKER, 15 },
165 { RTTHREADTYPE_VRDP_IO, 15 },
166 { RTTHREADTYPE_DEBUGGER, 15 },
167 { RTTHREADTYPE_MSG_PUMP, 15 },
168 { RTTHREADTYPE_IO, 15 },
169 { RTTHREADTYPE_TIMER, 15 }
170};
171
172/**
173 * Process and thread level priority, full access at thread level.
174 */
175static const PROCPRIORITY g_aProcessAndThread[] =
176{
177 { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesThreadFlat },
178 { RTPROCPRIORITY_LOW, "Low", 9, 0, g_aTypesThread },
179 { RTPROCPRIORITY_LOW, "Low", 11, 0, g_aTypesThread },
180 { RTPROCPRIORITY_LOW, "Low", 15, 0, g_aTypesThread },
181 { RTPROCPRIORITY_LOW, "Low", 17, 0, g_aTypesThread },
182 { RTPROCPRIORITY_LOW, "Low", 19, 0, g_aTypesThread },
183 { RTPROCPRIORITY_LOW, "Low", 7, 0, g_aTypesThread },
184 { RTPROCPRIORITY_LOW, "Low", 5, 0, g_aTypesThread },
185 { RTPROCPRIORITY_LOW, "Low", 3, 0, g_aTypesThread },
186 { RTPROCPRIORITY_LOW, "Low", 1, 0, g_aTypesThread },
187 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesThread },
188 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesThreadFlat },
189 { RTPROCPRIORITY_HIGH, "High", -9, 0, g_aTypesThread },
190 { RTPROCPRIORITY_HIGH, "High", -7, 0, g_aTypesThread },
191 { RTPROCPRIORITY_HIGH, "High", -5, 0, g_aTypesThread },
192 { RTPROCPRIORITY_HIGH, "High", -3, 0, g_aTypesThread },
193 { RTPROCPRIORITY_HIGH, "High", -1, 0, g_aTypesThread },
194 { RTPROCPRIORITY_HIGH, "High", -9, 0, g_aTypesThreadFlat },
195 { RTPROCPRIORITY_HIGH, "High", -1, 0, g_aTypesThreadFlat }
196};
197
198/**
199 * Deltas for a process in which we are not restricted
200 * to only be lowering the priority.
201 */
202static const PROCPRIORITYTYPE g_aTypesUnixFree[RTTHREADTYPE_END] =
203{
204 { RTTHREADTYPE_INVALID, -999999999 },
205 { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
206 { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
207 { RTTHREADTYPE_EMULATION, +1 },
208 { RTTHREADTYPE_DEFAULT, 0 },
209 { RTTHREADTYPE_GUI, 0 },
210 { RTTHREADTYPE_MAIN_WORKER, 0 },
211 { RTTHREADTYPE_VRDP_IO, -1 },
212 { RTTHREADTYPE_DEBUGGER, -1 },
213 { RTTHREADTYPE_MSG_PUMP, -2 },
214 { RTTHREADTYPE_IO, -3 },
215 { RTTHREADTYPE_TIMER, -4 }
216};
217
218/**
219 * Deltas for a process in which we are restricted
220 * to only be lowering the priority.
221 */
222static const PROCPRIORITYTYPE g_aTypesUnixRestricted[RTTHREADTYPE_END] =
223{
224 { RTTHREADTYPE_INVALID, -999999999 },
225 { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
226 { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
227 { RTTHREADTYPE_EMULATION, +1 },
228 { RTTHREADTYPE_DEFAULT, 0 },
229 { RTTHREADTYPE_GUI, 0 },
230 { RTTHREADTYPE_MAIN_WORKER, 0 },
231 { RTTHREADTYPE_VRDP_IO, 0 },
232 { RTTHREADTYPE_DEBUGGER, 0 },
233 { RTTHREADTYPE_MSG_PUMP, 0 },
234 { RTTHREADTYPE_IO, 0 },
235 { RTTHREADTYPE_TIMER, 0 }
236};
237
238/**
239 * Deltas for a process in which we are restricted
240 * to only be lowering the priority.
241 */
242static const PROCPRIORITYTYPE g_aTypesUnixFlat[RTTHREADTYPE_END] =
243{
244 { RTTHREADTYPE_INVALID, -999999999 },
245 { RTTHREADTYPE_INFREQUENT_POLLER, 0 },
246 { RTTHREADTYPE_MAIN_HEAVY_WORKER, 0 },
247 { RTTHREADTYPE_EMULATION, 0 },
248 { RTTHREADTYPE_DEFAULT, 0 },
249 { RTTHREADTYPE_GUI, 0 },
250 { RTTHREADTYPE_MAIN_WORKER, 0 },
251 { RTTHREADTYPE_VRDP_IO, 0 },
252 { RTTHREADTYPE_DEBUGGER, 0 },
253 { RTTHREADTYPE_MSG_PUMP, 0 },
254 { RTTHREADTYPE_IO, 0 },
255 { RTTHREADTYPE_TIMER, 0 }
256};
257
258/**
259 * Process and thread level priority, full access at thread level.
260 */
261static const PROCPRIORITY g_aUnixConfigs[] =
262{
263 { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesUnixFlat },
264 { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixFree },
265 { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixFlat },
266 { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixFree },
267 { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixFlat },
268 { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixFree },
269 { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixFlat },
270 { RTPROCPRIORITY_LOW, "Low", 19, 19, g_aTypesUnixFlat },
271 { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixRestricted },
272 { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixRestricted },
273 { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixRestricted },
274 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixFree },
275 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixRestricted },
276 { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixFlat },
277 { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixFree },
278 { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixFree },
279 { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixFree },
280 { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixFree },
281 { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixFree },
282 { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixRestricted },
283 { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixRestricted },
284 { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixRestricted },
285 { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixRestricted },
286 { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixRestricted },
287 { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixFlat },
288 { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixFlat },
289 { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixFlat },
290 { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixFlat },
291 { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixFlat }
292};
293
294/**
295 * The dynamic default priority configuration.
296 *
297 * This will be recalulated at runtime depending on what the
298 * system allow us to do and what the current priority is.
299 */
300static PROCPRIORITY g_aDefaultPriority =
301{
302 RTPROCPRIORITY_LOW, "Default", 0, 0, g_aTypesUnixRestricted
303};
304
305/** Pointer to the current priority configuration. */
306static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
307
308
309/** Set to what kind of scheduling priority support the host
310 * OS seems to be offering. Determined at runtime.
311 */
312static enum
313{
314 OSPRIOSUP_UNDETERMINED = 0,
315 /** An excellent combination of process and thread level
316 * I.e. setpriority() works on process level, one have to be supervisor
317 * to raise priority as is the custom in unix. While pthread_setschedparam()
318 * works on thread level and we can raise the priority just like we want.
319 *
320 * I think this is what FreeBSD offers. (It is certainly analogous to what
321 * NT offers if you wondered.) Linux on the other hand doesn't provide this
322 * for processes with SCHED_OTHER policy, and I'm not sure if we want to
323 * play around with using the real-time SCHED_RR and SCHED_FIFO which would
324 * require special privileges anyway.
325 */
326 OSPRIOSUP_PROCESS_AND_THREAD_LEVEL,
327 /** A rough thread level priority only.
328 * setpriority() is the only real game in town, and it works on thread level.
329 */
330 OSPRIOSUP_THREAD_LEVEL
331} volatile g_enmOsPrioSup = OSPRIOSUP_UNDETERMINED;
332
333/** Set if we figure we have nice capability, meaning we can use setpriority
334 * to raise the priority. */
335static bool g_fCanNice = false;
336
337
338/*********************************************************************************************************************************
339* Internal Functions *
340*********************************************************************************************************************************/
341
342
343/**
344 * Saves all the scheduling attributes we can think of.
345 */
346static void rtSchedNativeSave(PSAVEDPRIORITY pSave)
347{
348 memset(pSave, 0xff, sizeof(*pSave));
349
350 errno = 0;
351 pSave->iPriority = getpriority(PRIO_PROCESS, 0 /* current process */);
352 Assert(errno == 0);
353
354 errno = 0;
355 sched_getparam(0 /* current process */, &pSave->SchedParam);
356 Assert(errno == 0);
357
358 errno = 0;
359 pSave->iPolicy = sched_getscheduler(0 /* current process */);
360 Assert(errno == 0);
361
362 int rc = pthread_getschedparam(pthread_self(), &pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
363 Assert(rc == 0); NOREF(rc);
364}
365
366
367/**
368 * Restores scheduling attributes.
369 * Most of this won't work right, but anyway...
370 */
371static void rtSchedNativeRestore(PSAVEDPRIORITY pSave)
372{
373 setpriority(PRIO_PROCESS, 0, pSave->iPriority);
374 sched_setscheduler(0, pSave->iPolicy, &pSave->SchedParam);
375 sched_setparam(0, &pSave->SchedParam);
376 pthread_setschedparam(pthread_self(), pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
377}
378
379
380/**
381 * Starts a worker thread and wait for it to complete.
382 * We cannot use RTThreadCreate since we're already owner of the RW lock.
383 */
384static int rtSchedCreateThread(void *(*pfnThread)(void *pvArg), void *pvArg)
385{
386 /*
387 * Setup thread attributes.
388 */
389 pthread_attr_t ThreadAttr;
390 int rc = pthread_attr_init(&ThreadAttr);
391 if (!rc)
392 {
393 rc = pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_JOINABLE);
394 if (!rc)
395 {
396 rc = pthread_attr_setstacksize(&ThreadAttr, 128*1024);
397 if (!rc)
398 {
399 /*
400 * Create the thread.
401 */
402 pthread_t Thread;
403 rc = pthread_create(&Thread, &ThreadAttr, pfnThread, pvArg);
404 if (!rc)
405 {
406 pthread_attr_destroy(&ThreadAttr);
407 /*
408 * Wait for the thread to finish.
409 */
410 void *pvRet = (void *)-1;
411 do
412 {
413 rc = pthread_join(Thread, &pvRet);
414 } while (rc == EINTR);
415 if (rc)
416 return RTErrConvertFromErrno(rc);
417 return (int)(uintptr_t)pvRet;
418 }
419 }
420 }
421 pthread_attr_destroy(&ThreadAttr);
422 }
423 return RTErrConvertFromErrno(rc);
424}
425
426
427static void rtSchedDumpPriority(void)
428{
429#ifdef THREAD_LOGGING
430 Log(("Priority: g_fCanNice=%d g_enmOsPrioSup=%d\n", g_fCanNice, g_enmOsPrioSup));
431 Log(("Priority: enmPriority=%d \"%s\" iNice=%d iDelta=%d\n",
432 g_pProcessPriority->enmPriority,
433 g_pProcessPriority->pszName,
434 g_pProcessPriority->iNice,
435 g_pProcessPriority->iDelta));
436 Log(("Priority: %2d INFREQUENT_POLLER = %d\n", RTTHREADTYPE_INFREQUENT_POLLER, g_pProcessPriority->paTypes[RTTHREADTYPE_INFREQUENT_POLLER].iPriority));
437 Log(("Priority: %2d MAIN_HEAVY_WORKER = %d\n", RTTHREADTYPE_MAIN_HEAVY_WORKER, g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_HEAVY_WORKER].iPriority));
438 Log(("Priority: %2d EMULATION = %d\n", RTTHREADTYPE_EMULATION , g_pProcessPriority->paTypes[RTTHREADTYPE_EMULATION ].iPriority));
439 Log(("Priority: %2d DEFAULT = %d\n", RTTHREADTYPE_DEFAULT , g_pProcessPriority->paTypes[RTTHREADTYPE_DEFAULT ].iPriority));
440 Log(("Priority: %2d GUI = %d\n", RTTHREADTYPE_GUI , g_pProcessPriority->paTypes[RTTHREADTYPE_GUI ].iPriority));
441 Log(("Priority: %2d MAIN_WORKER = %d\n", RTTHREADTYPE_MAIN_WORKER , g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_WORKER ].iPriority));
442 Log(("Priority: %2d VRDP_IO = %d\n", RTTHREADTYPE_VRDP_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_VRDP_IO ].iPriority));
443 Log(("Priority: %2d DEBUGGER = %d\n", RTTHREADTYPE_DEBUGGER , g_pProcessPriority->paTypes[RTTHREADTYPE_DEBUGGER ].iPriority));
444 Log(("Priority: %2d MSG_PUMP = %d\n", RTTHREADTYPE_MSG_PUMP , g_pProcessPriority->paTypes[RTTHREADTYPE_MSG_PUMP ].iPriority));
445 Log(("Priority: %2d IO = %d\n", RTTHREADTYPE_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_IO ].iPriority));
446 Log(("Priority: %2d TIMER = %d\n", RTTHREADTYPE_TIMER , g_pProcessPriority->paTypes[RTTHREADTYPE_TIMER ].iPriority));
447#endif
448}
449
450
451/**
452 * The prober thread.
453 * We don't want to mess with the priority of the calling thread.
454 *
455 * @remark This is pretty presumptive stuff, but if it works on Linux and
456 * FreeBSD it does what I want.
457 */
458static void *rtSchedNativeProberThread(void *pvUser)
459{
460 SAVEDPRIORITY SavedPriority;
461 rtSchedNativeSave(&SavedPriority);
462
463 /*
464 * Let's first try and see what we get on a thread level.
465 */
466 int iMax = sched_get_priority_max(SavedPriority.iPthreadPolicy);
467 int iMin = sched_get_priority_min(SavedPriority.iPthreadPolicy);
468 if (iMax - iMin >= 32)
469 {
470 pthread_t Self = pthread_self();
471 int i = iMin;
472 while (i <= iMax)
473 {
474 struct sched_param SchedParam = SavedPriority.PthreadSchedParam;
475 SchedParam.sched_priority = i;
476 if (pthread_setschedparam(Self, SavedPriority.iPthreadPolicy, &SchedParam))
477 break;
478 i++;
479 }
480 if (i == iMax)
481 g_enmOsPrioSup = OSPRIOSUP_PROCESS_AND_THREAD_LEVEL;
482 }
483
484 /*
485 * Ok, we didn't have the good stuff, so let's fall back on the unix stuff.
486 */
487 if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
488 g_enmOsPrioSup = OSPRIOSUP_THREAD_LEVEL;
489
490 /*
491 * Check if we can get higher priority (typically only root can do this).
492 * (Won't work right if our priority is -19 to start with, but what the heck.)
493 *
494 * We assume that the unix priority is -19 to 19. I know there are defines
495 * for this, but I don't remember which and if I'm awake enough to make sense
496 * of them from any SuS spec.
497 */
498 int iStart = getpriority(PRIO_PROCESS, 0);
499 int i = iStart;
500 while (i-- > -19)
501 {
502 if (setpriority(PRIO_PROCESS, 0, i))
503 break;
504 }
505 if (getpriority(PRIO_PROCESS, 0) != iStart)
506 g_fCanNice = true;
507 else
508 g_fCanNice = false;
509
510 /* done */
511 rtSchedNativeRestore(&SavedPriority);
512 RT_NOREF(pvUser);
513 return (void *)VINF_SUCCESS;
514}
515
516
517/**
518 * Calculate the scheduling properties for all the threads in the default
519 * process priority, assuming the current thread have the type enmType.
520 *
521 * @returns iprt status code.
522 * @param enmType The thread type to be assumed for the current thread.
523 */
524DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
525{
526 Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
527
528 /*
529 * First figure out what's supported by the OS.
530 */
531 if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
532 {
533 int iPriority = getpriority(PRIO_PROCESS, 0);
534 int rc = rtSchedCreateThread(rtSchedNativeProberThread, NULL);
535 if (RT_FAILURE(rc))
536 return rc;
537 if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
538 g_enmOsPrioSup = OSPRIOSUP_THREAD_LEVEL;
539 Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority);
540 }
541
542 /*
543 * Now let's see what we can do...
544 */
545 int iPriority = getpriority(PRIO_PROCESS, 0);
546 switch (g_enmOsPrioSup)
547 {
548 case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
549 {
550 g_aDefaultPriority.iNice = iPriority;
551 g_aDefaultPriority.iDelta = 0;
552 g_aDefaultPriority.paTypes = g_aTypesThread;
553 Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType);
554 break;
555 }
556
557 case OSPRIOSUP_THREAD_LEVEL:
558 {
559 if (g_fCanNice)
560 g_aDefaultPriority.paTypes = g_aTypesUnixFree;
561 else
562 g_aDefaultPriority.paTypes = g_aTypesUnixRestricted;
563 Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType);
564 g_aDefaultPriority.iNice = iPriority - g_aDefaultPriority.paTypes[enmType].iPriority;
565 g_aDefaultPriority.iDelta = g_aDefaultPriority.iNice;
566 break;
567 }
568
569 default:
570 AssertFailed();
571 break;
572 }
573 rtSchedDumpPriority();
574 return VINF_SUCCESS;
575}
576
577
578/**
579 * The validator thread.
580 * We don't want to mess with the priority of the calling thread.
581 *
582 * @remark This is pretty presumptive stuff, but if it works on Linux and
583 * FreeBSD it does what I want.
584 */
585static void *rtSchedNativeValidatorThread(void *pvUser)
586{
587 const PROCPRIORITY *pCfg = (const PROCPRIORITY *)pvUser;
588 SAVEDPRIORITY SavedPriority;
589 rtSchedNativeSave(&SavedPriority);
590
591 int rc = VINF_SUCCESS;
592 switch (g_enmOsPrioSup)
593 {
594 /*
595 * Try set the specified process priority and then try
596 * out all the thread priorities which are used.
597 */
598 case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
599 {
600 if (!setpriority(PRIO_PROCESS, 0, pCfg->iNice))
601 {
602 int iMin = sched_get_priority_min(SavedPriority.iPolicy);
603 pthread_t Self = pthread_self();
604 for (int i = RTTHREADTYPE_INVALID + 1; i < RTTHREADTYPE_END; i++)
605 {
606 struct sched_param SchedParam = SavedPriority.PthreadSchedParam;
607 SchedParam.sched_priority = pCfg->paTypes[i].iPriority
608 + pCfg->iDelta + iMin;
609 rc = pthread_setschedparam(Self, SavedPriority.iPthreadPolicy, &SchedParam);
610 if (rc)
611 {
612 rc = RTErrConvertFromErrno(rc);
613 break;
614 }
615 }
616 }
617 else
618 rc = RTErrConvertFromErrno(errno);
619 break;
620 }
621
622 /*
623 * Try out the priorities from the top and down.
624 */
625 case OSPRIOSUP_THREAD_LEVEL:
626 {
627 int i = RTTHREADTYPE_END;
628 while (--i > RTTHREADTYPE_INVALID)
629 {
630 int iPriority = pCfg->paTypes[i].iPriority + pCfg->iDelta;
631 if (setpriority(PRIO_PROCESS, 0, iPriority))
632 {
633 rc = RTErrConvertFromErrno(errno);
634 break;
635 }
636 }
637 break;
638 }
639
640 default:
641 AssertFailed();
642 break;
643 }
644
645 /* done */
646 rtSchedNativeRestore(&SavedPriority);
647 return (void *)(intptr_t)rc;
648}
649
650
651DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
652{
653 Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
654
655#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
656 /*
657 * Make sure the proxy creation thread is started so we don't 'lose' our
658 * initial priority if it's lowered.
659 */
660 rtThreadPosixPriorityProxyStart();
661#endif
662
663 /*
664 * Nothing to validate for the default priority (assuming no external renice).
665 */
666 int rc = VINF_SUCCESS;
667 if (enmPriority == RTPROCPRIORITY_DEFAULT)
668 g_pProcessPriority = &g_aDefaultPriority;
669 else
670 {
671 /*
672 * Select the array to search.
673 */
674 const PROCPRIORITY *pa;
675 unsigned c;
676 switch (g_enmOsPrioSup)
677 {
678 case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
679 pa = g_aProcessAndThread;
680 c = RT_ELEMENTS(g_aProcessAndThread);
681 break;
682 case OSPRIOSUP_THREAD_LEVEL:
683 pa = g_aUnixConfigs;
684 c = RT_ELEMENTS(g_aUnixConfigs);
685 break;
686 default:
687 pa = NULL;
688 c = 0;
689 break;
690 }
691
692 /*
693 * Search the array.
694 */
695 rc = VERR_FILE_NOT_FOUND;
696 unsigned i;
697 for (i = 0; i < c; i++)
698 {
699 if (pa[i].enmPriority == enmPriority)
700 {
701 /*
702 * Validate it.
703 */
704 int iPriority = getpriority(PRIO_PROCESS, 0);
705 int rc3 = rtSchedCreateThread(rtSchedNativeValidatorThread, (void *)&pa[i]);
706 Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority);
707 if (RT_SUCCESS(rc))
708 rc = rc3;
709 if (RT_SUCCESS(rc))
710 break;
711 }
712 }
713
714 /*
715 * Did we get lucky?
716 * If so update process priority and globals.
717 */
718 if (RT_SUCCESS(rc))
719 {
720 switch (g_enmOsPrioSup)
721 {
722 case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
723 if (setpriority(PRIO_PROCESS, 0, pa[i].iNice))
724 {
725 rc = RTErrConvertFromErrno(errno);
726 AssertMsgFailed(("setpriority(,,%d) -> errno=%d rc=%Rrc\n", pa[i].iNice, errno, rc));
727 }
728 break;
729
730 default:
731 break;
732 }
733
734 if (RT_SUCCESS(rc))
735 g_pProcessPriority = &pa[i];
736 }
737 }
738
739#ifdef THREAD_LOGGING
740 LogFlow(("rtProcNativeSetPriority: returns %Rrc enmPriority=%d\n", rc, enmPriority));
741 rtSchedDumpPriority();
742#endif
743 return rc;
744}
745
746
747/**
748 * Worker for rtThreadNativeSetPriority/OSPRIOSUP_PROCESS_AND_THREAD_LEVEL
749 * that's either called on the priority proxy thread or directly if no proxy.
750 */
751static DECLCALLBACK(int) rtThreadPosixSetPriorityOnProcAndThrdCallback(PRTTHREADINT pThread, RTTHREADTYPE enmType)
752{
753 struct sched_param SchedParam = {-9999999};
754 int iPolicy = -7777777;
755 int rc = pthread_getschedparam((pthread_t)pThread->Core.Key, &iPolicy, &SchedParam);
756 if (!rc)
757 {
758 SchedParam.sched_priority = g_pProcessPriority->paTypes[enmType].iPriority
759 + g_pProcessPriority->iDelta
760 + sched_get_priority_min(iPolicy);
761
762 rc = pthread_setschedparam((pthread_t)pThread->Core.Key, iPolicy, &SchedParam);
763 if (!rc)
764 {
765#ifdef THREAD_LOGGING
766 Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPolicy=%d sched_priority=%d pid=%d\n",
767 pThread->Core.Key, enmType, iPolicy, SchedParam.sched_priority, getpid()));
768#endif
769 return VINF_SUCCESS;
770 }
771 }
772
773 int rcNative = rc;
774 rc = RTErrConvertFromErrno(rc);
775 AssertMsgFailed(("pthread_[gs]etschedparam(%p, %d, {%d}) -> rcNative=%d rc=%Rrc\n",
776 (void *)pThread->Core.Key, iPolicy, SchedParam.sched_priority, rcNative, rc)); NOREF(rcNative);
777 return rc;
778}
779
780
781DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
782{
783 Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
784 Assert(enmType == g_pProcessPriority->paTypes[enmType].enmType);
785
786 int rc = VINF_SUCCESS;
787 switch (g_enmOsPrioSup)
788 {
789 case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
790 {
791#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
792 if (rtThreadPosixPriorityProxyStart())
793 rc = rtThreadPosixPriorityProxyCall(pThread, (PFNRT)rtThreadPosixSetPriorityOnProcAndThrdCallback,
794 2, pThread, enmType);
795 else
796#endif
797 rc = rtThreadPosixSetPriorityOnProcAndThrdCallback(pThread, enmType);
798 break;
799 }
800
801 case OSPRIOSUP_THREAD_LEVEL:
802 {
803 /* No cross platform way of getting the 'who' parameter value for
804 arbitrary threads, so this is restricted to the calling thread only. */
805 AssertReturn((pthread_t)pThread->Core.Key == pthread_self(), VERR_NOT_SUPPORTED);
806
807 int iPriority = g_pProcessPriority->paTypes[enmType].iPriority + g_pProcessPriority->iDelta;
808 if (!setpriority(PRIO_PROCESS, 0, iPriority))
809 {
810 AssertMsg(iPriority == getpriority(PRIO_PROCESS, 0), ("iPriority=%d getpriority()=%d\n", iPriority, getpriority(PRIO_PROCESS, 0)));
811#ifdef THREAD_LOGGING
812 Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPriority=%d pid=%d\n", pThread->Core.Key, enmType, iPriority, getpid()));
813#endif
814 }
815 else
816 {
817#if 0
818 rc = RTErrConvertFromErrno(errno);
819 AssertMsgFailed(("setpriority(,, %d) -> errno=%d rc=%Rrc\n", iPriority, errno, rc));
820#else
821 /** @todo
822 * Just keep quiet about failures now - we'll fail here because we're not
823 * allowed to raise our own priority. This is a problem when starting the
824 * threads with higher priority from EMT (i.e. most threads it starts).
825 * This is apparently inherited from the parent in some cases and not
826 * in other cases. I guess this would come down to which kind of pthread
827 * implementation is actually in use, and how many sensible patches which
828 * are installed.
829 * I need to find a system where this problem shows up in order to come up
830 * with a proper fix. There's an pthread_create attribute for not inheriting
831 * scheduler stuff I think...
832 */
833 rc = VINF_SUCCESS;
834#endif
835 }
836 break;
837 }
838
839 /*
840 * Any thread created before we determine the default config, remains unchanged!
841 * The prober thread above is one of those.
842 */
843 default:
844 break;
845 }
846
847 return rc;
848}
849
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use