INtime SDK Help
Threads
INtime SDK v6 > About INtime > INtime Kernel > Threads

Overview

Threads, or threads of execution, are the active, code-executing objects in a system.

Threads typically respond to external interrupts or internal events. External interrupts include events such as a keystroke, a system clock tick, or any other hardware-based event. Internal events include events such as the arrival of a message at a mailbox. Threads have both a priority and an execution state, whether the thread is running or not.

There are system calls to create and delete threads, view and manipulate a thread's priority, control thread readiness, and obtain thread handles.

A thread runs a sequence of instructions that manipulate data and objects. Threads typically do the system's work. Threads are the active portion of a process.

Each process starts with an initial thread. Usually a process contains multiple threads. The initial thread, created by the RT Application Loader, initializes the process.

Place threads in the same process when they:

No hierarchy exists among RT threads: all threads in a process belong to the process, even if one thread created the others. All objects in a process belong to the process, not to the threads that created them.

The executable part of a thread is a procedure with a single parameter. A thread makes system calls and can call other procedures. You can write a procedure specifically for one thread, or share it among several threads.

The INtime kernel schedules threads so that each thread sees itself as having its code executed continuously. Depending on the needs of the application, a thread can execute in these or other ways:

Note: Since RT threads preempt Windows thread and interrupt operation, threads of this type are not recommended.

RT thread types

Basic thread types include:

Type RT thread function
Initial Initializes the process environment and creates one or more threads for the process. It is created by the RT Application Loader and is the first thread to run in a new process. It can exist for the life of the process, performing housekeeping and other functions, or execute once.
Ordinary Typically responds to internal events. Does the work of the application.
Interrupt Services interrupts.

RT thread attributes

A thread inherits some attributes from its process, such as its exception handler and exception mode. It also has these attributes of its own:

Once you create a thread, the INtime kernel tracks these attributes.

Creating RT threads

When you create a thread using CreateRtThread, the INtime kernel takes resources that it needs (such as memory for a stack) from the parent process. These are the resources you specify when you create a thread:

These system calls create the three thread types:

Thread type System call
Initial RT Application Loader
Ordinary CreateRtThread
Interrupt CreateRtThread followed by SetRtInterruptHandler called from within the new thread, causes it to become an interrupt thread.

Deleting RT threads

When you delete a thread using DeleteRtThread, the thread is disassociated from its process, and any stack space created for it is reclaimed for allocation to new threads. The thread's resources are returned to the parent process. These system calls delete the three thread types:

Thread type System call
Initial DeleteRtThread
Ordinary DeleteRtThread
Interrupt ResetRtInterruptHandler; when an interrupt thread is reset, DeleteRtThread is automatically called.

RT thread execution states

Available states

A thread exists in one of these execution states:



  1. A thread is always created in the ready state.
  2. The thread's instructions execute; only one thread can execute at a time.
  3. The thread voluntarily waits for something to wake it up; it controls the length of time it stays asleep. A thread goes to sleep because:
    • It makes a request that cannot run at once and it waits (forever if necessary).
    • It puts itself to sleep for a specified time. The thread cannot specify sleep forever.
    Note: The sleep state is the most common state for threads waiting for an event. A thread cannot put another thread to sleep.
  4. The sleeping thread is suspended. The suspension depth increases by one each time the thread is suspended. If the thread's sleep time expires first, it enters the suspended state. If the thread resumes first, it enters the sleep state.
  5. The thread postpones execution because it suspended itself or was suspended by another thread. The suspension depth increases by one each time the thread is suspended.

RT thread execution state transitions

As an application runs, a thread often transitions from one execution state to another. A thread in any state except ready cannot run, even if it has the highest priority. You can delete a thread from any state. Creating a thread instantly makes it ready.

Transition Reason
Ready to running The thread becomes ready, has the highest priority of all ready threads and one of these characteristics:
  • A higher priority than the running thread.
  • The running thread is suspended, put in the sleep state, or deleted.
  • The running thread's round-robin time quota expires, and the ready thread is next in the queue.
Running to ready One of these:
  • A higher priority thread becomes ready.
  • The thread uses all its time quota in round-robin scheduling.
Running to asleep One of these:
  • The thread puts itself to sleep for a specified time.
  • The thread requests something that cannot be done immediately and it can wait.
Asleep to ready, or asleep-suspended to suspended One of these:
  • The sleep time expires.
  • The sleep time expires before a request is granted.
  • The request is granted because another thread sends a message and the message is received.
  • The object the thread is waiting at is deleted.
Running to suspended The thread suspends itself.
Ready to suspended or asleep to asleep suspended. The thread is suspended by another thread. The suspension depth increases by one each time the thread suspends.

These system calls may cause execution state transitions:

CatalogRtHandle
CreateRtThread
LookupRtHandle
WaitForRtControl
ReceiveRtData
ReceiveRtHandle
WaitForRtSemaphore
ReleaseRtControl
SendRtData
SendRtHandle
ReleaseRtSemaphore
RtSleep
SuspendRtThread
WaitForRtInterrupt
ResetRtInterruptHandler
SendRtMessage
SendRtMessageRSVP
ReceiveRtMessage
ReceiveRtReply
SetRtThreadPriority
ResumeRtThread
DeleteRtThread

Suspending and resuming RT threads

To avoid problems, ensure that threads use only SuspendRtThread to suspend themselves. Unpredictable results may occur when a thread uses SuspendRtThread to suspend another thread for synchronization. Whenever possible, use a semaphore or mailbox to synchronize threads instead.

Each time you call SuspendRtThread, the suspension depth increases by one. The INtime kernel keeps track of the thread's suspension depth, up to 255. The larger the number of calls made, the greater the depth. When the suspension depth is greater than 0, you must make a corresponding number of ResumeRtThread calls to bring the thread out of suspension. You cannot obtain the suspension depth of a thread from the INtime kernel.

You must make multiple calls to ResumeRtThread from another thread to make a thread ready when the suspension depth is greater than 1. Each time you call ResumeRtThread, the suspension depth decreases by one. You do not need to make the ResumeRtThread calls from the thread that suspended the thread.

Prioritizing RT threads

Available priority

The INtime kernel handles thread scheduling based on priority. What process a thread belongs to has no effect on scheduling. The INtime kernel always executes the highest priority ready thread until it is interrupted, preempted by a higher priority ready thread, puts itself to sleep, suspends itself, or completes and deletes itself.

RT thread priority

The priority of a thread determines its importance in relation to other threads and interrupts. You specify a thread's static priority when you create it or later, if you need to, using SetRtThreadPriority. The thread's priority can be adjusted by the INtime kernel when using a region (described later); this is called dynamic priority. The priority is an integer value from 0 through 254, with 0 as the highest priority.

Note: The values of the static and dynamic priorities may be read at any time by calling GetRtThreadInfo
Range Used for
0-130 Used by the INtime kernel for servicing external interrupts.

Let the INtime kernel assign these levels to interrupt threads, based on the order you attached your external interrupt sources to the PICs.

In general, don't create threads in this range. A thread that runs in this range masks one or more interrupt, meaning response time to external interrupts is slower and interrupts may be lost.

131-253 Use for threads that handle internal events, like message passing and computation. Typically, you don't assign a thread to every level in this range.

You might put important threads, such as mailbox managers, in the range 140-159. Leave some gaps if you plan to add features and threads later on.

Round-robin scheduling begins, by default, at 141.

Interrupt RT thread priority

You create interrupt threads using CreateRtThread, assign any priority acceptable for the thread's process, then associate the thread to an interrupt handler using SetRtInterruptHandler with a byMaxInt value other than 0 (zero).

You may need to use SetRtProcessMaxPriority if the priority of the interrupt thread (as set by the RT kernel) will be higher (numerically larger) than the process' current maximum priority.

See also: About Interrupts

Round-robin scheduling

If you assign the same priority to more than one thread, the first thread that runs may continue running indefinitely. Use round-robin scheduling to ensure that each thread gets an opportunity to run.

To set up round-robin scheduling, set these parameters using the INtime Configuration utility:

Note:   The current values of both static and dynamic priority may be obtained from the GetRtThreadInfo system call.

By default, the threshold priority below which round-robin scheduling occurs, is set at 140 and the amount of time each thread gets to run is 50 milliseconds.


This illustrates round-robin scheduling and the priority threshold:




  1. At or above the priority threshold, no round-robin scheduling occurs.
  2. Below (numerically above) the priority threshold, round-robin scheduling automatically occurs between threads of equal priority.

Note: the minimum thread priority is 254 on INtime for Windows systems


This shows how round-robin scheduling works with priority-based scheduling.

The priority threshold in the figure is 200. The standard clock "tick" is 10ms and the default round-robin interval is 50ms. There are threads A, B, C, and D with these priorities in the figure:

Priority RT threads
130 Thread C
140 Thread D
200 Threads A and B

System accounting

Several system calls allow you to check thread state, the CPU, and other high-level system information. These calls can be useful at any time but are particularly useful after exceptions occur. You can use these calls to:

Enable and disable CPU tracking
Use SetRtSystemAccountingMode to enable or disable tracking of CPU usage by the operating system. Accounting must be enabled to use GetRtThreadAccounting.
Return information about a thread
Use GetRtThreadInfo to return high-level information such as thread priority, exception handler, the containing process, and execution state. For a more detailed look at the state of a thread, use GetRtThreadState. This call returns information about the state of any thread in the system, including such items as the execution state and the CPU registers for the thread's execution context.
Note:   CPU context is available only for threads suspended by the system that encountered a hardware exception.
Return thread creation and duration statistics
Use GetRtThreadAccounting to find out when a thread was created and how long it has run. This call can be useful in debugging a system when exceptions cause a thread to be suspended.

Communicating between RT threads

Communication methods

RT threads communicate with each other to exchange data and synchronize execution. The INtime kernel provides these exchange objects used to exchange data and synchronize threads:

See also: Developing INtime applications

For threads to share exchange objects, you must create them, then catalog them from the creating thread using CatalogRtHandle. Then other threads can use LookupRtHandle to get the object's RT Handle so they can access the object.

Using mailboxes

RT threads commonly use mailboxes to request a service from another thread. The client thread sends a message that specifies parameters for the server call; the service thread receives the message and provides the specified server. The server thread can return results of the server, if any, to the client using a mailbox.

Advantages and disadvantages of mailboxes

Two types of mailboxes are available for communications between threads: object mailboxes and data mailboxes. Object mailboxes are used to pass 2 byte object handles from thread to thread. Data mailboxes are used to pass up to 128 bytes of data from thread to thread. The data mailbox, however, has some disadvantages. A message arriving at a mailbox where no thread waits is copied by the RT kernel into buffer space in the mailbox message queue; this is the first copy. When a thread arrives to receive the message, the message is copied by the INtime kernel from the mailbox queue into the thread's message buffer; this is the second copy. There is no copying with object mailboxes, which pass only RT handles.

Note: consider using Message Queues to overcome some of the disadvantages of data mailboxes

Using semaphores and regions

Both semaphores and regions provide mutual exclusion to shared resources.

You can use a semaphore with more than one unit as a general purpose counter to synchronize the actions of multiple threads. A semaphore with one unit can also provide mutual exclusion of threads, but without the dynamic priority adjustment and deletion protection provided by regions. Semaphores do not enforce synchronization or mutual exclusion. Semaphores provide more flexibility in waiting for access to resources than regions because you can specify a time limit for waiting in the thread queue. A thread using a region cannot set a time limit.

RT threads use regions to enforce mutual exclusion to a specific resource or data. Only one thread at a time can control a region. The thread that controls the region cannot be deleted or suspended. If the region has a priority-based thread queue, the thread in the region has its priority dynamically adjusted making its priority always at least as high as the highest priority thread waiting in the queue. When a thread gains control of several regions, then gives up control of those regions one at a time, the thread's dynamic priority does not readjust to its static priority until the thread gives up control of the last region; this improves performance.

Thread queues

If a thread makes a request that cannot be filled immediately and the thread can wait, the thread stops executing, goes into a thread queue, and goes to sleep. More than one thread can wait in a queue. Using one of these queue methods when you create the exchange object, you specify how the RT kernel places threads in the queue.

You specify the maximum length of time the thread can wait in the queue for an event.

Besides the thread queues maintained by all exchange objects, mailboxes also have message queues to hold incoming messages for threads. Message queues are always FIFO-based.

System calls

This lists common thread tasks and the system calls that perform the operations.

To . . . Use this system call . . .
Create a thread CreateRtThread
Delete a thread DeleteRtThread
Put a thread to sleep RtSleep
RtSleepEx
Suspend thread SuspendRtThread
Resume a thread ResumeRtThread
Return information about the specified thread's stack use GetRtStackInfo
Modify a thread's priority GetRtThreadPriority
SetRtThreadPriority
Obtain a specific handle GetRtThreadHandles
Dynamically change the maximum priority of threads in a process SetRtProcessMaxPriority
Manage and retrieve thread accounting information GetRtThreadAccounting
SetRtSystemAccountingMode
Get high-level thread information GetRtThreadInfo
Get CPU information GetRtThreadState

This shows the order to make thread system calls and lists calls that threads frequently use:



  1. Make these calls from the thread that needs to create the new thread.
  2. Make these calls if the new thread is an interrupt thread.
  3. Make the GetRtThreadHandles and LookupRtHandle calls from the new thread to obtain RT handles for other processes, threads and objects in the system. Make RtSleep call if the thread needs to wait. Make the SuspendRtThread call from a thread that has completed and no longer needs to run. Make the ResumeRtThread call from another thread. Use calls that create, catalog, manipulate, and delete objects.
  4. Make these calls from the new thread to change its own or another thread's priority.
  5. Make this call from the thread that created the thread.

 

See Also