Advanced Search
Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page >

Using Mach Scheduling From User Applications

There are three basic ways to change how a user thread is scheduled. You can use the BSD pthreads API to change basic policy and importance. You can also use Mach RPC calls to change a task’s importance. Finally, you can use RPC calls to change the scheduling policy to move a thread into a different scheduling band. This is commonly used when interacting with CoreAudio.

The pthreads API is a user space API, and has limited relevance for kernel programmers. The Mach thread and task APIs are more general and can be used from anywhere in the kernel. The Mach thread and task calls can also be called from user applications.

In this section:

Using the pthreads API to Influence Scheduling
Using the Mach Thread API to Influence Scheduling
Using the Mach Task API to Influence Scheduling


Using the pthreads API to Influence Scheduling

Mac OS X supports a number of policies at the POSIX threads API level. If you need real-time behavior, you must use the Mach thread_policy_set call. This is described in “Using the Mach Thread API to Influence Scheduling”.

The pthreads API adjusts the priority of threads within a given task. It does not necessarily impact performance relative to threads in other tasks. To increase the priority of a task, you can use nice(1) or renice(8) from the command line or call getpriority and setpriority from your application.

The API provides two functions: pthread_getschedparam and pthread_setschedparam. Their prototypes look like this:

pthread_setschedparam(pthread_t thread, int policy,
        struct sched_param *param);
pthread_getschedparam(pthread_t thread, int *policy,
        struct sched_param *param)

The arguments for pthread_getschedparam are straightforward. The first argument is a thread ID, and the others are pointers to memory where the results will be stored.

The arguments to pthread_setschedparam are not as obvious, however. As with pthread_getschedparam, the first argument is a thread ID.

The second argument to pthread_setschedparam is the desired policy, which can currently be one of SCHED_FIFO (first in, first out), SCHED_RR (round-robin), or SCHED_OTHER. The SCHED_OTHER policy is generally used for extra policies that are specific to a given operating system, and should thus be avoided when writing portable code.

The third argument is a structure that contains various scheduling parameters.

Here is a basic example of using pthreads functions to set a thread’s scheduling policy and priority.

int set_my_thread_priority(int priority) {
    struct sched_param sp;
 
    memset(&sp, 0, sizeof(struct sched_param));
    sp.sched_priority=priority;
    if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp)  == -1) {
        printf("Failed to change priority.\n");
        return -1;
    }
    return 0;
}

This code snippet sets the scheduling policy for the current thread to round-robin scheduling, and sets the thread’s relative importance within the task to the value passed in through the priority argument.

For more information, see the manual page for pthread(3).

Using the Mach Thread API to Influence Scheduling

This API is frequently used in multimedia applications to obtain real-time priority. It is also useful in other situations when the pthread scheduling API cannot be used or does not provide the needed functionality.

The API consists of two functions, thread_policy_set and thread_policy_get.

kern_return_t thread_policy_set(
                thread_act_t thread,
                thread_policy_flavor_t flavor,
                thread_policy_t policy_info,
                mach_msg_type_number_t count);
 
kern_return_t thread_policy_get(
                thread_act_t thread,
                thread_policy_flavor_t flavor,
                thread_policy_t policy_info,
                mach_msg_type_number_t *count,
                boolean_t *get_default);

The parameters of these functions are roughly the same, except that the thread_policy_get function takes pointers for the count and the get_default arguments. The count is an inout parameter, meaning that it is interpreted as the maximum amount of storage (in units of int32_t) that the calling task has allocated for the return, but it is also overwritten by the scheduler to indicate the amount of data that was actually returned.

These functions get and set several parameters, according to the thread policy chosen. The possible thread policies are listed in Table 9-2.

Table 9-2  Thread policies

Policy

Meaning

THREAD_STANDARD_POLICY

Default value

THREAD_TIME_CONSTRAINT_POLICY

Used to specify real-time behavior.

THREAD_PRECEDENCE_POLICY

Used to indicate the importance of computation relative to other threads in a given task.

The following code snippet shows how to set the priority of a task to tell the scheduler that it needs real-time performance. The example values provided in comments are based on the estimated needs of esd (the Esound daemon).

#include <mach/mach_init.h>
#include <mach/thread_policy.h>
#include <mach/sched.h>
 
int set_realtime(int period, int computation, int constraint) {
    struct thread_time_constraint_policy ttcpolicy;
    int ret;
 
    ttcpolicy.period=period; // HZ/160
    ttcpolicy.computation=computation; // HZ/3300;
    ttcpolicy.constraint=constraint; // HZ/2200;
    ttcpolicy.preemptible=1;
 
    if ((ret=thread_policy_set(mach_thread_self(),
        THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&ttcpolicy,
        THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS) {
            fprintf(stderr, "set_realtime() failed.\n");
            return 0;
    }
    return 1;
}

The time values are in terms of Mach absolute time units. Since these values differ according to the bus speed of your computer, you should generally use numbers relative to HZ (a global variable containing the current number of ticks per second). You can either handle this conversion yourself by dividing this value by an appropriate quantity or use the conversion routines described in “Using Kernel Time Abstractions”.

Say your computer reports 133 million for the value of HZ. If you pass the example values given as arguments to this function, your thread tells the scheduler that it needs approximately 40,000 (HZ/3300) out of the next 833,333 (HZ/160) bus cycles. The preemptible value (1) indicates that those 40,000 bus cycles need not be contiguous. However, the constraint value (HZ/2200) tells the scheduler that there can be no more than 60,000 bus cycles between the start of computation and the end of computation.

Note: Because the constraint sets a maximum bound for computation, it must be larger than the value for computation.

A straightforward example using this API is code that displays video directly to the framebuffer hardware. It needs to run for a certain number of cycles every frame to get the new data into the frame buffer. It can be interrupted without worry, but if it is interrupted for too long, the video hardware starts displaying an outdated frame before the software writes the updated data, resulting in a nasty glitch. Audio has similar behavior, but since it is usually buffered along the way (in hardware and in software), there is greater tolerance for variations in timing, to a point.

Another policy call is THREAD_PRECEDENCE_POLICY. This is used for setting the relative importance of non-real-time threads. Its calling convention is similar, except that its structure is thread_precedence_policy, and contains only one field, an integer_t called importance. While this is a signed 32-bit value, the minimum legal value is zero (IDLE_PRI). threads set to IDLE_PRI will only execute when no other thread is scheduled to execute.

In general, larger values indicate higher priority. The maximum limit is subject to change, as are the priority bands, some of which have special purposes (such as real-time threads). Thus, in general, you should use pthreads APIs to achieve this functionality rather than using this policy directly unless you are setting up an idle thread.

Using the Mach Task API to Influence Scheduling

This relatively simple API is not particularly useful for most developers. However, it may be beneficial if you are developing a graphical user interface for Darwin. It also provides some insight into the prioritization of tasks in Mac OS X. It is presented here for completeness.

The API consists of two functions, task_policy_set and task_policy_get.

kern_return_t task_policy_set(
                task_t task,
                task_policy_flavor_t flavor,
                task_policy_t policy_info,
                mach_msg_type_number_t count);
 
kern_return_t task_policy_get(
                task_t task,
                task_policy_flavor_t flavor,
                task_policy_t policy_info,
                mach_msg_type_number_t *count,
                boolean_t *get_default);

As with thread_policy_set and thread_policy_get, the parameters are similar, except that the task_policy_get function takes pointers for the count and the get_default arguments. The count argument is an inout parameter. It is interpreted as the maximum amount of storage that the calling task has allocated for the return, but it is also overwritten by the scheduler to indicate the amount of data that was actually returned.

These functions get and set a single parameter, that of the role of a given task, which changes the way the task’s priority gets altered over time. The possible roles of a task are listed in Table 9-3.

Table 9-3  Task roles

Role

Meaning

TASK_UNSPECIFIED

Default value

TASK_RENICED

This is set when a process is executed with nice or is modified by renice.

TASK_FOREGROUND_APPLICATION

GUI application in the foreground. There can be more than one foreground application.

TASK_BACKGROUND_APPLICATION

GUI application in the background.

TASK_CONTROL_APPLICATION

Reserved for the dock or equivalent (assigned FCFS).

TASK_GRAPHICS_SERVER

Reserved for WindowServer or equivalent (assigned FCFS).

The following code snippet shows how to set the priority of a task to tell the scheduler that it is a foreground application (regardless of whether it really is).

#include <mach/mach_init.h>
#include <mach/task_policy.h>
#include <mach/sched.h>
 
int set_my_task_policy(void) {
    int ret;
    struct task_category_policy tcatpolicy;
 
    tcatpolicy.role = TASK_FOREGROUND_APPLICATION;
 
    if ((ret=task_policy_set(mach_task_self(),
        TASK_CATEGORY_POLICY, (thread_policy_t)&tcatpolicy,
        TASK_CATEGORY_POLICY_COUNT)) != KERN_SUCCESS) {
            fprintf(stderr, "set_my_task_policy() failed.\n");
            return 0;
    }
    return 1;
}


< Previous PageNext Page >


Last updated: 2006-11-07




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice