2.2.2 Preemptive vs. Non-Preemptive Multitasking
2.2.3 Reentrancy
Threads and Reentrancy
Non-preemptive multitasking relies on each thread to voluntarily relinquish control to the operating system
before letting another thread execute. This is usually done by requiring threads to periodically call an
operating system function, say yield(), to allow another thread to take control of the CPU or by simply
requiring all threads to complete within a specified short period. In a non-preemptive multi-threading
environment, the amount of time a thread is allowed to run is determined by the thread, whereas in a
preemptive environment, the time is determined by the operating system and the entire set of tasks that
are ready to run.
Note that the difference between those two flavors of multi-threading can be a very big one; for example,
under a non-preemptive system, you can safely assume that no other thread executes while a particular
algorithm processes data using on-chip data memory. Under preemptive execution, this is not true; a
thread may be preempted while it is in the middle of processing. Thus, if your application relies on the
assumption that things do not change in the middle of processing some data, it might break under a
preemptive execution scheme.
Since preemptive systems are designed to preserve the state of a preempted thread and restore it when
its execution continues, threads can safely assume that most registers and all of the thread's data memory
remain unchanged. What would cause an application to fail? Any assumptions related to the maximum
amount of time that can elapse between any two instructions, the state of any global system resource
such as a data cache, or the state of a global variable accessed by multiple threads, can cause an
application to fail in a preemptive environment.
Non-preemptive environments incur less overhead and often result in higher performance systems; for
example, data caches are much more effective in non-preemptive systems since each thread can control
when preemption (and therefore, cache flushing) will occur.
On the other hand, non-preemptive environments require that either each thread complete within a
specified maximum amount of time, or explicitly relinquish control of the CPU to the framework (or
operating system) at some minimum periodic rate. By itself, this is not a problem since most DSP threads
are periodic with real-time deadlines. However, this minimum rate is a function of the other threads in the
system and, consequently, non-preemptive threads are not completely independent of one another; they
must be sensitive to the scheduling requirements of the other threads in the system. Thus, systems that
are by their nature multirate and multichannel often require preemption; otherwise, all of the algorithms
used would have to be rewritten whenever a new algorithm is added to the system.
If we want all algorithms to be framework-independent, we must either define a framework-neutral way for
algorithms to relinquish control, or assume that algorithms used in a non-preemptive environment always
complete in less than the required maximum scheduling latency time. Since we require documentation of
worst-case execution times, it is possible for system integrators to quickly determine if an algorithm will
cause a non-preemptive system to violate its scheduling latency requirements. Thus, the TMS320 DSP
Algorithm Standard does not define a framework-neutral "yield" operation for algorithms.
Since algorithms can be used in both preemptive and non-preemptive environments, it is important that all
algorithms be designed to support both. This means that algorithms should minimize the maximum time
that they can delay other algorithms in a non-preemptive system.
Reentrancy is the attribute of a program or routine that allows the same copy of the program or routine to
be used concurrently by two or more threads.
Reentrancy is an extremely valuable property for functions. In multichannel systems, for example, any
function that can be invoked as part of one channel's processing must be reentrant ; otherwise, that
function would not be usable for other channels. In single channel multirate systems, any function that
must be used at two different rates must be reentrant; for example, a general digital filter function used for
both echo cancellation and pre-emphasis for a vocoder. Unfortunately, it is not always easy to determine if
a function is reentrant.
SPRU352G – June 2005 – Revised February 2007 General Programming Guidelines 17
Submit Documentation Feedback