ADAMS_DECLARE_THREADSAFE

A call to the ADAMS_DECLARE_THREADSAFE subroutine informs Adams Solver (C++) that the user-written subroutine is threadsafe and that multiple threads can execute this user-written subroutine simultaneously.

Use

Called By

Any user-written subroutine

Prerequisite

None

Calling Sequence

CALL ADAMS_DECLARE_THREADSAFE ()

Input Arguments

None

Output Arguments

None

Extended Definition

Adams Solver (C++) includes the option of threaded (parallel) execution. That is, multiple threads are permitted to execute portions of the code simultaneously. On machines with multiple processors, this can result in a significant reduction in the wall time required for a simulation to complete.
This parallel capability places restrictions on the way the code is written to ensure that different threads do not adversely interact with each other. Code that is written in such a way is threadsafe. Care has been taken to ensure that relevant portions of Adams Solver (C++) are threadsafe, but no such assumption can be made about user-written subroutines. Consequently, by default, all user-written subroutines are executed serially. By default, no user-supplied subroutine will take advantage of the parallel capabilities of Adams Solver (C++).
Because complex models often use user-written subroutines extensively, it can greatly improve performance to execute these in parallel, as well. If this is to be done, you must ensure that the user-written subroutine and its dependants are threadsafe. If so, the user-written subroutine can indicate this with a call to ADAMS_DECLARE_THREADSAFE. If the user-written subroutine making the call is written in FORTRAN and the input parameter IFLAG is declared LOGICAL, then the call to ADAMS_DECLARE_THREADSAFE must be done when IFLAG is TRUE. If the input parameter is IGLAF declared INTEGER, then the call to ADAMS_DECLARE_THREADSAFE should be made when IFLAG=1 (initialization). If the user-written subroutine making the call is written in C/C++, the input parameter IFLAG is declared as int (integer) and the call to ADAMS_DECLARE_THREADSAFE should be done when IFLAG==1 (initialization). Additional calls done when IFLAG is different than zero will cause no errors.
If a user-written subroutine makes a call to ADAMS_DECLARE_THREADSAFE, then Adams Solver (C++) permits threads to make simultaneous calls to that user-written subroutine. For more information, see the PREFERENCES statement.

Writing Threadsafe Code

While a complete guide to writing parallel code is left to other texts, a few general points are covered here.
Code can be made thread-safe by eliminating any possibility for data (program variables) to be shared between different threads. For example, if all variables in a user-written subroutine were either passed in as arguments or are dynamically allocated local variables, then that user-written subroutine would be threadsafe.
Coding in such a manner, however, is often not practical. There may be a need for a user-written subroutine to access constant tabular data or other parameters, which is most efficiently implemented as global data and shared by multiple threads. To allow for this, during subroutine initialization, all user-written subroutines are run serially. In addition, calls to REQSUBs are always performed serially.

General Guidelines

1. The only data that can be modified at any time is:
Dynamically allocated local variables.
Static or global variable that are uniquely indexed so there is no possibility that two threads will modify them simultaneously. For example, a user-written subroutine with a static array of data could safely modify the array element that is uniquely mapped to the ID of the current modeling element being evaluated. Because each thread is evaluating a different modeling element, the threads will not access the same array element at the same time.
2. All data that will be shared by multiple threads must be allocated and set during subroutine initialization. At other times, this data may be read, but must not be written to.
3. No special coding practices need to be followed inside a REQSUB.

FORTRAN-Specific Guidelines

1. Some FORTRAN compilers (such as Digital/Compaq Visual FORTRAN) statically allocate local variables by default. This is avoided by using a compiler option (/automatic) that causes all local variables to be allocated on the stack (unless the SAVE statement is used).
2. Avoid or minimize the use of data in COMMON blocks. Such data is statically allocated and shared between threads. As stated in the General Guidelines above, such data must either be written during the initialization phase only or uniquely indexed.
3. Avoid or minimize the use of SAVE statements. Again, this data is statically allocated and must either be written during the initialization phase only or uniquely indexed.
 
Notes:  
There is no way to specify, ensure, or verify that any modeling element be evaluated by any particular thread.

The use of synchronization constructs within the user-written subroutines themselves is not recommended. Because user-written subroutines are called so frequently, it is likely that the overhead of the synchronization will exceed the benefit of parallelization.

Argument IFLAG should be declared of type INTEGER when using FORTRAN.

The calls to ADAMS_DECLARE_THREADSAFE takes effect only if the value of NTHREADS is bigger than one. See the PREFERENCES statement.