Main Content

Guidelines for Writing Thread-Safe S-Functions

Simulink® lets you run S-functions in parallel with multithreaded programming, which makes simulations run more quickly than serial runs. Multithreaded programming with S-functions requires you to make S-functions thread-safe. Creating thread-safe code involves ensuring that data shared between multiple threads is protected so that data and results are as expected. Simulation with S-functions that are not thread-safe might cause unexpected behavior.

Background

C/C++ S-functions are implemented in C or C++ and built into shared libraries known as MEX files. When an S-function block refers to a shared library, MATLAB® loads the S-function block into the process. When multiple S-function blocks refer to the same shared library, they also refer to the initial shared library copy. This process results in multiple S-function blocks sharing the same data owned by the shared library. Thus, multithreaded S-function blocks access the same data at the same time.

In addition, if these S-functions refer to the same resources, multithreaded S-function blocks can access the same resources (such as files) at the same time, even when the S-function blocks are associated with different S-functions.

Guidelines

An S-function is generally considered thread-safe when it can safely execute concurrently using multiple threads. To designate an S-function as thread-safe, use the ssSetRuntimeThreadSafetyCompliance function. If you are not sure about the thread-safety of your S-function, use these guidelines to investigate and make it thread-safe.

Data Share

DefinitionProblemSolution

S-function refers to data using pointers (for example, ssSetUserData, ssGetUserData, ssSetPWorkValue and ssGetPWorkValue). An S-function easily shares data using these pointers.

Multiple threads can use pointers to access the same data. If the threads try to write to the same memory location at the same time, they violate thread-safety. Concurrent reads from multiple threads are safe as long as there are no writes before, during, or after the reads, which can cause incoherent caches.

Be cautious when accessing data shared by multiple threads.

  • Make the data constant if it is read-only.

  • To control access to data, consider using mutexes. Mutexes can also ensure cache coherence.

Global Variables

DefinitionProblemSolution

Global variables are shared data accessible throughout an application.

Multiple threads writing to non-protected shared data is not safe. Reading is safe as long as there are no writes before, during, or after the reads, which can cause incoherent caches.

  • If you are writing to global data, localize it, control access to it using mutexes, or change the semantics of your algorithm. To ensure cache coherence, also consider using memory fences such as atomic or mutex.

  • If you are reading, make your global variable constant.

Local Static Variables Initialization

DefinitionProblemSolution

Local static variables are stored in one location.

  • In C++, local static variables are initialized upon entering the function scope and are not thread-safe.

  • In C, local static variables are initialized at the start of the application as long as the value on the right of the assignment (the value being assigned into the variable) is compile-time constant. These variables are thread-safe.

If multiple threads enter the function scope at the same time, the software makes multiple attempts to write to the same location. This issue holds even if the local static variable is constant.

  • If you are using a C++ version prior to C++11, protect the initialization of local static variable using a mutex or thread-safe initialization mechanism, such as std::call_once or boost::call_once.

  • If you are using C++11 or later, local static variable initialization is guaranteed to be thread-safe.

Resources

DefinitionProblemSolution

Resources are entities that are explicitly requested from and returned to the system. Some examples of resources include dynamically allocated memory, files, database connections, and network sockets. Your application might need to manage resources.

Accessing resources from multiple threads might not be thread-safe, such as reading and writing to a file from multiple threads. Even if these operations are thread-safe, they might not produce the expected results.

Be cautious when managing a resource. Thread-safety of a resource depends on its implementation. For more information about thread-safety specifications, see the resource documentation. Optionally, you can guard access to the resource using a mechanism such as a mutex.

Reentrancy

DefinitionProblemSolution

A function is reentrant if it is safe to call multiple times from the same thread (recursively). For example, the strtok function is not reentrant because it keeps some internal state of the string to be tokenized. A function is reentrant if it does not call nonreentrant functions.

Calling a nonreentrant function from multiple threads might not be safe.

Make your function reentrant. For example:

  • Eliminate the states the function holds.

  • Replace nonreentrant functions calls with reentrant equivalents. For example, replace strtok with strtok_r.

mexCallMATLAB

DefinitionProblemSolution

An S-function might call MATLAB using the mexCallMATLAB function.

Simulink code that handles mexCallMATLAB functionality is not thread-safe.

Do not call mexCallMATLAB in your S-function.

Exception Free Code

DefinitionProblemSolution

An S-function is exception free as long as none of its subroutines, when called, has the potential of long jumping. For more information about exception free S-functions, see Exception Free Code.

When an S-function is not exception free, its subroutines are indirectly called through mexCallMATLAB, which is not thread-safe (see mexCallMATLAB).

Examine your S-function for long jumps. If there are none, mark the S-function as exception free using the ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE) function. If the S-function long jumps despite this flag, unpredictable behavior occurs.

If an S-function throws an exception but uses a try/catch block to catch the exception, that S-function is safe.

Data Race

DefinitionProblemSolution

Data race occurs when the output of your application depends on the order of execution such that the behavior of your application changes between executions.

The application might have unexpected behavior.

Consider one of the following:

  • Revise your algorithm to eliminate data races.

  • Use locks to control the order of execution in critical parts of your code, or make critical operations atomic.

Volatile

DefinitionProblemSolution

The volatile keyword tells the compiler not to optimize a variable because its value might be changed by some mechanism that the compiler is not aware of.

Applications might mistakenly use volatile to achieve thread-safety. volatile does not provide atomicity or synchronization among threads.

Do not use the volatile keyword for thread-safety.

Error Status

DefinitionProblemSolution

S-functions use ssSetErrorStatus, ssGetErrorStatus, ssSetLocalErrorStatus, and ssGetLocalErrorStatus to access error status.

ssSetErrorStatus and ssGetErrorStatus are not thread-safe. These functions can overwrite existing errors and cause reporting of inaccurate errors. For example, Block A might report the error thrown by Block B.

Use the thread-safe ssSetLocalErrorStatus, and ssGetLocalErrorStatus functions. Do not use ssSetErrorStatus and ssGetErrorStatus.

See Also

|

Related Topics