• No results found

Multitasking .8 Shared Code and Reentrancy

Static Libraries, Shared Libraries, and Plug-Ins

INCLUDE_CPLUS_DEMANGLER

6 Multitasking .8 Shared Code and Reentrancy

Shared code must be reentrant. A subroutine is reentrant if a single copy of the routine can be called from several task contexts simultaneously without conflict.

Such conflict typically occurs when a subroutine modifies global or static variables, because there is only a single copy of the data and code. A routine’s references to such variables can overlap and interfere in invocations from different task contexts.

Most routines in VxWorks are reentrant. However, you should assume that any routine someName( ) is not reentrant if there is a corresponding routine named someName_r( ) — the latter is provided as a reentrant version of the routine. For example, because ldiv( ) has a corresponding routine ldiv_r( ), you can assume that ldiv( ) is not reentrant.

The majority of VxWorks routines use the following reentrancy techniques:

– dynamic stack variables

– global and static variables guarded by semaphores

Wind River recommends applying these same techniques when writing application code that can be called from several task contexts simultaneously.

6.8.1 Dynamic Stack Variables

Many subroutines are pure code, having no data of their own except dynamic stack variables. They work exclusively on data provided by the caller as parameters. The linked-list library, lstLib, is a good example of this. Its routines operate on lists and nodes provided by the caller in each subroutine call.

Subroutines of this kind are inherently reentrant. Multiple tasks can use such routines simultaneously, without interfering with each other, because each task does indeed have its own stack. See Figure 6-5.

Figure 6-4 Shared Code

NOTE: Initialization routines should be callable multiple times, even if logically they should only be called once. As a rule, routines should avoid static variables that keep state information. Initialization routines are an exception; using a static variable that returns the success or failure of the original initialization routine call is appropriate.

6.8.2 Guarded Global and Static Variables

Some libraries encapsulate access to common data. This kind of library requires some caution because the routines are not inherently reentrant. Multiple tasks simultaneously invoking the routines in the library might interfere with access to common variables. Such libraries must be made explicitly reentrant by providing a mutual-exclusion mechanism to prohibit tasks from simultaneously executing critical sections of code. The usual mutual-exclusion mechanism is the mutex semaphore facility provided by semMLib and described in 7.4.4 Mutual-Exclusion Semaphores, p.130.

6.8.3 Task-Specific Variables

Task-specific variables can be used to ensure that shared code is reentrant by providing task-specific variables of the same name that are located in each task’s stack, instead of a standard global or static variables. Each task thereby has its own unique copy of the data item.This allows, for example, several tasks to reference a private buffer of memory and while referring to it with the same global variable name.

Also note that each task has a VxWorks events register, which receives events sent from other tasks, ISRs, semaphores, or message queues. See 7.7 VxWorks Events,

Figure 6-5 Stack Variables and Shared Code

TASKS TASK STACKS COMMON SUBROUTINE

...

NOTE: The __thread storage class variables can be used for both UP and SMP configurations of VxWorks, and Wind River recommends their use in both cases as the best method of providing task-specific variables. The taskVarLib and tlsOldLib (formerly tlsLib) facilities—for the kernel-space and user-space respectively—are not compatible with VxWorks SMP. They are now obsolete and will be removed from a future release of VxWorks. In addition to being

incompatible with VxWorks SMP, the taskVarLib and tlsOldLib facilities increase task context switch times. For information about migration, see the VxWorks Kernel Programmer’s Guide: VxWorks SMP.

6 Multitasking 6.8 Shared Code and Reentrancy

p.143 for more information about this register, and the routines used to interact with it.

Thread-Local Variables: __thread Storage Class

Thread-local storage is a compiler facility that allows for allocation of a variable such that there are unique instances of the variable for each thread (or task, in VxWorks terms).

Configure VxWorks with the INLCUDE_TLS component for thread-local storage support.

The __thread storage class instructs the compiler to make the defined variable a thread-local variable. This means one instance of the variable is created for every task in the system. The compiler key word is used as follows:

__thread int i;

extern __thread struct state s;

static __thread char *p;

The __thread specifier may be used alone, with the extern or static specifiers, but with no other storage class specifier. When used with extern or static, __thread must appear immediately after the other storage class specifier.

The __thread specifier may be applied to any global, file-scoped static,

function-scoped static, or static data member of a class. It may not be applied to block-scoped automatic or non-static data member.

When the address-of operator is applied to a thread-local variable, it is evaluated at run-time and returns the address of the current task’s instance of that variable.

The address may be used by any task. When a task terminates, any pointers to thread-local variables in that task become invalid.

No static initialization may refer to the address of a thread-local variable.

In C++, if an initializer is present for a thread-local variable, it must be a constant-expression, as defined in 5.19.2 of the ANSI/ISO C++ standard.

For more information see the tlsLib entry in the VxWorks API reference.

tlsOldLib and Task Variables

VxWorks provides the task-local storage (TLS) facility and the routines provided by tlsOldLib (formerly tlsLib) to maintain information on a per-task basis in RTPs.

! CAUTION: Do not access __thread variables from an ISR. Doing so may lead to unpredictable results.

NOTE: Wind River does not recommend using the tlsOldLib facility, which is maintained primarily for backwards-compatibility. Use thread-local (__thread) storage class variables instead.

6.8.4 Multiple Tasks with the Same Main Routine

With VxWorks, it is possible to spawn several tasks with the same main routine.

Each spawn creates a new task with its own stack and context. Each spawn can also pass the main routine different parameters to the new task. In this case, the same rules of reentrancy described in 6.8.3 Task-Specific Variables, p.120 apply to the entire task.

This is useful when the same function must be performed concurrently with different sets of parameters. For example, a routine that monitors a particular kind of equipment might be spawned several times to monitor several different pieces of that equipment. The arguments to the main routine could indicate which particular piece of equipment the task is to monitor.

In Figure 6-6, multiple joints of the mechanical arm use the same code. The tasks manipulating the joints invoke joint( ). The joint number (jointNum) is used to indicate which joint on the arm to manipulate.

Figure 6-6 Multiple Tasks Utilizing Same Code

joint_1

joint_2

joint_3

joint (

int jointNum )

{

/* joint code here */

}

7

Intertask and Interprocess