summaryrefslogtreecommitdiff
path: root/ports_smp/linux/gnu
diff options
context:
space:
mode:
Diffstat (limited to 'ports_smp/linux/gnu')
-rw-r--r--ports_smp/linux/gnu/CMakeLists.txt41
-rw-r--r--ports_smp/linux/gnu/example_build/CMakeLists.txt17
-rw-r--r--ports_smp/linux/gnu/inc/tx_port.h28
-rw-r--r--ports_smp/linux/gnu/src/tx_thread_schedule.c41
-rw-r--r--ports_smp/linux/gnu/src/tx_thread_smp_unprotect.c15
-rw-r--r--ports_smp/linux/gnu/src/tx_thread_stack_build.c12
6 files changed, 143 insertions, 11 deletions
diff --git a/ports_smp/linux/gnu/CMakeLists.txt b/ports_smp/linux/gnu/CMakeLists.txt
new file mode 100644
index 00000000..7ff9e0a5
--- /dev/null
+++ b/ports_smp/linux/gnu/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Copyright (c) 2026 Eclipse ThreadX contributors
+# SPDX-License-Identifier: MIT
+# Portions of this file were generated with AI assistance.
+#
+# ThreadX port sources for linux, GNU tools.
+
+target_sources(${PROJECT_NAME}
+ PRIVATE
+ # {{BEGIN_TARGET_SOURCES}}
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_core_get.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_core_preempt.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_current_state_get.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_current_thread_get.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_initialize_wait.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_low_level_initialize.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_protect.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_time_get.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_unprotect.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.c
+ # {{END_TARGET_SOURCES}}
+)
+
+target_include_directories(${PROJECT_NAME}
+ SYSTEM
+ PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}/inc
+)
+
+# The demo program beside the port. Off by default so the ordinary build
+# produces just the library; -DTHREADX_SAMPLE=ON adds the sample_threadx target.
+option(THREADX_SAMPLE "Build the sample program for this port" OFF)
+if(THREADX_SAMPLE)
+ add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/example_build)
+endif()
diff --git a/ports_smp/linux/gnu/example_build/CMakeLists.txt b/ports_smp/linux/gnu/example_build/CMakeLists.txt
new file mode 100644
index 00000000..90d4078a
--- /dev/null
+++ b/ports_smp/linux/gnu/example_build/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Build the sample_threadx program that ports/linux/gnu/Makefile builds, so the
+# CMake build can produce a runnable demo rather than only the library.
+#
+# Guarded by THREADX_SAMPLE in the parent so the default build stays a pure
+# library build.
+
+add_executable(sample_threadx)
+
+target_sources(sample_threadx PRIVATE ${CMAKE_CURRENT_LIST_DIR}/sample_threadx.c)
+
+target_include_directories(sample_threadx
+ PRIVATE
+ ${CMAKE_CURRENT_LIST_DIR}/../inc
+ ${CMAKE_CURRENT_LIST_DIR}/../../../../${TX_COMMON_DIR}/inc
+)
+
+target_link_libraries(sample_threadx PRIVATE threadx)
diff --git a/ports_smp/linux/gnu/inc/tx_port.h b/ports_smp/linux/gnu/inc/tx_port.h
index bb1021dd..b7da882a 100644
--- a/ports_smp/linux/gnu/inc/tx_port.h
+++ b/ports_smp/linux/gnu/inc/tx_port.h
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Portions of this file were generated with AI assistance.
+
/**************************************************************************/
/**************************************************************************/
@@ -373,13 +375,21 @@ void _tx_linux_debug_entry_insert(char *action, char *file, unsigned long lin
*/
-#ifndef TX_MISRA_ENABLE
+/* The clock below is read for every trace event in both builds. The MISRA form
+ of the in-line insert takes the time stamp as an argument rather than reading
+ it itself, so the macro has to be an expression; that is why it carries no
+ trailing semicolon. */
+
#ifndef TX_TRACE_TIME_SOURCE
#define TX_TRACE_TIME_SOURCE ((ULONG) (_tx_linux_time_stamp.tv_nsec))
#endif
-#else
+
+/* Declared here rather than used: this port supplies its own time stamp above,
+ but the generic definition in common_smp/src/tx_misra.c is compiled for this
+ port and needs a declaration in scope. */
+
+#ifdef TX_MISRA_ENABLE
ULONG _tx_misra_time_stamp_get(VOID);
-#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get()
#endif
#ifndef TX_TRACE_TIME_MASK
@@ -503,6 +513,16 @@ typedef struct
} TX_LINUX_MUTEX;
+/* Define how long a thread waits on the Linux mutex before retrying. A thread
+ parked on the mutex can be suspended by the port's signal handler and so never
+ act on the wake-up the next release sends it, which leaves the wake-up lost
+ and every other waiter parked on a mutex that is free. */
+
+#ifndef TX_LINUX_MUTEX_RETRY_NSEC
+#define TX_LINUX_MUTEX_RETRY_NSEC 1000000
+#endif
+
+
/* Define Linux-specific critical section APIs. */
void _tx_linux_mutex_obtain(TX_LINUX_MUTEX *mutex);
@@ -622,7 +642,7 @@ void _tx_thread_smp_debug_entry_insert(ULONG id, ULONG su
#ifdef TX_THREAD_INIT
CHAR _tx_version_id[] =
- "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX SMP/Linux/gcc Version 6.5.1.202602a *";
+ "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX SMP/Linux/gcc Version 6.5.2.202603 *";
#else
extern CHAR _tx_version_id[];
#endif
diff --git a/ports_smp/linux/gnu/src/tx_thread_schedule.c b/ports_smp/linux/gnu/src/tx_thread_schedule.c
index 0c3e8e32..0b6eba25 100644
--- a/ports_smp/linux/gnu/src/tx_thread_schedule.c
+++ b/ports_smp/linux/gnu/src/tx_thread_schedule.c
@@ -292,6 +292,8 @@ void _tx_linux_mutex_obtain(TX_LINUX_MUTEX *mutex)
TX_THREAD *thread_ptr;
pthread_t current_thread_id;
UINT i;
+INT linux_status;
+struct timespec ts;
/* Pickup the current thread ID. */
current_thread_id = pthread_self();
@@ -343,8 +345,43 @@ UINT i;
thread_ptr -> tx_thread_linux_mutex_access = TX_TRUE;
}
- /* Get the Linux mutex. */
- pthread_mutex_lock(&mutex -> tx_linux_mutex);
+ /* Get the Linux mutex. The wait is timed and retried rather than left
+ to pthread_mutex_lock, because a thread can be signalled into the
+ port's suspend handler while it is parked on this mutex. That handler
+ does not return until the thread is resumed, so the wake-up the next
+ release sends is delivered to a thread that never retries and is lost.
+ Any other thread parked on the mutex then waits on a mutex that is
+ free, which on this port deadlocks the whole process: the thread that
+ can resume the suspended one is the scheduler, and the scheduler takes
+ this mutex on every pass. Retrying on a timeout costs nothing when
+ the mutex is handed over normally, and turns that lost wake-up into a
+ delay of at most the retry period. */
+ do
+ {
+
+ /* Set the deadline for this attempt. */
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec = ts.tv_nsec + TX_LINUX_MUTEX_RETRY_NSEC;
+ if (ts.tv_nsec >= 1000000000)
+ {
+
+ ts.tv_nsec = ts.tv_nsec - 1000000000;
+ ts.tv_sec++;
+ }
+
+ linux_status = pthread_mutex_timedlock(&mutex -> tx_linux_mutex, &ts);
+
+ /* Anything but the deadline expiring is a real failure to obtain the
+ mutex, so stop retrying and record it. */
+ if ((linux_status != 0) && (linux_status != ETIMEDOUT))
+ {
+
+ /* Increment the system error counter. */
+ _tx_linux_system_error++;
+ break;
+ }
+
+ } while (linux_status != 0);
/* At this point we have the mutex. */
diff --git a/ports_smp/linux/gnu/src/tx_thread_smp_unprotect.c b/ports_smp/linux/gnu/src/tx_thread_smp_unprotect.c
index 7428666f..2e0e7f1e 100644
--- a/ports_smp/linux/gnu/src/tx_thread_smp_unprotect.c
+++ b/ports_smp/linux/gnu/src/tx_thread_smp_unprotect.c
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Portions of this file were generated with AI assistance.
+
/**************************************************************************/
/**************************************************************************/
@@ -124,11 +126,18 @@ pthread_t current_thread_id;
_tx_linux_debug_entry_insert("UNPROTECT-nested", __FILE__, __LINE__);
}
- /* Only release the critical section. */
- _tx_linux_mutex_release(&_tx_linux_mutex);
}
- /* Release the critical section. */
+ /* Release the critical section taken by the matching _tx_thread_smp_protect.
+ The protection and the critical section are separate counts, and this
+ release is owed whether or not the protection still names this core: the
+ protection can be cleared by another core while this one holds the
+ critical section it took. The critical section is only handed back to
+ Linux when its nesting count reaches zero, so a release skipped here is
+ never made up and the mutex stays locked for the life of the process. */
+ _tx_linux_mutex_release(&_tx_linux_mutex);
+
+ /* Release the critical section taken on entry. */
_tx_linux_mutex_release(&_tx_linux_mutex);
}
diff --git a/ports_smp/linux/gnu/src/tx_thread_stack_build.c b/ports_smp/linux/gnu/src/tx_thread_stack_build.c
index 1b491a3a..65b8e25c 100644
--- a/ports_smp/linux/gnu/src/tx_thread_stack_build.c
+++ b/ports_smp/linux/gnu/src/tx_thread_stack_build.c
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Portions of this file were generated with AI assistance.
+
/**************************************************************************/
/**************************************************************************/
@@ -80,6 +82,7 @@ void *_tx_linux_thread_entry(void *ptr);
/**************************************************************************/
VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID))
{
+ALIGN_TYPE fake_stack_ptr;
struct sched_param sp;
pthread_attr_t attrs;
@@ -120,8 +123,13 @@ pthread_attr_t attrs;
tx_interrupt_control nesting. */
thread_ptr -> tx_thread_linux_mutex_access = TX_FALSE;
- /* Setup a fake thread stack pointer. */
- thread_ptr -> tx_thread_stack_ptr = (VOID *) (((CHAR *) thread_ptr -> tx_thread_stack_end) - 8);
+ /* Setup a fake thread stack pointer. The stack end points at the last byte of the
+ thread's stack area and is therefore not necessarily aligned, so round the result
+ down to a ULONG boundary. This pointer is dereferenced as a ULONG below and is
+ also the starting value of the stack checking logic's highest used pointer. */
+ fake_stack_ptr = (ALIGN_TYPE) ((VOID *) (((CHAR *) thread_ptr -> tx_thread_stack_end) - 8));
+ fake_stack_ptr = fake_stack_ptr & (~((ALIGN_TYPE) (sizeof(ULONG) - 1)));
+ thread_ptr -> tx_thread_stack_ptr = (VOID *) ((ALIGN_TYPE) fake_stack_ptr);
/* Clear the first word of the stack. */
*(((ULONG *) thread_ptr -> tx_thread_stack_ptr) - 1) = 0;