diff options
| author | Frédéric Desbiens <[email protected]> | 2026-10-02 10:42:44 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-10-02 10:42:44 -0400 |
| commit | 93387b0a60382a29579bbad5045c68328e3751df (patch) | |
| tree | 42be790d602cb76f55090e5a7c418df7ea2b3428 /ports_smp/linux/gnu | |
| parent | 44d7c95c582d415c4ad84527180b29c93c3bf664 (diff) | |
| parent | 28281cd5647d4671236ca08d19bd18ed15b2265a (diff) | |
Merge pull request #783 from eclipse-threadx/devv6.5.2.202603_rel
Merged changes ahead of the v6.5.2.202603 release
Diffstat (limited to 'ports_smp/linux/gnu')
| -rw-r--r-- | ports_smp/linux/gnu/CMakeLists.txt | 41 | ||||
| -rw-r--r-- | ports_smp/linux/gnu/example_build/CMakeLists.txt | 17 | ||||
| -rw-r--r-- | ports_smp/linux/gnu/inc/tx_port.h | 28 | ||||
| -rw-r--r-- | ports_smp/linux/gnu/src/tx_thread_schedule.c | 41 | ||||
| -rw-r--r-- | ports_smp/linux/gnu/src/tx_thread_smp_unprotect.c | 15 | ||||
| -rw-r--r-- | ports_smp/linux/gnu/src/tx_thread_stack_build.c | 12 |
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; |
