]> Kevux Git Server - fll/commitdiff
Feature: Add f_thread_name_set() and f_thread_name_get().
authorKevin Day <Kevin@kevux.org>
Sat, 2 Aug 2025 03:40:19 +0000 (22:40 -0500)
committerKevin Day <Kevin@kevux.org>
Sat, 2 Aug 2025 03:40:19 +0000 (22:40 -0500)
These provide support for `pthread_setname_np()` and `pthread_getname_np(0)` respectively.

These pthread functions are not portable so provide the following variables to gracefully disable the calls:
  - `_pthread_setname_np_unsupported_`
  - `_pthread_getname_np_unsupported_`

The newly added functions return `F_implement_not` when these unsupported defines are set.
This allows for programs that call these to not really care if the thread naming support is present.
They can call the function and if it works, then it works and if it doesn't work, then it doesn't work.

16 files changed:
build/level_0/settings
build/monolithic/settings
level_0/f_thread/c/thread.c
level_0/f_thread/c/thread.h
level_0/f_thread/data/build/defines
level_0/f_thread/data/build/settings
level_0/f_thread/data/build/settings-mocks
level_0/f_thread/data/build/settings-tests
level_0/f_thread/tests/unit/c/mock-thread.c
level_0/f_thread/tests/unit/c/mock-thread.h
level_0/f_thread/tests/unit/c/test-thread-name_get.c [new file with mode: 0644]
level_0/f_thread/tests/unit/c/test-thread-name_get.h [new file with mode: 0644]
level_0/f_thread/tests/unit/c/test-thread-name_set.c [new file with mode: 0644]
level_0/f_thread/tests/unit/c/test-thread-name_set.h [new file with mode: 0644]
level_0/f_thread/tests/unit/c/test-thread.c
level_0/f_thread/tests/unit/c/test-thread.h

index 11e6e60..2343fc7 100644 (file)
@@ -157,6 +157,7 @@ environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY L
 
 #defines -D_di_libcap_
 #defines -D_f_file_rename_use_renameat2_
+#defines -D_pthread_getname_np_unsupported_ -D_pthread_setname_np_unsupported_
 defines -D_libcap_legacy_only_
 defines-clang -D_clang_not_a_compile_time_constant_workaround_
 defines-android -D_di_f_thread_attribute_affinity_get_ -D_di_f_thread_attribute_affinity_set_ -D_di_f_thread_attribute_concurrency_get_ -D_di_f_thread_attribute_concurrency_set_ -D_di_f_thread_attribute_default_get_ -D_di_f_thread_attribute_default_set_ -D_di_f_thread_cancel_ -D_di_f_thread_cancel_state_set_ -D_di_f_thread_cancel_test_ -D_di_f_thread_join_try_ -D_di_f_thread_join_timed_ -D_pthread_mutex_prioceiling_unsupported_ -D_di_f_thread_semaphore_file_close_ -D_di_f_thread_semaphore_file_open_ -D_di_f_thread_semaphore_file_delete_ -D_di_f_thread_cancel_type_set_
index 3766926..e69e6a3 100644 (file)
@@ -200,6 +200,7 @@ environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY L
 
 #defines -D_di_libcap_
 #defines -D_f_file_rename_use_renameat2_
+#defines -D_pthread_getname_np_unsupported_ -D_pthread_setname_np_unsupported_
 defines -D_libcap_legacy_only_
 defines-clang -D_clang_not_a_compile_time_constant_workaround_
 defines-android -D_di_f_thread_attribute_affinity_get_ -D_di_f_thread_attribute_affinity_set_ -D_di_f_thread_attribute_concurrency_get_ -D_di_f_thread_attribute_concurrency_set_ -D_di_f_thread_attribute_default_get_ -D_di_f_thread_attribute_default_set_ -D_di_f_thread_cancel_ -D_di_f_thread_cancel_state_set_ -D_di_f_thread_cancel_test_ -D_di_f_thread_join_try_ -D_di_f_thread_join_timed_ -D_pthread_mutex_prioceiling_unsupported_ -D_di_f_thread_semaphore_file_close_ -D_di_f_thread_semaphore_file_open_ -D_di_f_thread_semaphore_file_delete_ -D_di_f_thread_cancel_type_set_
index fb6b3b0..4f6bde1 100644 (file)
@@ -1642,6 +1642,61 @@ extern "C" {
   }
 #endif // _di_f_thread_mutex_lock_try_
 
+#if defined(_pthread_setname_np_unsupported_) && !defined(_di_f_thread_name_set_)
+  f_status_t f_thread_name_set(const f_thread_id_t id, const f_string_static_t name) {
+    return F_status_set_error(F_implement_not);
+  }
+#elif !defined(_di_f_thread_name_set_)
+  f_status_t f_thread_name_set(const f_thread_id_t id, const f_string_static_t name) {
+
+    if (!name.used || !name.string) return F_data_not;
+
+    const int error = pthread_setname_np(id, name.string);
+
+    if (error) {
+      if (error == ERANGE) return F_status_set_error(F_buffer_overflow);
+
+      return F_status_set_error(F_failure);
+    }
+
+    return F_okay;
+  }
+#endif // defined(_pthread_getname_np_unsupported_) && !defined(_di_f_thread_name_get_)
+
+#if defined(_pthread_getname_np_unsupported_) && !defined(_di_f_thread_name_get_)
+  f_status_t f_thread_name_get(const f_thread_id_t id, f_string_static_t * const name) {
+    #ifndef _di_level_0_parameter_checking_
+      if (!name) return F_status_set_error(F_parameter);
+    #endif // _di_level_0_parameter_checking_
+
+    return F_status_set_error(F_implement_not);
+  }
+#elif !defined(_di_f_thread_name_get_)
+  f_status_t f_thread_name_get(const f_thread_id_t id, f_string_static_t * const name) {
+    #ifndef _di_level_0_parameter_checking_
+      if (!name) return F_status_set_error(F_parameter);
+    #endif // _di_level_0_parameter_checking_
+
+    if (name->size) {
+      if (name->used > name->size) return F_status_set_error(F_parameter);
+      if (name->used == name->size) return F_status_set_error(F_buffer_underflow);
+    }
+    else if (!name->used) return F_status_set_error(F_buffer_underflow);
+
+    const int error = name->size
+      ? pthread_getname_np(id, name->string + name->used, name->size - name->used)
+      : pthread_getname_np(id, name->string, name->used);
+
+    if (error) {
+      if (error == ERANGE) return F_status_set_error(F_buffer_overflow);
+
+      return F_status_set_error(F_failure);
+    }
+
+    return F_okay;
+  }
+#endif // _di_f_thread_name_get_
+
 #if defined(_pthread_mutex_prioceiling_unsupported_) && !defined(_di_f_thread_mutex_priority_ceiling_get_)
   f_status_t f_thread_mutex_priority_ceiling_get(f_thread_mutex_t * const mutex, int * const ceiling) {
     #ifndef _di_level_0_parameter_checking_
index 80c9cc4..0474cbb 100644 (file)
@@ -2283,6 +2283,70 @@ extern "C" {
   extern f_status_t f_thread_mutex_lock_try(f_thread_mutex_t * const mutex);
 #endif // _di_f_thread_mutex_lock_try_
 
+/**
+ * Set the name of the thread.
+ *
+ * This uses a non-portal call and may do nothing on systems that do not support custom thread names.
+ *
+ * @param id
+ *   The thread to name.
+ * @param name
+ *   The thread name.
+ *
+ *   This must be a NULL terminated string.
+ *
+ * @return
+ *   F_data_not if name.used is 0 or name.string is NULL.
+ *   F_okay on success.
+ *
+ *   F_buffer_overflow (with error bit) If the given name is larged than is allowed for a thread name.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_implement_not (with error bit) if pthread_setname_np() is not available.
+ *
+ *   F_failure (with error bit) on any other error.
+ *
+ * @see pthread_setname_np()
+ */
+#ifndef _di_f_thread_name_set_
+  extern f_status_t f_thread_name_set(const f_thread_id_t id, const f_string_static_t name);
+#endif // _di_f_thread_name_set_
+
+/**
+ * Set the name of the thread.
+ *
+ * This uses a non-portal call and may do nothing on systems that do not support custom thread names.
+ *
+ * @param id
+ *   The thread to name.
+ * @param name
+ *   The thread name.
+ *
+ *    The pthread_getname_np() uses a static array so this variable is processed as follows:
+ *      - If name.size is 0, then this is considered a static string and then name.used is considered the length.
+ *        - If name.used is also 0, then this is an error.
+ *      - If name.size is greater than 0, then the name.size - name.used is the length.
+ *        - If name.used >= name.size, then this is an error.
+ *      - The thread name is assigned to the name.string as follows:
+ *        - If name.size is 0, then name.string is replaced with the thread name and is always NULL terminated (at name.used - 1; if name.used is 1 then the string.used[0] will always be NULL).
+ *        - If name.size > 0, then the thread name is appended onto the string at position of name.used and then the name.used is incremented (this is not NULL terminated).
+ *
+ *    Must not be NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   F_buffer_overflow (with error bit) if name is too small to fit the thread name.
+ *   F_buffer_underflow (with error bit) if name.size > 0 and name.used == name.size or name.size == 0 and name.used == 0.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ *   F_failure (with error bit) on any other error.
+ *
+ * @see pthread_getname_np()
+ */
+#ifndef _di_f_thread_name_get_
+  extern f_status_t f_thread_name_get(const f_thread_id_t id, f_string_static_t * const name);
+#endif // _di_f_thread_name_get_
+
 /**
  * Call the given routine only one time and never again.
  *
index a7c99da..6b5d521 100644 (file)
@@ -2,5 +2,7 @@
 
 _di_thread_support_ disables thread support, which is intended to disable this entire f_thread project. This is not directly used by this library, but is instead used by other libraries to not include f_thread (such as special compilations like monolithic). Future versions may potentially support this, providing stubs.
 _pthread_attr_unsupported_ Disable non-portable functionality associated with pthread_attr.
+_pthread_getname_np_unsupported_ Disable GNU specific pthread_getname_np().
 _pthread_mutex_prioceiling_unsupported_ Disable mutex_prioceiling related functionality for systems that do not support it.
+_pthread_setname_np_unsupported_ Disable GNU specific pthread_setname_np().
 _pthread_sigqueue_unsupported_ Disable GNU specific sigqueue().
index 142779b..0fcfd2f 100644 (file)
@@ -71,6 +71,7 @@ environment PATH LD_LIBRARY_PATH
 environment LANG LC_ALL LC_COLLATE LC_CTYPE LC_FASTMSG LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME LOCPATH NLSPATH
 
 defines -D_pthread_attr_unsupported_ -D_pthread_sigqueue_unsupported_
+#defines -D_pthread_getname_np_unsupported_ -D_pthread_setname_np_unsupported_
 
 flags -O2 -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses -Wno-missing-braces
 flags -fstack-clash-protection -fno-delete-null-pointer-checks
index 9b52550..bcef337 100644 (file)
@@ -121,6 +121,7 @@ flags -Wl,--wrap=pthread_exit
 flags -Wl,--wrap=pthread_getattr_default_np
 flags -Wl,--wrap=pthread_getcpuclockid
 flags -Wl,--wrap=pthread_getconcurrency
+flags -Wl,--wrap=pthread_getname_np
 flags -Wl,--wrap=pthread_getschedparam
 flags -Wl,--wrap=pthread_getspecific
 flags -Wl,--wrap=pthread_join
@@ -164,6 +165,7 @@ flags -Wl,--wrap=pthread_setattr_default_np
 flags -Wl,--wrap=pthread_setcancelstate
 flags -Wl,--wrap=pthread_setcanceltype
 flags -Wl,--wrap=pthread_setconcurrency
+flags -Wl,--wrap=pthread_setname_np
 flags -Wl,--wrap=pthread_setschedparam
 flags -Wl,--wrap=pthread_setschedprio
 flags -Wl,--wrap=pthread_setspecific
index d97aee3..c93065a 100644 (file)
@@ -93,6 +93,7 @@ build_sources_program test-thread-signal_mask.c test-thread-signal_queue.c test-
 build_sources_program test-thread-spin_create.c test-thread-spin_delete.c
 build_sources_program test-thread-spin_lock.c test-thread-spin_lock_try.c test-thread-spin_unlock.c
 build_sources_program test-thread-mutex_priority_ceiling_get.c test-thread-mutex_priority_ceiling_set.c
+build_sources_program test-thread-name_get.c test-thread-name_set.c
 build_sources_program test-thread-once.c
 build_sources_program test-thread-semaphore_create.c test-thread-semaphore_delete.c
 build_sources_program test-thread-semaphore_file_open.c test-thread-semaphore_file_close.c test-thread-semaphore_file_delete.c
index c674e2a..e6367c0 100644 (file)
@@ -542,6 +542,17 @@ int __wrap_pthread_getcpuclockid(pthread_t thread, clockid_t *clock_id) {
   return 0;
 }
 
+int __wrap_pthread_getname_np(pthread_t thread, char name[], size_t size) {
+
+  const bool failure = mock_type(bool);
+
+  if (failure) {
+    return mock_type(int);
+  }
+
+  return 0;
+}
+
 int __wrap_pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param) {
 
   const bool failure = mock_type(bool);
@@ -645,6 +656,17 @@ int __wrap_pthread_setconcurrency(int new_level) {
   return 0;
 }
 
+int __wrap_pthread_setname_np(pthread_t thread, const char *name) {
+
+  const bool failure = mock_type(bool);
+
+  if (failure) {
+    return mock_type(int);
+  }
+
+  return 0;
+}
+
 int __wrap_pthread_setschedparam(pthread_t pthread, int policy, const struct sched_param *param) {
 
   const bool failure = mock_type(bool);
index 80c7bc2..f80e3f4 100644 (file)
@@ -79,6 +79,7 @@ extern void __wrap_pthread_exit(void *value);
 extern int __wrap_pthread_getattr_default_np(pthread_attr_t *attr);
 extern int __wrap_pthread_getconcurrency(void);
 extern int __wrap_pthread_getcpuclockid(pthread_t thread, clockid_t *clock_id);
+extern int __wrap_pthread_getname_np(pthread_t thread, char name[], size_t size);
 extern int __wrap_pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param);
 extern void *__wrap_pthread_getspecific(pthread_key_t key);
 extern int __wrap_pthread_join(pthread_t pthread, void **value_ptr);
@@ -90,6 +91,7 @@ extern int __wrap_pthread_setattr_default_np(const pthread_attr_t *attr);
 extern int __wrap_pthread_setcancelstate(int state, int *oldstate);
 extern int __wrap_pthread_setcanceltype(int type, int *oldtype);
 extern int __wrap_pthread_setconcurrency(int new_level);
+extern int __wrap_pthread_setname_np(pthread_t thread, const char *name);
 extern int __wrap_pthread_setschedparam(pthread_t pthread, int policy, const struct sched_param *param);
 extern int __wrap_pthread_setschedprio(pthread_t thread, int prio);
 extern int __wrap_pthread_setspecific(pthread_key_t key, const void *value);
diff --git a/level_0/f_thread/tests/unit/c/test-thread-name_get.c b/level_0/f_thread/tests/unit/c/test-thread-name_get.c
new file mode 100644 (file)
index 0000000..67daad3
--- /dev/null
@@ -0,0 +1,82 @@
+#include "test-thread.h"
+#include "test-thread-name_get.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_thread_name_get__fails(void **state) {
+
+  const f_thread_id_t id = f_thread_id_t_initialize;
+  f_string_static_t test = macro_f_string_static_t_initialize_1("test", 0, 4);
+  f_string_static_t no_size_no_used = macro_f_string_static_t_initialize_1(0, 0, 0);
+  f_string_static_t size_is_used = macro_f_string_static_t_initialize_1("test", 4, 4);
+  f_string_static_t size_less_used = macro_f_string_static_t_initialize_1("test", 2, 4);
+
+  int errnos[] = {
+    ERANGE,
+    mock_errno_generic,
+  };
+
+  f_status_t statuss[] = {
+    F_buffer_overflow,
+    F_failure,
+  };
+
+  for (uint8_t i = 0; i < 2; ++i) {
+
+    will_return(__wrap_pthread_getname_np, true);
+    will_return(__wrap_pthread_getname_np, errnos[i]);
+
+    const f_status_t status = f_thread_name_get(id, &test);
+
+    assert_int_equal(status, F_status_set_error(statuss[i]));
+  } // for
+
+  {
+    const f_status_t status = f_thread_name_get(id, &no_size_no_used);
+
+    assert_int_equal(status, F_status_set_error(F_buffer_underflow));
+  }
+
+  {
+    const f_status_t status = f_thread_name_get(id, &size_is_used);
+
+    assert_int_equal(status, F_status_set_error(F_buffer_underflow));
+  }
+
+  {
+    const f_status_t status = f_thread_name_get(id, &size_less_used);
+
+    assert_int_equal(status, F_status_set_error(F_parameter));
+  }
+}
+
+void test__f_thread_name_get__parameter_checking(void **state) {
+
+  const f_thread_id_t id = f_thread_id_t_initialize;
+
+  {
+    const f_status_t status = f_thread_name_get(id, 0);
+
+    assert_int_equal(status, F_status_set_error(F_parameter));
+  }
+}
+
+void test__f_thread_name_get__works(void **state) {
+
+  const f_thread_id_t id = f_thread_id_t_initialize;
+  f_string_static_t test = macro_f_string_static_t_initialize_1("test", 0, 4);
+
+  {
+    will_return(__wrap_pthread_getname_np, false);
+
+    const f_status_t status = f_thread_name_get(id, &test);
+
+    assert_int_equal(status, F_okay);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_thread/tests/unit/c/test-thread-name_get.h b/level_0/f_thread/tests/unit/c/test-thread-name_get.h
new file mode 100644 (file)
index 0000000..c8c5b64
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Thread
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the thread project.
+ */
+#ifndef _TEST__F_thread__name_get_h
+#define _TEST__F_thread__name_get_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_thread_name_get()
+ */
+extern void test__f_thread_name_get__fails(void **state);
+
+/**
+ * Test that parameter checking works as expected.
+ *
+ * @see f_thread_name_get()
+ */
+extern void test__f_thread_name_get__parameter_checking(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_thread_name_get()
+ */
+extern void test__f_thread_name_get__works(void **state);
+
+#endif // _TEST__F_thread__name_get_h
diff --git a/level_0/f_thread/tests/unit/c/test-thread-name_set.c b/level_0/f_thread/tests/unit/c/test-thread-name_set.c
new file mode 100644 (file)
index 0000000..9e19ebb
--- /dev/null
@@ -0,0 +1,64 @@
+#include "test-thread.h"
+#include "test-thread-name_set.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_thread_name_set__fails(void **state) {
+
+  const f_thread_id_t id = f_thread_id_t_initialize;
+  const f_string_static_t test = macro_f_string_static_t_initialize_1("test", 0, 4);
+
+  int errnos[] = {
+    ERANGE,
+    mock_errno_generic,
+  };
+
+  f_status_t statuss[] = {
+    F_buffer_overflow,
+    F_failure,
+  };
+
+  for (uint8_t i = 0; i < 2; ++i) {
+
+    will_return(__wrap_pthread_setname_np, true);
+    will_return(__wrap_pthread_setname_np, errnos[i]);
+
+    const f_status_t status = f_thread_name_set(id, test);
+
+    assert_int_equal(status, F_status_set_error(statuss[i]));
+  } // for
+}
+
+void test__f_thread_name_set__works(void **state) {
+
+  const f_thread_id_t id = f_thread_id_t_initialize;
+  const f_string_static_t test = macro_f_string_static_t_initialize_1("test", 0, 4);
+  const f_string_static_t no_used = macro_f_string_static_t_initialize_1("test", 0, 0);
+  const f_string_static_t no_string = macro_f_string_static_t_initialize_1(0, 0, 1);
+
+  {
+    will_return(__wrap_pthread_setname_np, false);
+
+    const f_status_t status = f_thread_name_set(id, test);
+
+    assert_int_equal(status, F_okay);
+  }
+
+  {
+    const f_status_t status = f_thread_name_set(id, no_used);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_thread_name_set(id, no_string);
+
+    assert_int_equal(status, F_data_not);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_thread/tests/unit/c/test-thread-name_set.h b/level_0/f_thread/tests/unit/c/test-thread-name_set.h
new file mode 100644 (file)
index 0000000..5dcec57
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Thread
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the thread project.
+ */
+#ifndef _TEST__F_thread__name_set_h
+#define _TEST__F_thread__name_set_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_thread_name_set()
+ */
+extern void test__f_thread_name_set__fails(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_thread_name_set()
+ */
+extern void test__f_thread_name_set__works(void **state);
+
+#endif // _TEST__F_thread__name_set_h
index 6b26d53..d0a8813 100644 (file)
@@ -340,6 +340,12 @@ int main(void) {
     cmocka_unit_test(test__f_thread_mutex_priority_ceiling_set__fails),
     cmocka_unit_test(test__f_thread_mutex_priority_ceiling_set__works),
 
+    cmocka_unit_test(test__f_thread_name_get__fails),
+    cmocka_unit_test(test__f_thread_name_get__works),
+
+    cmocka_unit_test(test__f_thread_name_set__fails),
+    cmocka_unit_test(test__f_thread_name_set__works),
+
     cmocka_unit_test(test__f_thread_once__fails),
     cmocka_unit_test(test__f_thread_once__works),
 
@@ -610,6 +616,9 @@ int main(void) {
       cmocka_unit_test(test__f_thread_mutex_priority_ceiling_get__parameter_checking),
       cmocka_unit_test(test__f_thread_mutex_priority_ceiling_set__parameter_checking),
 
+      cmocka_unit_test(test__f_thread_name_get__parameter_checking),
+      // f_thread_name_set() doesn't use parameter checking.
+
       cmocka_unit_test(test__f_thread_once__parameter_checking),
 
       cmocka_unit_test(test__f_thread_scheduler_parameter_get__parameter_checking),
index 31dfedc..87f712a 100644 (file)
 #include "test-thread-mutex_lock_try.h"
 #include "test-thread-mutex_priority_ceiling_get.h"
 #include "test-thread-mutex_priority_ceiling_set.h"
+#include "test-thread-name_get.h"
+#include "test-thread-name_set.h"
 #include "test-thread-once.h"
 #include "test-thread-scheduler_parameter_get.h"
 #include "test-thread-scheduler_parameter_set.h"