DLIB multi-threaded support

Introduction

In a multi-threaded environment the C/C++ library must handle all library objects differently depending on whether they are global or local. An object can be a true global object, any updates of its state must then be guarded by some locking mechanism to make sure that only one thread can update it at any one time. In which case an object is local to each thread, the static variables containing the object state must reside in a variable area local for the thread. This area is commonly named thread-local storage, TLS.

The DLIB library uses two kinds of locks - system and file stream locks. These library objects are guarded by system locks:

The file streams locks are only needed in the full library configuration.

These library objects needs TLS:

Using multi-threaded support

To use the DLIB libraries multi-threading environment you need to do this (see below for more information about each bullet):

System locks interface

The following interface must be fully implemented for system locks to function in DLIB:


   typedef void *__iar_Rmtx;                   // Lock info object

   void __iar_system_Mtxinit(__iar_Rmtx *);    // Initialize a system lock
   void __iar_system_Mtxdst(__iar_Rmtx *);     // Destroy a system lock
   void __iar_system_Mtxlock(__iar_Rmtx *);    // Lock a system lock
   void __iar_system_Mtxunlock(__iar_Rmtx *);  // Unlock a system lock
   

The lock and unlock implementation must survive nested calls.

File stream locks interface

The following interface may be disregarded if no file streams are used. If file streams are used, either they can be fully implemented or they can be redirected to the system locks interface:


   typedef void *__iar_Rmtx;                   // Lock info object

   void __iar_file_Mtxinit(__iar_Rmtx *);    // Initialize a file lock
   void __iar_file_Mtxdst(__iar_Rmtx *);     // Destroy a file lock
   void __iar_file_Mtxlock(__iar_Rmtx *);    // Lock a file lock
   void __iar_file_Mtxunlock(__iar_Rmtx *);  // Unlock a file lock
   

The lock and unlock implementation must survive nested calls.

TLS handling in DLib

The support for threads in DLIB uses two types of threads:

If you need DLIB to support secondary threads, you must override the function


   void *__iar_dlib_perthread_access(void *symbp). 

Its parameter is the address to the TLS variable that should be accessed, in the main threads TLS area, and it should return the address to the symbol in the current TLS area.

There are two interfaces that can be used for creating and destroying secondary threads. You can use the following interface that allocates a segment on the heap and initializes it. At deallocation it destroys the objects in the segment and then frees it.


   void *__iar_dlib_perthread_allocate(void)
   void  __iar_dlib_perthread_deallocate(void *)

Alternatively, if the application handles the TLS allocation, you can use the following interface for initializing and destroying the objects in the segment:


   void __iar_dlib_perthread_initialize(void *)
   void __iar_dlib_perthread_destroy(void)

that initializes the segment and destroys the objects in the segment respectively.

These macros can be helpful when implementing an interface for creating and destroying secondary threads:

Note that the size needed for TLS variables are dependent on what DLIB resources the program uses.

This is an example of how to create a secondary thread:


  #include <yvals.h>
  void _DLIB_TLS_MEMORY *TLSp = __iar_dlib_perthread_allocate();
                                     // Create thread-local segment
                                     // and store pointer to it in
                                     // TLSp.
  int inUserThread = 0;              // Are we in a user thread?
  

This is an example of how to destruct a secondary thread:


  #include <yvals.h>
  __iar_dlib_perthread_deallocate(TLSp);  // Destroy the thread-local
                                         // segment
  

The access function could look like this:


  void _DLIB_TLS_MEMORY *__iar_dlib_perthread_access(
                                              void _DLIB_TLS_MEMORY *symbp)
  {
    char _DLIB_TLS_MEMORY *p = 0;
    if (inUserThread)
      p = (char _DLIB_TLS_MEMORY *) TLSp;
    else
      p = (void _DLIB_TLS_MEMORY *) __segment_begin("__DLIB_PERTHREAD");
    p += __IAR_DLIB_PERTHREAD_SYMBOL_OFFSET(symbp);
    return (void _DLIB_TLS_MEMORY *) p;
  }
  

The TLSp is unique for each thread, and must be exchanged by the RTOS or the user somehow whenever a thread switch occurs.

Changes to the linker configuration file

For ILINK, the linker normally makes an automatic choice on how to initialize static data. If threads are to be used the main threads TLS area has to be initialized by naive copying. The reason for this is that the initializers are used for each secondary threads TLS area as well. Insert the following statement in the used configuration file.


   initialize by copy with packing = none { section __DLIB_PERTHREAD };