casaroli commented on PR #20803:
URL: https://github.com/apache/nuttx/pull/20803#issuecomment-6262202700

   Thank you for this PR! A per-thread `__emutls_get_address()` is something I 
need too, and the `--wrap` idea is a nice way to make sure libc's copy is used 
in a flat image.
   
   I tried it on master fc91db17fb7 with qemu-armv8a (QEMU 11) and the Arm GNU 
Toolchain 14.2.Rel1 for aarch64-none-elf, which is built with `--disable-tls`, 
so `__thread` becomes calls to `__emutls_get_address()`. With large enough 
limits it works well: with `TLS_NELEM=16` and `TLS_TASK_NELEM=32`, a program 
with 12 `__thread` variables in two threads passes. I found a few things that I 
think are worth a look. I hope the details help:
   
   **1. A kernel build does not compile.** `TLS_TASK_NELEM` depends on 
`!BUILD_KERNEL`, so it is not defined there, and then `ifneq 
($(CONFIG_TLS_TASK_NELEM),0)` in Make.defs and tools/Config.mk is true (and the 
CMake condition `NOT CONFIG_TLS_TASK_NELEM EQUAL 0` too). tls_emutls.c is then 
compiled without the task TLS functions:
   
   ```
   ./tools/configure.sh qemu-armv8a:knsh
   kconfig-tweak --set-val TLS_NELEM 4 && make olddefconfig && make
   tls/tls_emutls.c:169:11: error: implicit declaration of function 
'task_tls_get_value'
   tls/tls_emutls.c:183:15: error: implicit declaration of function 
'task_tls_set_value'
   tls/tls_emutls.c:233:26: error: implicit declaration of function 
'task_tls_alloc'
   ```
   
   **2. The sim link fails in CI.** When the link goes through gcc or c++, as 
in the sim, the driver does not know `--wrap=...` without `-Wl,`. This is the 
Linux (sim-01) failure of this PR, `sim:citest`:
   
   ```
   c++: error: unrecognized command-line option '--wrap=__emutls_get_address'
   c++: error: unrecognized command-line option 
'--wrap=__emutls_register_common'
   ```
   
   **3. The task TLS slots can run out.** Each variable takes one task TLS 
slot, and in a flat build the slots belong to the whole system and are not 
given back. With `TLS_NELEM=4` and `TLS_TASK_NELEM=4` on `qemu-armv8a:nsh`, a 
program with 5 `__thread` variables stops at an assertion:
   
   ```
   nsh> tls5
   dump_assert_info: Assertion failed : at file: tls/task_tls_destruct.c:109 
task: tls5
   ```
   
   The same happens across programs. Five small programs with one `__thread` 
variable each, run one after the other in the same boot: the first four pass 
and exit, and the fifth stops, because the first four still hold their slots:
   
   ```
   tls1a: PASS
   tls1b: PASS
   tls1c: PASS
   tls1d: PASS
   dump_assert_info: Assertion failed : at file: tls/task_tls_destruct.c:109 
task: tls1e
   tls1a: PASS
   ```
   
   (`tls1a` passes again at the end because it still has its slot.) I think a 
loaded ELF module would also take new slots each time it is loaded, because its 
control objects start at zero again, but I did not test that one.
   
   **4. The pthread keys can run out too.** Each variable also takes one 
pthread key in each task group, from the same `TLS_NELEM` keys that the program 
uses for its own `pthread_key_create()`. With `TLS_NELEM=4` and 
`TLS_TASK_NELEM=32`, the same `tls5` program stops here:
   
   ```
   nsh> tls5
   dump_assert_info: Assertion failed panic: at file: tls/tls_emutls.c:179 
task: tls5
   ```
   
   This can come quickly in C++: libstdc++ built with thread support has 
`thread_local` variables of its own (`std::__once_call`, 
`std::__once_callable`).
   
   One idea, in case it helps: one pthread key per task group that holds an 
array of the thread's copies, with an index per variable given out once from a 
global counter. That needs no task TLS slot and has no fixed limit, so it would 
also work in a kernel build.
   
   Happy to test a new version!
   
   <details><summary>Test programs (built-in apps, PROGNAME = tls5 tls1a tls1b 
tls1c tls1d tls1e)</summary>
   
   ```c
   /* tls5.c */
   #include <pthread.h>
   #include <stdio.h>
   
   static __thread int a = 1, b = 2, c = 3, d = 4, e = 5;
   
   static void *worker(void *arg)
   {
     a = 10;
     return (void *)(long)(a + b + c + d + e);
   }
   
   int main(int argc, char **argv)
   {
     pthread_t t;
     void *r;
   
     pthread_create(&t, NULL, worker, NULL);
     pthread_join(t, &r);
     printf("tls5: %s\n", (long)r == 24 && a == 1 ? "PASS" : "FAIL");
     return 0;
   }
   ```
   
   ```c
   /* tls1a.c; tls1b.c to tls1e.c are the same with their own name */
   #include <stdio.h>
   
   static __thread int v = 1;
   
   int main(int argc, char **argv)
   {
     v++;
     printf("tls1a: %s\n", v == 2 ? "PASS" : "FAIL");
     return 0;
   }
   ```
   
   </details>
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to