Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

Reentrant

From Texas Instruments Wiki
Jump to: navigation, search

A function is reentrant if it can safely be invoked again while another instance of the function is active.

Generally, a function is reentrant if it doesn't access the program state.

Terminology

The program state is the value of all of memory, especially global variables. The program state also includes the function call stack, as well as the dynamic memory pool.

A thread is a single flow of control through a program. It has its own stack. In a single-threaded environment, there is only one thread and one stack. The C standard assumes a single-threaded environment. A multi-threaded environment has several threads, each with its own stack. See thread-safe for a discussion of what it means to have multiple threads.

A function is reentrant if it can safely be invoked again while another instance of the function is active. This is usually due to a recursive function, but could be due to an interrupt or signal. A reentrant function is not necessarily thread-safe, interrupt-safe, or signal-safe, or vice versa.

Entering a function

When a function is called (entered), a stack frame (activation record) is pushed onto the function call stack. The frame on top of the stack represents the state of the currently executing function. The other frames on the stack represent the state of functions that have been entered but have not finished; they were suspended by a function call, interrupt, or something similar. Each frame represents an instance of a function that has been called but has not yet returned.

Reentering a function

If there are two frames for one function on the stack, that function has been reentered. This means that somehow during execution of that function, or one of the functions it calls, something called the function again. This is typically due to recursion, but can happen during an interrupt or context switch.

If the function is not re-entrant, your data could get corrupted. It may not be obvious that a function has been reentered; the reentrant call could be several call layers deep or in an interrupt function.

In a single-threaded environment, a function could be re-entered in several ways:

  • a recursive function call
  • an interrupt or trap handler
  • a signal handler
  • a C++ exception

These cases are all conceptually just like a function call. A new frame is pushed onto the stack for the called function, interrupt handler, signal handler, or exception handler.

Non-reentrant functions in the RTS (run-time support library)

Any function which accesses non-const program state is not reentrant. Program state is static or global data, such as errno, the C I/O buffer, the stack, the dynamic memory pool, and external objects such as files. Additionally, functions which call non-reentrant functions are non-reentrant.

The compiler run-time support library has static and global data. Here is a non-exhaustive list of this data:

  • errno
  • the C I/O buffer
  • the dynamic allocation heap
  • the function call stack
  • the atexit registered function list
  • the temporary file name list
  • the asctime return value buffer
  • the asctime/strftime format buffer
  • the random number generator seed
  • the currently-registered C++ new handler

Here are some examples of functions which access errno:

  • perror, strerror
  • asin, acos, log, sqrt, etc
  • fgetpos, ftell
  • strtod, strtol

Here are some examples of functions which access the C I/O buffer, or access other global state in the C I/O subsystem:

  • the entire printf family except sprintf
  • the entire scanf family except sscanf
  • the entire puts family
  • the entire putc family
  • fopen, freopen, fclose, tmpfile
  • fgetpos, fsetpos, ftell, feof, rewind
  • clearerr, ferror, perror
  • fopen, fread, fwrite, fseek, fclose
  • open, read, write, lseek, close, unlink, rename
  • HOSTopen, HOSTclose, HOSTread, HOSTwrite, HOSTlseek, HOSTunlink, HOSTrename, HOSTtime, HOSTclock
  • tabinit, add_device, remove_device
  • clock, time, getenv
  • setbuf, setvbuf
  • assert

Here are some examples of functions that access the dynamic allocation heap:

  • the entire *alloc family including free
  • operators new and delete
  • setbuf, setvbuf
  • minit, memalign, memmap (on targets which have them)

Unsafe examples

The printf family of functions is not reentrant because these functions modify the I/O buffer, which is global state. It is not safe to call printf from within a function which could be called during the execution of printf or any of its descendants. An interrupt could occur at any time, including during execution of printf, so it is not safe to call printf from within an interrupt handler:

interrupt void handler(void)
{
    printf("in handler\n"); /* unsafe */
}

The strtok function is non-reentrant because proper use of it relies on global state maintained from call to call. It is not safe to nest calls to strtok.

#include <stdio.h>
#include <string.h>
 
int main()
{
    char input[] = "a=b:c=d";
 
    char *colon, *equal;
 
    for (colon = strtok(input, ":"); 
         colon = strtok(NULL,  ":"); )
    {
        printf("[%s]\n", colon);
 
        for (equal = strtok(colon, "="); 
             equal = strtok(NULL,  "="); )
            printf("[%s]\n", equal);
    }
 
    return 0;
}

You should not create any C++ class objects in an interrupt function, because they typically require dynamic allocation, and the interrupt may have occurred during some dynamic memory allocation function:

interrupt void handler(void)
{
    class thing; /* this may require dynamic allocation! */
}

If you use C++ exceptions, do not throw an exception out of a destructor. If you throw from a destructor while an exception is being handled, the program will abort; C++ destructors are not reentrant.

Non-reentrant, but potentially safe examples

A function which is technically non-reentrant might still be safe to call if your program doesn't care about the side effects.

For example, any function which could set errno is non-reentrant, but if the program always ignores errno, it doesn't matter.

Code which uses dynamic memory allocation is not strictly reentrant, but if the following conditions are true, we don't care that it's technically non-reentrant:

  • the program cannot reenter any dynamic memory allocation function, because
    • the program is a single-threaded environment
    • the program's interrupt functions never call any dynamic memory allocation function (checked recursively)
  • there is enough memory to satisfy all allocations
  • the program doesn't care about exactly what the heap looks like (no pointer comparisons between alloc'd objects)
void allocate_and_work(void)
{
    char * mem = malloc(4);
    if (mem) { work(); free(mem); }
}

How do I avoid a problem?

In a single-threaded environment, the following simple tips will avoid most problems:

  • Don't call any functions from an interrupt handler function unless you know that the function is reentrant.
  • If you use C++ exceptions, don't call any functions from a C++ destructor unless you know that the function is reentrant.
  • If you don't know whether a function is reentrant, assume that it is non-reentrant.
  • Don't overlap calls to strtok.

Note that the C++ destructors sometimes de-allocate memory, which is OK, because the destructor will not generally be invoked during execution of a C dynamic memory allocation function.

other

Things to consider:

  • C++ exception handling
  • volatile
  • setjmp/longjmp
  • heap
  • thread-local storage
  • multiple cores
  • shared memory
  • dynamic linking
  • critical sections (lock, mutex)
  • atomic accesses

From the page Before asking for DSP/BIOS support:

Despite being written in C, DSP/BIOS can interface with C++ - check out the bigtime example. However avoid using the C++ STL exception handling (try/throw/catch calls) -- they are not thread-safe. Check the last topic of this page.

Special:WhatLinksHere/Reentrant