www.ti.com
Threads and Reentrancy
The definition of reentrant code often implies that the code does not retain "state" information. That is, if you invoke the code with the same data at different times, by the same or other thread, it will yield the same results. This is not always true, however. How can a function maintain state and still be reentrant? Consider the rand() function. Perhaps a better example is a function with state that protects that state by disabling scheduling around its critical sections. These examples illustrate some of the subtleties of reentrant programming.
The property of being reentrant is a function of the threading model; after all, before you can determine whether multiple threads can use a particular function, you must know what types of threads are possible in a system. For example, if threads are not preemptive, a function may freely use global variables if it uses them for scratch storage only; i.e., it does not assume these variables have any values upon entry to the function. In a preemptive environment, however, use of these global variables must be protected by a critical section or they must be part of the context of every thread that uses them.
Although there are exceptions, reentrancy usually requires that algorithms:
∙only modify data on the stack or in an instance "object"
∙treat global and static variables as
∙never employ self modifying code
These rules can sometimes be relaxed by disabling all interrupts (and therefore, disabling all thread scheduling) around the critical sections that violate the rules above. Since algorithms are not permitted to directly manipulate the interrupt state of the processor, the allowed DSP/BIOS HWI module functions (or equivalent implementations) must be called to create these critical sections.
Rule 2
All algorithms must be reentrant within a preemptive environment (including
2.2.4 Example
In the remainder of this section we consider several implementations of a simple algorithm, digital filtering of an input speech data stream, and show how the algorithm can be made reentrant and maintain acceptable levels of performance. It is important to note that, although these examples are written in C, the principles and techniques apply equally well to assembly language implementations.
Speech signals are often passed through a
13
yn + xn * xn*1 ) 32 xn*2
The following implementation is not reentrant because it references and updates the global variables z0 and z1. Even in a
int z0 = 0, z1 = 0; /* previous input values */
void PRE_filter(int input[], int length)
{
int I, tmp;
for (I = 0; I < length; I++) {
tmp = input[i] - z0 + (13 * z1 + 16) / 32; z1 = z0;
z0 = input[i]; input[i] = tmp;
}
}
We can make this function reentrant by requiring the caller to supply previous values as arguments to the function. This way, PRE_filter1 no longer references any global data and can be used, therefore, on any number of distinct input data streams.
18 | General Programming Guidelines | SPRU352G |