Basic Thread Scheduling

Priority Scheduling

Thread Priority

Thread priority is specified by a number from 0 to 31: the lower the number, the higher the priority. Therefore, 0 indicates the highest priority and 31 the lowest. The priority of the default thread (the thread obtained by OSGetDefaultThread) on each core in the COS is set to 16.

Scheduling Policy

Thread scheduling is performed according to the priority set per thread. Of executable threads on each core, the highest priority thread is selected, and then executed.

The scheduler does not reschedule using a time quantum. This behavior is different from many major operating systems because it means that the scheduler does not manage the thread CPU occupation time. Instead, rescheduling occurs when an interrupt occurs or when the active thread goes into standby. Even after rescheduling, the thread that was previously running still holds the highest priority. As long as the current thread is executable, the current thread continues to run even if a different thread with the same priority exists. However, if the interrupt is caused by different thread with a higher priority, the original thread is placed at the end of the priority queue, and the thread with the higher priority is executed.

Thread Rescheduling

Thread rescheduling occurs under the following conditions:

Core Affinity

When creating a thread, you specify a core for the thread to run on, which is named its core affinity. The thread then runs only on the specified core unless and until its affinity is changed. Threads do not migrate between cores automatically.

Changing the Core Affinity

You may use OSSetThreadAffinity to change a thread's core affinity. After the change, threads are executed according to the newly specified core affinity.

Disadvantages Associated with a Core Affinity Change

The core affinity should not be changed frequently because of the following disadvantages.

Interaction with Interrupts

Because interrupt and exception handlers have a higher priority than threads, they stop all threads on a core where an interrupt or exception occurs.

Callbacks

Callbacks are invoked from the application I/O threads described below. The devices and alarm interrupt handlers notify the application I/O threads of the interrupts. When an interrupt handler finishes processing, the application I/O thread runs and executes a callback registered by the application. During a callback, interrupts are enabled.

Application I/O Threads

The application I/O thread has a higher priority than the application thread created by the OSCreateThread function. The application I/O thread is used to execute user callbacks. All callbacks, such as alarm, audio, controller, and file system, are executed from the application I/O threads.

Alarm Callback

An alarm callback is executed from the application I/O thread. An alarm application I/O thread is created for each core at the time of COS initialization. The COS prepares the stack for these threads; the size is 16KB. In Green Hills MULTI, these threads are displayed in the thread list with the following names: App I/O Alarm Core 0, App I/O Alarm Core 1, and App I/O Alarm Core 2.

The process flow for executing an alarm callback is shown in Figures 7 − 10. The vertical axis indicates the process transition.


Figure 7: Executing an Alarm Callback on Alarm Application I/O Thread

In Figure 7, an alarm callback is executed while interrupt is enabled. Thread synchronization objects, such as mutexes, may be used within the alarm callback. However, exercise caution when using thread synch objects in the alarm callback. The application I/O thread sequentially invokes a callback for every triggered alarm. If the thread is blocked within one alarm callback, succeeding callbacks are not called until it is unblocked. Do not block a thread for an extended period of time.

The following alarm callback cases require some modifications to applications:

The alarm callback receives the OSContext pointer as an argument. This pointer indicates the OSContext variable address in the structure of the thread that was running at the time of interrupt. When an interrupt occurs, COS saves the context of the running thread in this variable.


Figure 8: OSContext of the alarm callback is different from the thread at the instant of an alarm
Executing an alarm while alarm application I/O thread is running, and then another Interrupt occurs.

In Figure 8, while the alarm application I/O thread is running, an alarm interrupt occurs; any interrupt that takes place before the callback is executed. Then, by the time of the interrupt after the alarm interrupt, the saved context is overwritten. Therefore, OSContext at the time that the alarm callback is invoked should contain the context saved at the time of the interrupt after the alarm interrupt.

If an alarm interrupt occurs while the application thread is running, the context saved at the time of the alarm interrupt is usually not overwritten as described above. This is because the application I/O thread to execute the alarm callback has a higher priority than the application thread. The interrupted application thread is not executed until the alarm callback execution completes.

From the alarm callback, you can obtain the structure of the current thread with OSGetCurrentThread and find the context variable address of the alarm application I/O thread. Because of the possibility of the issue described in the previous example, if the OSContext argument indicates the context of the alarm application I/O thread, the context at the time of the alarm interrupt may have been overwritten. Therefore, it is recommended not to use it.


Figure 9: OSContext of the alarm callback is different from the thread at the instant of an alarm
The alarm callback goes to sleep waiting for a semaphore, and the thread interrupted by the alarm resumes.

In this case, if any interrupt occurs when the application thread resumes its processing while waiting for a semaphore, the context saved at the time of alarm interrupt is overwritten. Using no synch object within the alarm callback can ensure that the saved context remains intact.

Also, the following may occur to a thread with multicore affinity. An application thread has Core 0 and Core 1 affinity. An alarm interrupts the thread while it is running on Core 0. Then, the application thread execution moves to Core 1.


Figure 10: OSContext of the alarm callback is different from the thread at the instant of an alarm
Execute an alarm callback on another Core after a thread with multicore affinity is interrupted by the alarm.

In Figure 10, while executing the alarm callback on Core 0, if any interrupt occurs to the application thread that is running on Core 1, the context stored at the time of the alarm interrupt is overwritten. Because of the possibility of the issue described in this example, if the OSContext argument is for a thread with multicore affinity, it is recommended that you do not use the OSContext argument for the callback.

In summary, when the following conditions are satisfied, you can guarantee that memory indicated by the OSContext argument of the alarm callback contains the context at the time of alarm interrupt.

Using Multiple Threads on One Core

Demo: Executing Multiple Threads on One Core

The following demo displays Hello! on the main thread and sub-threads:

$CAFE_ROOT/system/src/demo/os/thread/multithreads_on_1core.c

Basic Thread synchronization

Demo: Demonstrate Sleeping/Waking Threads on One Core

The following demo shows how to control the thread behavior using OSSleep and OSWakeupThread:

$CAFE_ROOT/system/src/demo/os/thread/sleep_wake_on_1core.c

Demo: Demonstrate Yielding Between Threads on One Core

The following demo shows how to control the thread behavior using OSYieldThread:

$CAFE_ROOT/system/src/demo/os/thread/yield_on_1core.c



Revision History

2013/08/06 Removed references to older SDKs.
2013/05/08 Automated cleanup pass.
2013/03/20 Converted to HTML.


CONFIDENTIAL