Dealing with non-reentrant functions in RTOS-based designs

This article is written by Jean J. Labrosse, RTOS Expert.

Reentrant vs Non-reentrant functions:

If shared functions only use local variables, they are generally reentrant (assuming the compiler itself generates reentrant code).  

Conversely, a non-reentrant function is one that uses one or more global variables to keep track of the state of the function. The online documentation for the IAR toolchain provides a list of non-reentrant functions as shown in Figure 1.


Figure 1,
List of non-reentrant functions provided bythe IAR STD library

A good example of a non-reentrant function can be found in the DLIB run-time C library, string.h: strtok().  This function is used to parse an ASCII string for “tokens”.  The description of strtok() from the IAR documentation is shown in Figure 2:

Figure 2, Description of strtok(), a non-reentrant function

  1. The simple (and partial) code example shown in Figure 3 shows the use of strtok(). ptoken is a pointer to the beginning of the next token (i.e. one of the animal names) extracted from string[].
  2. The first time you call strtok(), you specify the ASCII string to parse (i.e. string[]) and a list (could be more than one delimiter) of token delimiters, in this example: “,” (a single comma). The function returns as soon as it locates the first delimiter and replaces that delimiter in string[] with a NULstrtok() then positions an internal pointer at the next character (i.e. the next token) in string[].
  3. You must specify a NULL pointer (I used 0 in this case) as the first argument to the second (and subsequent) calls to strtok(). The second argument contains the token delimiter(s) which, by the way, could be different if you needed to look for other delimiting characters, but in this case, I kept the comma.
  4. Again, to extract the next token, you must specify a NULL pointer (I used 0 in this case) as the first argument to the second (and subsequent) calls to strtok(). The second argument contains the token delimiter(s).
  5. You continue passing the NULL pointer and the desired delimiter(s) until strtok() returns a NULL At that point, there are no more tokens in the string[].  
1)	char *ptoken;
char  string[80] = “Lion,Tiger,Panther”;

2)	ptoken = strtok(string, “,”);
ptoken contains “Lion”
The internal pointer points to the T in Tiger.

3)	ptoken = strtok(0, “,”);
   ptoken contains “Tiger”
The internal pointer points to the P in Panther.

4)	ptoken = strtok(0, “,”);
   ptoken contains “Panther”
   The internal pointer points to the NUL at the end of string[].

5)	ptoken = strtok(0, “,”);
   ptoken contains a NULL pointer because there are no more tokens

Figure 3, Invocation sequence of strtok()

Of course, you would implement strtok() in a loop if you are expecting many tokens.

Using Non-Reentrant functions in an RTOS application:

The whole point of the previous discussion is to emphasize the fact that strtok() (and other non-reentrant functions) keeps an internal pointer that keeps track of the next token.   

As a general rule, I recommend avoiding non-reentrant functions in an RTOS application. However, if you need the functionality provided by some non-reentrant functions, your RTOS application code needs to either:

  1. Implement a reentrant version of the desired non-reentrant function.
  2. ONLY use strtok() (and other non-reentrant functions) from one task. Although, each task can have its own non-reentrant function(s).
  3. A non-reentrant function is a shared resource that needs to be protected using mutex.

The pseudo-code snippet in Figure 4 shows how you would use a mutex to protect access to the non-reentrant function.

// Allocate storage for the mutex

// Initialize the mutex before any functions that uses strtok()


void  MyTask (void) 
{
    // Initialize task variables and I/Os as needed

    while (1) {
        // Wait for the event to wake up ‘MyTask’
        :
        :
        // Get string with tokens
        AcquireMutex(..);
        // Extract token(s) from desired string;
        ReleaseMutex(..);
        :
        :
    } 
}

Figure 4, Ensuring exclusive access to a non-reentrant function

Each task that uses strtok() would need to acquire/release the mutex as shown above.  Alternatively, you could implement a function (i.e. a service to your application code) that parses tokens into an array of tokens.  This function would hide the mutex implementation details and thus allow any task to extract tokens without having to worry about non-reentrancy.

Also note that I specified the use of a mutex because it avoids unbounded priority inversions which can happen if you were to use a semaphore.

About the author

This article is part of a series on the topic of developing applications with RTOS.

Jean Labrosse, Micrium Founder and author of the widely popular uC/OS-II and uC/OS-III kernels, remains actively involved in the evolution of the uC/ line of embedded software.

Given his wealth of experience and deep understanding of the embedded systems market, Jean serves as a principal advisor and consultant to Weston Embedded Solutions helping to chart the future direction of the current RTOS offerings. Weston Embedded Solutions specializes in the support and development of the highly reliable Cesium RTOS family of products that are derived from the Micrium codebase. 

You can contact him at Jean.Labrosse@Weston-Embedded.com.

jean_labrosse.jpg

We do no longer support Internet Explorer. To get the best experience of iar.com, we recommend upgrading to a modern browser such as Chrome or Edge.