VirtualBox

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

Last change on this file since 4512 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

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