Difference between revisions of "Thread-Local Storage"

From SEGGER Wiki
Jump to: navigation, search
(Created page with "Thread-Local Storage (TLS) enables the use of local and global variables to be unique to a thread. The most popular thread-local variable is `errno`. == Thread-local variable...")
 
(Linker configuration)
Line 45: Line 45:
   
 
To resolve this warning, define the block which contains tbss and tdata with fixed order.
 
To resolve this warning, define the block which contains tbss and tdata with fixed order.
  +
  +
=== GNU Linker ===
  +
  +
With the GNU Linker thread-local data and thread-local bss need to be placed with fixed order and layout, too.
  +
  +
In Embedded Studio the section placement file can take care of this:
  +
  +
<MemorySegment name="$(FLASH_NAME:FLASH);FLASH1">
  +
...
  +
<ProgramSection alignment="4" load="Yes" runin=".data_run" name=".data" />
  +
<ProgramSection alignment="4" load="Yes" runin=".tdata_run" name=".tdata" />
  +
...
  +
</MemorySegment>
  +
<MemorySegment name="$(RAM_NAME:RAM);SRAM;RAM1">
  +
...
  +
<ProgramSection alignment="4" load="No" name=".data_run" />
  +
<ProgramSection alignment="4" load="No" name=".bss" />
  +
<ProgramSection alignment="4" load="No" name=".tbss" />
  +
<ProgramSection alignment="4" load="No" name=".tdata_run" />
  +
...
  +
</MemorySegment>
  +
  +
'''Note:''' The default *placement.xml in Embedded Studio prior to version 6.30 created mixed TLS and non-TLS sections and might need to be updated in existing projects.
  +
  +
In a manually created linker script the ordering can look like this:
  +
  +
__tbss_load_start__ = ALIGN(__bss_end__ , 4);
  +
.tbss ALIGN(__bss_end__ , 4) (NOLOAD) : AT(ALIGN(__bss_end__ , 4))
  +
{
  +
__tbss_start__ = .;
  +
*(.tbss .tbss.*)
  +
}
  +
__tbss_end__ = __tbss_start__ + SIZEOF(.tbss);
  +
__tbss_size__ = SIZEOF(.tbss);
  +
__tbss_load_end__ = __tbss_end__;
  +
  +
__tdata_load_start__ = ALIGN(__data_load_start__ + SIZEOF(.data) , 4);
  +
.tdata ALIGN(__tbss_end__ , 4) : AT(ALIGN(__data_load_start__ + SIZEOF(.data) , 4))
  +
{
  +
__tdata_start__ = .;
  +
*(.tdata .tdata.*)
  +
}
  +
__tdata_end__ = __tdata_start__ + SIZEOF(.tdata);
  +
__tdata_size__ = SIZEOF(.tdata);
  +
__tdata_load_end__ = __tdata_load_start__ + SIZEOF(.tdata);
  +
  +
.tdata_run ALIGN(__tbss_end__ , 4) (NOLOAD) :
  +
{
  +
__tdata_run_start__ = .;
  +
}
  +
__tdata_run_end__ = __tdata_run_start__ + SIZEOF(.tdata);
  +
__tdata_run_size__ = __tdata_run_end__ - __tdata_run_start__;
  +
__tdata_run_load_end__ = __tdata_run_end__;
  +
  +
Other section ordering might lead to the error message "TLS sections are not adjacent:"

Revision as of 18:21, 16 January 2023

Thread-Local Storage (TLS) enables the use of local and global variables to be unique to a thread. The most popular thread-local variable is `errno`.

Thread-local variables in the standard library

C standard libraries can support usage of thread-local storage.

Library objects that need thread-local storage when used in multiple tasks are for example:

  • error functions - errno, strerror.
  • locale functions - localeconv, setlocale.
  • time functions - asctime, localtime, gmtime, mktime.
  • multibyte functions - mbrlen, mbrtowc, mbsrtowc, mbtowc, wcrtomb, wcsrtomb, wctomb.
  • rand functions - rand, srand.
  • etc functions - atexit, strtok.
  • C++ exception engine.

Handling thread-local storage

To use thread-local storage it needs to be enabled and handled by the OS. If TLS is not enabled, most systems will treat thread-local variables like regular variables and share them across the whole system.

embOS

embOS is prepared to support TLS, but does not enable it per default. This has the advantage of no additional overhead as long as TLS is not needed by the application. The embOS implementation of thread-local storage allows activation of TLS separately for every task. Only tasks that call functions using TLS need to activate it by calling an initialization function when the task is started.

Linker configuration

Thread-local data and thread-local bss need to be placed in memory in a block and order which is known to the OS. The OS creates a copy of the block for each thread or task and on access of a thread-local variable points to the block copy belonging to the active thread.

SEGGER Linker

With the SEGGER Linker thread-local data and thread-local bss can be put into a block, which can then be placed regularly in RAM.

  define block tls with fixed order { block tbss, block tdata };
  
  place in RAM with auto order      { block tls, readwrite, zeroinit };

Linker warning "thread-local and non-thread-local sections cannot be mixed"

When the tls block is declared without a specific ordering, the SEGGER Linker uses auto order to reduce the loss due to alignment and may interfere with the ordering expected by the OS.

To resolve this warning, define the block which contains tbss and tdata with fixed order.

GNU Linker

With the GNU Linker thread-local data and thread-local bss need to be placed with fixed order and layout, too.

In Embedded Studio the section placement file can take care of this:

 <MemorySegment name="$(FLASH_NAME:FLASH);FLASH1">
   ...
   <ProgramSection alignment="4" load="Yes" runin=".data_run" name=".data" />
   <ProgramSection alignment="4" load="Yes" runin=".tdata_run" name=".tdata" />
   ...
 </MemorySegment>
 <MemorySegment name="$(RAM_NAME:RAM);SRAM;RAM1">
   ...
   <ProgramSection alignment="4" load="No" name=".data_run" />
   <ProgramSection alignment="4" load="No" name=".bss" />
   <ProgramSection alignment="4" load="No" name=".tbss" />
   <ProgramSection alignment="4" load="No" name=".tdata_run" />
   ...
 </MemorySegment>

Note: The default *placement.xml in Embedded Studio prior to version 6.30 created mixed TLS and non-TLS sections and might need to be updated in existing projects.

In a manually created linker script the ordering can look like this:

 __tbss_load_start__ = ALIGN(__bss_end__ , 4);
 .tbss ALIGN(__bss_end__ , 4) (NOLOAD) : AT(ALIGN(__bss_end__ , 4))
 {
   __tbss_start__ = .;
   *(.tbss .tbss.*)
 }
 __tbss_end__ = __tbss_start__ + SIZEOF(.tbss);
 __tbss_size__ = SIZEOF(.tbss);
 __tbss_load_end__ = __tbss_end__;

 __tdata_load_start__ = ALIGN(__data_load_start__ + SIZEOF(.data) , 4);
 .tdata ALIGN(__tbss_end__ , 4) : AT(ALIGN(__data_load_start__ + SIZEOF(.data) , 4))
 {
   __tdata_start__ = .;
   *(.tdata .tdata.*)
 }
 __tdata_end__ = __tdata_start__ + SIZEOF(.tdata);
 __tdata_size__ = SIZEOF(.tdata);
 __tdata_load_end__ = __tdata_load_start__ + SIZEOF(.tdata);

 .tdata_run ALIGN(__tbss_end__ , 4) (NOLOAD) :
 {
   __tdata_run_start__ = .;
 }
 __tdata_run_end__ = __tdata_run_start__ + SIZEOF(.tdata);
 __tdata_run_size__ = __tdata_run_end__ - __tdata_run_start__;
 __tdata_run_load_end__ = __tdata_run_end__;

Other section ordering might lead to the error message "TLS sections are not adjacent:"