Midnight Beach logo

Kylix for Delphi programmers - Linux Thread Priority

Under Delphi, TThread.Priority is an enum, and priorities like THREAD_PRIORITY_LOWEST and THREAD_PRIORITY_ABOVE_NORMAL have obvious interpretations. Under Kylix, scheduling is much more complicated. To start with, Linux processes have both static and dynamic priorities. TThread.Priority affects only the static priority. The dynamic priority is automatically increased whenever a normal process is ready to run but has to wait for other processes, and can be manually altered with the nice() or setpriority() system calls (below).

TThread.Priority is an integer from 0 to 99, where higher values have higher priority. In addition, Thread Priority is tightly bound to the scheduling Policy. Tasks running with superuser privileges can set Policy to SCHED_RR (real-time, round-robin scheduling), SCHED_FIFO (real-time, first-in first-out scheduling) or SCHED_OTHER (regular, non- real-time scheduling). Tasks running without superuser privileges can only use the default SCHED_OTHER Policy.

In the two real time policies, SCHED_RR and SCHED_FIFO, Priority can be set to a number from 1..99; in the standard policy, SCHED_OTHER, Priority must be 0. When a process with a high static priority becomes ready to run (emerges from a wait or a sleep), it preempts any running process with a lower static priority. SCHED_FIFO processes can only be preempted by higher priority tasks; they run until they block, yield, or terminate. SCHED_FIFO processes are not timeshared. SCHED_RR processes are much like SCHED_FIFO, except that they are timeshared and can be preempted by a waiting SCHED_RR task of the same priority. When the high priority process blocks, yields, terminates or is pre-empted, the next process of the same priority is run. If there is no process with the same priority ready to run, Linux gives the CPU (or "a CPU", on SMP machines) to the ready process with the next highest priority. Thus, normal SCHED_OTHER processes (with a static priority of 0) are only run if there is no real- time processes (which have a higher static priority) ready to run.

The "Linux scheduling policies" table summarizes the meaning of the three scheduling policies; see man sched_setscheduler for more information.

Linux scheduling policies
Policy Priority
Range
Policy
SCHED_OTHER 0 The default Linux time-sharing policy. Processes are only run when there is no waiting real-time process. You can change the dynamic priority with the nice() and setpriority() system calls.
SCHED_FIFO 1..99 The basic real-time scheduling policy. The system maintains a FIFO queue for each process priority; a FIFO process runs until it surrenders control or is preempted by a higher priority process. A preempted process stays at the head of its queue, and resumes execution as soon as there are no higher priority processes waiting.
SCHED_RR 1..99 A more sophisticated real-time scheduling policy. RR processes are subject to preemption if they exceed their CPU time "quantum" and there is at least one task of the same priority waiting. RR processes preempted by higher priority processes stay at the head of their queue and get the rest of their "quantum" before they time out. Process that exceed their quantum are placed at the end of the queue, and are not executed again until all currently waiting processes of the same priority have had a chance.

When there are more than one normal processes (with a static priority of 0) waiting for execution, execution order is governed by the dynamic priority. Dynamic priority does not apply to processes with a non-zero static priority. A normal process's dynamic priority is automatically increased whenever the process is ready to run but has to wait for other processes. When the scheduler is selecting a process to run, it will choose the highest priority waiting process; if there is more than process with the same high priority, it will choose the one that's been waiting longest. Dynamic priorities run from 20 to 20; unlike static priorities, a smaller number is a higher priority. That is, a dynamic priority of 0 is a higher priority than a dynamic priority of 20, and a dynamic priority of -20 is a higher priority than a dynamic priority of 0.

You can retrieve a process's dynamic priority with Libc.getpriority(ProcessID, 0), and if your process is running as a superuser - you can set it with Libc.setpriority(ProcessID, 0, NewPriority). Normal user processes can not use setpriority() which allows them to raise priority, but they can use Libc.nice() to lower their priority. That is, applications running as (for) normal users can only lower their priority or their thread's priority; they can't raise it.

Note that a ThreadId is not the same as a process ID (PID). To get the thread's PID to pass to getpriority() or setpriority(), you can use Libc.getpid. This returns the current process's PID. If you call it from the main (GUI) thread, you will get a different result than if you call it from a background thread.

Created on September 12, 2002, last updated March 24, 2006 • Contact jon@midnightbeach.com