This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 8ffa65519 examples/nxflat: Add tests of __thread and thread_local 
variables.
8ffa65519 is described below

commit 8ffa65519cfb9b1dc2604d0931badaebedaf3e3c
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Oct 19 10:02:59 2026 +0200

    examples/nxflat: Add tests of __thread and thread_local variables.
    
    tls has a __thread variable without an initial value, one with an
    initial value, and a struct whose template holds a string pointer and a
    data pointer.  The main thread and a second thread each check the
    initial values and change them.  Each thread must not see the changes
    of the other one.
    
    tlsxx does the same with a C++ thread_local object with a constructor,
    and with the static thread_local member of a class template.  Each
    thread must construct its own objects at their first use.  The member
    of the template is in COMDAT section groups.  tlsxx needs
    CONFIG_HAVE_CXX.
    
    With a toolchain that emulates TLS, a module imports
    __emutls_get_address() from the base code, and the generated symbol
    table exports it.  The checks of the second thread pass only if the
    base code keeps a copy of each variable per thread.  The libgcc of such
    a toolchain keeps one copy for all threads.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 examples/nxflat/tests/Makefile         |   5 +-
 examples/nxflat/tests/tls/.gitignore   |   4 +
 examples/nxflat/tests/tls/Makefile     |  70 +++++++++++++++
 examples/nxflat/tests/tls/tls.c        | 148 ++++++++++++++++++++++++++++++
 examples/nxflat/tests/tlsxx/.gitignore |   4 +
 examples/nxflat/tests/tlsxx/Makefile   |  70 +++++++++++++++
 examples/nxflat/tests/tlsxx/tlsxx.cxx  | 158 +++++++++++++++++++++++++++++++++
 7 files changed, 458 insertions(+), 1 deletion(-)

diff --git a/examples/nxflat/tests/Makefile b/examples/nxflat/tests/Makefile
index 814eb1801..e3fb0a340 100644
--- a/examples/nxflat/tests/Makefile
+++ b/examples/nxflat/tests/Makefile
@@ -35,7 +35,10 @@ SUBDIRS              += longjmp
 endif
 
 ifneq ($(CONFIG_DISABLE_PTHREAD),y)
-SUBDIRS                += mutex pthread
+SUBDIRS                += mutex pthread tls
+ifeq ($(CONFIG_HAVE_CXX),y)
+SUBDIRS                += tlsxx
+endif
 endif
 
 ifneq ($(CONFIG_ARCH_ADDRENV),y)
diff --git a/examples/nxflat/tests/tls/.gitignore 
b/examples/nxflat/tests/tls/.gitignore
new file mode 100644
index 000000000..3d1b8c6d3
--- /dev/null
+++ b/examples/nxflat/tests/tls/.gitignore
@@ -0,0 +1,4 @@
+tls
+*.r1
+*.r2
+*-thunk.S
diff --git a/examples/nxflat/tests/tls/Makefile 
b/examples/nxflat/tests/tls/Makefile
new file mode 100644
index 000000000..f507bc219
--- /dev/null
+++ b/examples/nxflat/tests/tls/Makefile
@@ -0,0 +1,70 @@
+############################################################################
+# apps/examples/nxflat/tests/tls/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs    # Basic make _info
+
+BIN                    = tls
+
+R1SRCS                 = $(BIN).c
+R1OBJS                 = $(R1SRCS:.c=.o)
+
+R2SRC                  = $(BIN)-thunk.S
+R2OBJ                  = $(R2SRC:.S=.o)
+
+all: $(BIN)
+
+$(R1OBJS): %.o: %.c
+       @echo "CC: $<"
+       $(Q) $(CC) -c $(CPICFLAGS) $< -o $@
+
+$(R2OBJ): %.o: %.S
+       @echo "AS: $<"
+       $(Q) $(CC) -c $(CPICFLAGS) $< -o $@
+
+$(BIN).r1: $(R1OBJS)
+       @echo "LD: $<"
+       $(Q) $(LD) $(NXFLATLDFLAGS1) -o $@ $^
+
+$(R2SRC): $(BIN).r1
+       @echo "MK: $<"
+       $(Q) $(MKNXFLAT) -o $@ $^
+
+$(BIN).r2: $(R2OBJ)
+       @echo "LD: $<"
+       $(Q) $(LD) $(NXFLATLDFLAGS2) -o $@ $(R1OBJS) $(R2OBJ)
+
+$(BIN): $(BIN).r2
+       @echo "LD: $<"
+       $(Q) $(LDNXFLAT) $(LDNXFLATFLAGS) -o $@ $^
+
+$(ROMFS_DIR)/$(BIN): $(BIN)
+       $(Q) mkdir -p $(ROMFS_DIR)
+       $(Q) install -m 0755 $(BIN) $(ROMFS_DIR)/$(BIN)
+
+install: $(ROMFS_DIR)/$(BIN)
+
+clean:
+       $(call DELFILE, $(BIN))
+       $(call DELFILE, $(R2SRC))
+       $(call DELFILE, *.r1)
+       $(call DELFILE, *.r2)
+       $(call CLEAN)
diff --git a/examples/nxflat/tests/tls/tls.c b/examples/nxflat/tests/tls/tls.c
new file mode 100644
index 000000000..ad1625ea8
--- /dev/null
+++ b/examples/nxflat/tests/tls/tls.c
@@ -0,0 +1,148 @@
+/****************************************************************************
+ * apps/examples/nxflat/tests/tls/tls.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct tls_s
+{
+  const char *name;
+  int *ptr;
+  int n;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+int g_global = 117;
+
+/* No initial value: no template.  An initial value: a template.  A struct
+ * with pointers: a template that the loader must relocate.
+ */
+
+__thread int g_zero;
+__thread int g_init = 42;
+__thread struct tls_s g_struct =
+{
+  "tls-string", &g_global, 3
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static int g_errors;
+static int *g_childaddr;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void check(const char *who, const char *what, int ok)
+{
+  printf("%s: %-34s %s\n", who, what, ok ? "PASS" : "FAIL");
+  if (!ok)
+    {
+      g_errors++;
+    }
+}
+
+static void check_initial(const char *who)
+{
+  printf("%s: g_zero=%d g_init=%d name=%s *ptr=%d n=%d\n", who, g_zero,
+         g_init, g_struct.name, *g_struct.ptr, g_struct.n);
+
+  check(who, "zero-initialized variable", g_zero == 0);
+  check(who, "variable with a template", g_init == 42);
+  check(who, "string pointer in a template",
+        strcmp(g_struct.name, "tls-string") == 0);
+  check(who, "data pointer in a template", g_struct.ptr == &g_global);
+  check(who, "value through that pointer", *g_struct.ptr == 117);
+  check(who, "integer in a struct template", g_struct.n == 3);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/* A function that is used through a pointer must have global scope:  see
+ * the pthread test.
+ */
+
+void *child_start_routine(void *arg)
+{
+  check_initial("CHILD");
+
+  g_childaddr = &g_zero;
+  g_zero      = 2;
+  g_init      = 102;
+  g_struct.n  = 302;
+
+  check("CHILD", "own values read back",
+        g_zero == 2 && g_init == 102 && g_struct.n == 302);
+  return NULL;
+}
+
+int main(int argc, char **argv)
+{
+  pthread_t thread;
+  int status;
+
+  check_initial("MAIN");
+
+  g_zero     = 1;
+  g_init     = 101;
+  g_struct.n = 301;
+
+  status = pthread_create(&thread, NULL, child_start_routine, NULL);
+  if (status != 0)
+    {
+      printf("MAIN: pthread_create() returned %d\n", status);
+      return EXIT_FAILURE;
+    }
+
+  pthread_join(thread, NULL);
+
+  check("MAIN", "values kept after the child ran",
+        g_zero == 1 && g_init == 101 && g_struct.n == 301);
+  check("MAIN", "child has its own copy", g_childaddr != &g_zero);
+
+  if (g_errors != 0)
+    {
+      printf("TLS test FAILED: %d error(s)\n", g_errors);
+      return EXIT_FAILURE;
+    }
+
+  printf("TLS test PASSED\n");
+  return EXIT_SUCCESS;
+}
diff --git a/examples/nxflat/tests/tlsxx/.gitignore 
b/examples/nxflat/tests/tlsxx/.gitignore
new file mode 100644
index 000000000..c2e1aac44
--- /dev/null
+++ b/examples/nxflat/tests/tlsxx/.gitignore
@@ -0,0 +1,4 @@
+tlsxx
+*.r1
+*.r2
+*-thunk.S
diff --git a/examples/nxflat/tests/tlsxx/Makefile 
b/examples/nxflat/tests/tlsxx/Makefile
new file mode 100644
index 000000000..c7ea7aece
--- /dev/null
+++ b/examples/nxflat/tests/tlsxx/Makefile
@@ -0,0 +1,70 @@
+############################################################################
+# apps/examples/nxflat/tests/tlsxx/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs    # Basic make _info
+
+BIN                    = tlsxx
+
+R1SRCS                 = $(BIN).cxx
+R1OBJS                 = $(R1SRCS:.cxx=.o)
+
+R2SRC                  = $(BIN)-thunk.S
+R2OBJ                  = $(R2SRC:.S=.o)
+
+all: $(BIN)
+
+$(R1OBJS): %.o: %.cxx
+       @echo "CXX: $<"
+       $(Q) $(CXX) -c $(CXXPICFLAGS) $< -o $@
+
+$(R2OBJ): %.o: %.S
+       @echo "AS: $<"
+       $(Q) $(CC) -c $(CPICFLAGS) $< -o $@
+
+$(BIN).r1: $(R1OBJS)
+       @echo "LD: $<"
+       $(Q) $(LD) $(NXFLATLDFLAGS1) -o $@ $^
+
+$(R2SRC): $(BIN).r1
+       @echo "MK: $<"
+       $(Q) $(MKNXFLAT) -o $@ $^
+
+$(BIN).r2: $(R2OBJ)
+       @echo "LD: $<"
+       $(Q) $(LD) $(NXFLATLDFLAGS2) -o $@ $(R1OBJS) $(R2OBJ)
+
+$(BIN): $(BIN).r2
+       @echo "LD: $<"
+       $(Q) $(LDNXFLAT) $(LDNXFLATFLAGS) -o $@ $^
+
+$(ROMFS_DIR)/$(BIN): $(BIN)
+       $(Q) mkdir -p $(ROMFS_DIR)
+       $(Q) install -m 0755 $(BIN) $(ROMFS_DIR)/$(BIN)
+
+install: $(ROMFS_DIR)/$(BIN)
+
+clean:
+       $(call DELFILE, $(BIN))
+       $(call DELFILE, $(R2SRC))
+       $(call DELFILE, *.r1)
+       $(call DELFILE, *.r2)
+       $(call CLEAN)
diff --git a/examples/nxflat/tests/tlsxx/tlsxx.cxx 
b/examples/nxflat/tests/tlsxx/tlsxx.cxx
new file mode 100644
index 000000000..c49fe19d8
--- /dev/null
+++ b/examples/nxflat/tests/tlsxx/tlsxx.cxx
@@ -0,0 +1,158 @@
+/****************************************************************************
+ * apps/examples/nxflat/tests/tlsxx/tlsxx.cxx
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The constructor of a thread_local object runs in each thread, at the
+ * first use of the object in that thread.
+ */
+
+class CCounter
+{
+public:
+  CCounter(void);
+
+  const char *m_name;
+  int m_value;
+};
+
+/* The static thread_local member of a class template.  The emutls control
+ * objects of the member and of its guard variable are in COMDAT groups.
+ */
+
+template <typename T> class CHolder
+{
+public:
+  CHolder(void) : m_value(42)
+  {
+  }
+
+  static thread_local CHolder<T> s_holder;
+  T m_value;
+};
+
+template <typename T> thread_local CHolder<T> CHolder<T>::s_holder;
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static int g_nctors;
+static int g_errors;
+static int *g_childaddr;
+static thread_local CCounter g_counter;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+CCounter::CCounter(void)
+{
+  m_name  = "tlsxx-counter";
+  m_value = 17;
+  g_nctors++;
+}
+
+static void check(const char *who, const char *what, bool ok)
+{
+  printf("%s: %-34s %s\n", who, what, ok ? "PASS" : "FAIL");
+  if (!ok)
+    {
+      g_errors++;
+    }
+}
+
+static void check_initial(const char *who)
+{
+  printf("%s: name=%s value=%d\n", who, g_counter.m_name,
+         g_counter.m_value);
+
+  check(who, "constructor set the string",
+        strcmp(g_counter.m_name, "tlsxx-counter") == 0);
+  check(who, "constructor set the value", g_counter.m_value == 17);
+  check(who, "template member initialized",
+        CHolder<int>::s_holder.m_value == 42);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/* A function that is used through a pointer must have global scope:  see
+ * the pthread test.
+ */
+
+extern "C" void *child_start_routine(void *arg)
+{
+  check_initial("CHILD");
+
+  g_childaddr       = &g_counter.m_value;
+  g_counter.m_value = 2;
+  CHolder<int>::s_holder.m_value = 2;
+  return NULL;
+}
+
+extern "C" int main(int argc, char **argv)
+{
+  pthread_t thread;
+  int status;
+
+  check_initial("MAIN");
+  g_counter.m_value = 1;
+  CHolder<int>::s_holder.m_value = 1;
+
+  status = pthread_create(&thread, NULL, child_start_routine, NULL);
+  if (status != 0)
+    {
+      printf("MAIN: pthread_create() returned %d\n", status);
+      return EXIT_FAILURE;
+    }
+
+  pthread_join(thread, NULL);
+
+  check("MAIN", "value kept after the child ran", g_counter.m_value == 1);
+  check("MAIN", "template member kept",
+        CHolder<int>::s_holder.m_value == 1);
+  check("MAIN", "child has its own object",
+        g_childaddr != &g_counter.m_value);
+  check("MAIN", "one constructor call per thread", g_nctors == 2);
+
+  if (g_errors != 0)
+    {
+      printf("TLS C++ test FAILED: %d error(s)\n", g_errors);
+      return EXIT_FAILURE;
+    }
+
+  printf("TLS C++ test PASSED\n");
+  return EXIT_SUCCESS;
+}

Reply via email to