VirtualBox

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

Last change on this file since 78365 was 77910, checked in by vboxsync, 6 years ago

Main: bugref:7929: Added ability to change the priority of the VM process

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