sched-posix.cpp revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/* $Id$ */
/** @file
* InnoTek Portable Runtime - Scheduling, POSIX.
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*
* !WARNING!
*
* When talking about lowering and raising priority, we do *NOT* refer to
* the common direction priority values takes on unix systems (lower means
* higher). So, when we raise the priority of a linux thread the nice
* value will decrease, and when we lower the priority the nice value
* will increase. Confusing, right?
*
* !WARNING!
*/
/** @def THREAD_LOGGING
* Be very careful with enabling this, it may cause deadlocks when combined
* with the 'thread' logging prefix.
*/
#ifdef __DOXYGEN__
#define THREAD_LOGGING
#endif
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_THREAD
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <unistd.h>
#include <sys/resource.h>
#include <iprt/semaphore.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Array scheduler attributes corresponding to each of the thread types. */
typedef struct PROCPRIORITYTYPE
{
/** For sanity include the array index. */
/** The thread priority or nice delta - depends on which priority type. */
int iPriority;
/**
* Configuration of one priority.
*/
typedef struct
{
/** The priority. */
/** The name of this priority. */
const char *pszName;
/** The process nice value. */
int iNice;
/** The delta applied to the iPriority value. */
int iDelta;
/** Array scheduler attributes corresponding to each of the thread types. */
const PROCPRIORITYTYPE *paTypes;
} PROCPRIORITY;
/**
* Saved priority settings
*/
typedef struct
{
/** Process priority. */
int iPriority;
/** Process level. */
struct sched_param SchedParam;
/** Process level. */
int iPolicy;
/** pthread level. */
struct sched_param PthreadSchedParam;
/** pthread level. */
int iPthreadPolicy;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
* Thread level priorities based on a 0..31 priority range
* seems to be using this (needs more research to be
* certain).
*/
{
{ RTTHREADTYPE_INVALID, -999999999 },
{ RTTHREADTYPE_INFREQUENT_POLLER, 5 },
{ RTTHREADTYPE_MAIN_HEAVY_WORKER, 12 },
{ RTTHREADTYPE_EMULATION, 14 },
{ RTTHREADTYPE_DEFAULT, 15 },
{ RTTHREADTYPE_GUI, 16 },
{ RTTHREADTYPE_MAIN_WORKER, 18 },
{ RTTHREADTYPE_VRDP_IO, 24 },
{ RTTHREADTYPE_DEBUGGER, 28 },
{ RTTHREADTYPE_MSG_PUMP, 29 },
{ RTTHREADTYPE_IO, 30 },
{ RTTHREADTYPE_TIMER, 31 }
};
{
{ RTTHREADTYPE_INVALID, ~0 },
{ RTTHREADTYPE_INFREQUENT_POLLER, 15 },
{ RTTHREADTYPE_MAIN_HEAVY_WORKER, 15 },
{ RTTHREADTYPE_EMULATION, 15 },
{ RTTHREADTYPE_DEFAULT, 15 },
{ RTTHREADTYPE_GUI, 15 },
{ RTTHREADTYPE_MAIN_WORKER, 15 },
{ RTTHREADTYPE_VRDP_IO, 15 },
{ RTTHREADTYPE_DEBUGGER, 15 },
{ RTTHREADTYPE_MSG_PUMP, 15 },
{ RTTHREADTYPE_IO, 15 },
{ RTTHREADTYPE_TIMER, 15 }
};
/**
* Process and thread level priority, full access at thread level.
*/
static const PROCPRIORITY g_aProcessAndThread[] =
{
};
/**
* Deltas for a process in which we are not restricted
* to only be lowering the priority.
*/
{
{ RTTHREADTYPE_INVALID, -999999999 },
{ RTTHREADTYPE_INFREQUENT_POLLER, +3 },
{ RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
{ RTTHREADTYPE_EMULATION, +1 },
{ RTTHREADTYPE_DEFAULT, 0 },
{ RTTHREADTYPE_GUI, 0 },
{ RTTHREADTYPE_MAIN_WORKER, 0 },
{ RTTHREADTYPE_VRDP_IO, -1 },
{ RTTHREADTYPE_DEBUGGER, -1 },
{ RTTHREADTYPE_MSG_PUMP, -2 },
{ RTTHREADTYPE_IO, -3 },
{ RTTHREADTYPE_TIMER, -4 }
};
/**
* Deltas for a process in which we are restricted
* to only be lowering the priority.
*/
{
{ RTTHREADTYPE_INVALID, -999999999 },
{ RTTHREADTYPE_INFREQUENT_POLLER, +3 },
{ RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
{ RTTHREADTYPE_EMULATION, +1 },
{ RTTHREADTYPE_DEFAULT, 0 },
{ RTTHREADTYPE_GUI, 0 },
{ RTTHREADTYPE_MAIN_WORKER, 0 },
{ RTTHREADTYPE_VRDP_IO, 0 },
{ RTTHREADTYPE_DEBUGGER, 0 },
{ RTTHREADTYPE_MSG_PUMP, 0 },
{ RTTHREADTYPE_IO, 0 },
{ RTTHREADTYPE_TIMER, 0 }
};
/**
* Deltas for a process in which we are restricted
* to only be lowering the priority.
*/
{
{ RTTHREADTYPE_INVALID, -999999999 },
{ RTTHREADTYPE_INFREQUENT_POLLER, 0 },
{ RTTHREADTYPE_MAIN_HEAVY_WORKER, 0 },
{ RTTHREADTYPE_EMULATION, 0 },
{ RTTHREADTYPE_DEFAULT, 0 },
{ RTTHREADTYPE_GUI, 0 },
{ RTTHREADTYPE_MAIN_WORKER, 0 },
{ RTTHREADTYPE_VRDP_IO, 0 },
{ RTTHREADTYPE_DEBUGGER, 0 },
{ RTTHREADTYPE_MSG_PUMP, 0 },
{ RTTHREADTYPE_IO, 0 },
{ RTTHREADTYPE_TIMER, 0 }
};
/**
* Process and thread level priority, full access at thread level.
*/
static const PROCPRIORITY g_aUnixConfigs[] =
{
};
/**
* The dynamic default priority configuration.
*
* This will be recalulated at runtime depending on what the
* system allow us to do and what the current priority is.
*/
static PROCPRIORITY g_aDefaultPriority =
{
};
/** Pointer to the current priority configuration. */
/** Set to what kind of scheduling priority support the host
* OS seems to be offering. Determined at runtime.
*/
static enum
{
/** An excellent combination of process and thread level
* I.e. setpriority() works on process level, one have to be supervisor
* to raise priority as is the custom in unix. While pthread_setschedparam()
* works on thread level and we can raise the priority just like we want.
*
* I think this is what FreeBSD offers. (It is certainly analogous to what
* NT offers if you wondered.) Linux on the other hand doesn't provide this
* for processes with SCHED_OTHER policy, and I'm not sure if we want to
* play around with using the real-time SCHED_RR and SCHED_FIFO which would
* require special privilegdes anyway.
*/
/** A rough thread level priority only.
* setpriority() is the only real game in town, and it works on thread level.
*/
} volatile g_enmOsPrioSup = OSPRIOSUP_UNDETERMINED;
/** Set if we figure we have nice capability, meaning we can use setpriority
* to raise the priority. */
bool g_fCanNice = false;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Saves all the scheduling attributes we can think of.
*/
{
errno = 0;
errno = 0;
errno = 0;
}
/**
* Restores scheduling attributes.
* Most of this won't work right, but anyway...
*/
{
}
/**
* Starts a worker thread and wait for it to complete.
* We cannot use RTThreadCreate since we're already owner of the RW lock.
*/
{
/*
* Setup thread attributes.
*/
if (!rc)
{
if (!rc)
{
if (!rc)
{
/*
* Create the thread.
*/
if (!rc)
{
/*
* Wait for the thread to finish.
*/
void *pvRet = (void *)-1;
do
{
if (rc)
return RTErrConvertFromErrno(rc);
}
}
}
}
return RTErrConvertFromErrno(rc);
}
static void rtSchedDumpPriority(void)
{
#ifdef THREAD_LOGGING
Log(("Priority: enmPriority=%d \"%s\" iNice=%d iDelta=%d\n",
Log(("Priority: %2d INFREQUENT_POLLER = %d\n", RTTHREADTYPE_INFREQUENT_POLLER, g_pProcessPriority->paTypes[RTTHREADTYPE_INFREQUENT_POLLER].iPriority));
Log(("Priority: %2d MAIN_HEAVY_WORKER = %d\n", RTTHREADTYPE_MAIN_HEAVY_WORKER, g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_HEAVY_WORKER].iPriority));
Log(("Priority: %2d EMULATION = %d\n", RTTHREADTYPE_EMULATION , g_pProcessPriority->paTypes[RTTHREADTYPE_EMULATION ].iPriority));
Log(("Priority: %2d DEFAULT = %d\n", RTTHREADTYPE_DEFAULT , g_pProcessPriority->paTypes[RTTHREADTYPE_DEFAULT ].iPriority));
Log(("Priority: %2d GUI = %d\n", RTTHREADTYPE_GUI , g_pProcessPriority->paTypes[RTTHREADTYPE_GUI ].iPriority));
Log(("Priority: %2d MAIN_WORKER = %d\n", RTTHREADTYPE_MAIN_WORKER , g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_WORKER ].iPriority));
Log(("Priority: %2d VRDP_IO = %d\n", RTTHREADTYPE_VRDP_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_VRDP_IO ].iPriority));
Log(("Priority: %2d DEBUGGER = %d\n", RTTHREADTYPE_DEBUGGER , g_pProcessPriority->paTypes[RTTHREADTYPE_DEBUGGER ].iPriority));
Log(("Priority: %2d MSG_PUMP = %d\n", RTTHREADTYPE_MSG_PUMP , g_pProcessPriority->paTypes[RTTHREADTYPE_MSG_PUMP ].iPriority));
Log(("Priority: %2d IO = %d\n", RTTHREADTYPE_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_IO ].iPriority));
Log(("Priority: %2d TIMER = %d\n", RTTHREADTYPE_TIMER , g_pProcessPriority->paTypes[RTTHREADTYPE_TIMER ].iPriority));
#endif
}
/**
* The prober thread.
* We don't want to mess with the priority of the calling thread.
*
* @remark This is pretty presumptive stuff, but if it works on Linux and
* FreeBSD it does what I want.
*/
static void *rtSchedNativeProberThread(void *pvUser)
{
/*
* Let's first try and see what we get on a thread level.
*/
{
int i = iMin;
while (i <= iMax)
{
SchedParam.sched_priority = i;
break;
i++;
}
if (i == iMax)
}
/*
* Ok, we didn't have the good stuff, so let's fall back on the unix stuff.
*/
if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
/*
* Check if we can get higher priority (typically only root can do this).
* (Won't work right if our priority is -19 to start with, but what the heck.)
*
* We assume that the unix priority is -19 to 19. I know there are defines
* for this, but I don't remember which and if I'm awake enough to make sense
* of them from any SuS spec.
*/
int i = iStart;
while (i-- > -19)
{
if (setpriority(PRIO_PROCESS, 0, i))
break;
}
g_fCanNice = true;
else
g_fCanNice = false;
/* done */
return (void *)VINF_SUCCESS;
}
/**
* Calculate the scheduling properties for all the threads in the default
* process priority, assuming the current thread have the type enmType.
*
* @returns iprt status code.
* @param enmType The thread type to be assumed for the current thread.
*/
{
/*
* First figure out what's supported by the OS.
*/
if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
{
if (RT_FAILURE(rc))
return rc;
if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
}
/*
* Now let's see what we can do...
*/
switch (g_enmOsPrioSup)
{
{
g_aDefaultPriority.iDelta = 0;
break;
}
case OSPRIOSUP_THREAD_LEVEL:
{
if (g_fCanNice)
else
break;
}
default:
AssertFailed();
break;
}
return VINF_SUCCESS;
}
/**
* The validator thread.
* We don't want to mess with the priority of the calling thread.
*
* @remark This is pretty presumptive stuff, but if it works on Linux and
* FreeBSD it does what I want.
*/
static void *rtSchedNativeValidatorThread(void *pvUser)
{
int rc = VINF_SUCCESS;
switch (g_enmOsPrioSup)
{
/*
* Try set the specified process priority and then try
* out all the thread priorities which are used.
*/
{
{
{
if (rc)
{
break;
}
}
}
else
break;
}
/*
* Try out the priorities from the top and down.
*/
case OSPRIOSUP_THREAD_LEVEL:
{
int i = RTTHREADTYPE_LAST;
while (--i > RTTHREADTYPE_INVALID)
{
{
break;
}
}
break;
}
default:
AssertFailed();
break;
}
/* done */
return (void *)rc;
}
/**
* Validates and sets the process priority.
* This will check that all rtThreadNativeSetPriority() will success for all the
* thread types when applied to the current thread.
*
* @returns iprt status code.
* @param enmPriority The priority to validate and set.
*/
{
int rc = VINF_SUCCESS;
if (enmPriority == RTPROCPRIORITY_DEFAULT)
else
{
/*
* Select the array to search.
*/
const PROCPRIORITY *pa;
unsigned c;
switch (g_enmOsPrioSup)
{
c = ELEMENTS(g_aProcessAndThread);
break;
case OSPRIOSUP_THREAD_LEVEL:
pa = g_aUnixConfigs;
c = ELEMENTS(g_aUnixConfigs);
break;
default:
c = 0;
break;
}
/*
* Search the array.
*/
unsigned i;
for (i = 0; i < c; i++)
{
{
/*
* Validate it.
*/
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
break;
}
}
/*
* Did we get lucky?
* If so update process priority and globals.
*/
if (RT_SUCCESS(rc))
{
switch (g_enmOsPrioSup)
{
{
}
break;
default:
break;
}
if (RT_SUCCESS(rc))
g_pProcessPriority = &pa[i];
}
}
#ifdef THREAD_LOGGING
#endif
return rc;
}
/**
* Sets the priority of the thread according to the thread type
* and current process priority.
*
* The RTTHREADINT::enmType member has not yet been updated and will be updated by
* the caller on a successful return.
*
* @returns iprt status code.
* @param Thread The thread in question.
* @param enmType The thread type.
*/
{
int rc = VINF_SUCCESS;
switch (g_enmOsPrioSup)
{
{
int iPolicy = -7777777;
if (!rc)
{
if (!rc)
{
#ifdef THREAD_LOGGING
Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPolicy=%d sched_priority=%d pid=%d\n",
#endif
break;
}
}
AssertMsgFailed(("pthread_[gs]etschedparam(%p, %d, {%d}) -> rcNative=%d rc=%Vrc\n",
break;
}
case OSPRIOSUP_THREAD_LEVEL:
{
{
AssertMsg(iPriority == getpriority(PRIO_PROCESS, 0), ("iPriority=%d getpriority()=%d\n", iPriority, getpriority(PRIO_PROCESS, 0)));
#ifdef THREAD_LOGGING
Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPriority=%d pid=%d\n", pThread->Core.Key, enmType, iPriority, getpid()));
#endif
}
else
{
#if 0
#else
/** @todo
* Just keep quiet about failures now - we'll fail here because we're not
* allowed to raise our own priority. This is a problem when starting the
* threads with higher priority from EMT (i.e. most threads it starts).
* This is apparently inherited from the parent in some cases and not
* in other cases. I guess this would come down to which kind of pthread
* implementation is actually in use, and how many sensible patches which
* are installed.
* I need to find a system where this problem shows up in order to come up
* with a proper fix. There's an pthread_create attribute for not inherting
* scheduler stuff I think...
*/
rc = VINF_SUCCESS;
#endif
}
break;
}
/*
* Any thread created before we determin the default config, remains unchanged!
* The prober thread above is one of those.
*/
default:
break;
}
return rc;
}