diff --git a/common/inc/tx_mutex.h b/common/inc/tx_mutex.h index b0b46c2f9..f062cc4a5 100644 --- a/common/inc/tx_mutex.h +++ b/common/inc/tx_mutex.h @@ -84,6 +84,7 @@ VOID _tx_mutex_initialize(VOID); VOID _tx_mutex_cleanup(TX_THREAD *thread_ptr, ULONG suspension_sequence); VOID _tx_mutex_thread_release(TX_THREAD *thread_ptr); VOID _tx_mutex_priority_change(TX_THREAD *thread_ptr, UINT new_priority); +VOID _tx_mutex_inherit_priority_update(TX_MUTEX *mutex_ptr); /* Mutex management component data declarations follow. */ diff --git a/common/inc/tx_thread.h b/common/inc/tx_thread.h index 5ba8b3f54..cbfecbbc7 100644 --- a/common/inc/tx_thread.h +++ b/common/inc/tx_thread.h @@ -390,6 +390,13 @@ THREAD_DECLARE volatile UINT _tx_thread_preempt_disable; THREAD_DECLARE VOID (*_tx_thread_mutex_release)(TX_THREAD *thread_ptr); +/* Define the global function pointer that updates the priority inheritance of a mutex + after the priority of a thread waiting on it changes. This pointer is setup during + mutex initialization. */ + +THREAD_DECLARE VOID (*_tx_thread_mutex_priority_update)(TX_MUTEX *mutex_ptr); + + /* Define the global build options variable. This contains a bit map representing how the ThreadX library was built. The following are the bit field definitions: diff --git a/common/src/tx_mutex_cleanup.c b/common/src/tx_mutex_cleanup.c index d32f69fc0..3adb21ada 100644 --- a/common/src/tx_mutex_cleanup.c +++ b/common/src/tx_mutex_cleanup.c @@ -307,3 +307,182 @@ UINT status; TX_RESTORE } + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_mutex_inherit_priority_update PORTABLE C */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function brings the priority inheritance of a mutex and of */ +/* its owner up to date after the set of threads waiting on the mutex, */ +/* or the priority of one of them, has changed other than through */ +/* tx_mutex_get or tx_mutex_put. The highest priority waiting on the */ +/* mutex is recomputed from its suspension list, the owner's inherited */ +/* priority is recomputed from every priority inheritance mutex it */ +/* owns, and the owner's priority is changed if it differs from the */ +/* higher of that and its own user priority. */ +/* */ +/* Unless TX_NOT_INTERRUPTABLE is defined, the caller must have */ +/* preemption disabled and interrupts enabled, as for */ +/* _tx_mutex_priority_change. Otherwise interrupts must be disabled. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_mutex_prioritize Move highest priority waiter to */ +/* the front of the list */ +/* _tx_mutex_priority_change Change owner's priority */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX internal code */ +/* */ +/**************************************************************************/ +VOID _tx_mutex_inherit_priority_update(TX_MUTEX *mutex_ptr) +{ + +#ifndef TX_NOT_INTERRUPTABLE +TX_INTERRUPT_SAVE_AREA +#endif + +TX_THREAD *owner_ptr; +TX_THREAD *suspended_thread; +TX_MUTEX *next_mutex; +UINT inheritance_priority; +UINT new_priority; +#ifdef TX_MISRA_ENABLE +UINT status; +#endif + + + /* Determine if there is more than one thread suspended on the mutex. */ + if (mutex_ptr -> tx_mutex_suspended_count > ((UINT) 1)) + { + + /* Yes, prioritize the list so the highest priority thread is placed at the + front of the suspension list. */ +#ifdef TX_MISRA_ENABLE + do + { + status = _tx_mutex_prioritize(mutex_ptr); + } while (status != TX_SUCCESS); +#else + _tx_mutex_prioritize(mutex_ptr); +#endif + } + +#ifndef TX_NOT_INTERRUPTABLE + + /* Disable interrupts. */ + TX_DISABLE +#endif + + /* Pickup the thread at the front of the suspension list. */ + suspended_thread = mutex_ptr -> tx_mutex_suspension_list; + + /* Determine if there still are threads suspended for this mutex. */ + if (suspended_thread != TX_NULL) + { + + /* Setup the highest priority thread waiting on this mutex. */ + mutex_ptr -> tx_mutex_highest_priority_waiting = suspended_thread -> tx_thread_priority; + } + else + { + + /* No thread is waiting on this mutex. */ + mutex_ptr -> tx_mutex_highest_priority_waiting = ((UINT) TX_MAX_PRIORITIES); + } + + /* Pickup the mutex owner. */ + owner_ptr = mutex_ptr -> tx_mutex_owner; + +#ifndef TX_NOT_INTERRUPTABLE + + /* Restore interrupts. */ + TX_RESTORE +#endif + + /* The owner is not NULL. A thread could only suspend on this mutex while a + thread owned it, since tx_mutex_get raises the owner's priority when it does, + and the owner keeps the mutex for as long as this processing can run. */ + + /* Default the inheritance priority to disabled. */ + inheritance_priority = ((UINT) TX_MAX_PRIORITIES); + + /* Search the mutexes owned by this thread to determine the highest priority + it still inherits. */ + next_mutex = owner_ptr -> tx_thread_owned_mutex_list; + while (next_mutex != TX_NULL) + { + + /* Does this mutex support priority inheritance? */ + if (next_mutex -> tx_mutex_inherit == TX_TRUE) + { + + /* Determine if highest priority field of the mutex is higher than the + inheritance priority so far. */ + if (next_mutex -> tx_mutex_highest_priority_waiting < inheritance_priority) + { + + /* Use this priority. */ + inheritance_priority = next_mutex -> tx_mutex_highest_priority_waiting; + } + } + + /* Move mutex pointer to the next mutex in the list. */ + next_mutex = next_mutex -> tx_mutex_owned_next; + + /* Are we at the end of the list? */ + if (next_mutex == owner_ptr -> tx_thread_owned_mutex_list) + { + + /* Yes, set the next mutex to NULL. */ + next_mutex = TX_NULL; + } + } + +#ifndef TX_NOT_INTERRUPTABLE + + /* Disable interrupts. */ + TX_DISABLE +#endif + + /* Set the inherit priority to that of the highest priority thread waiting on + any mutex the owner holds. */ + owner_ptr -> tx_thread_inherit_priority = inheritance_priority; + + /* The owner runs at the higher of its own priority and the inherited one. */ + new_priority = owner_ptr -> tx_thread_user_priority; + if (inheritance_priority < new_priority) + { + + /* Use the inherited priority. */ + new_priority = inheritance_priority; + } + +#ifndef TX_NOT_INTERRUPTABLE + + /* Restore interrupts. */ + TX_RESTORE +#endif + + /* Determine if the owner's priority has to change. */ + if (owner_ptr -> tx_thread_priority != new_priority) + { + + /* Yes, change the priority of the owner. */ + _tx_mutex_priority_change(owner_ptr, new_priority); + } +} diff --git a/common/src/tx_mutex_create.c b/common/src/tx_mutex_create.c index a1f5eba85..ed559f302 100644 --- a/common/src/tx_mutex_create.c +++ b/common/src/tx_mutex_create.c @@ -11,7 +11,6 @@ /* Portions of this file were generated with AI assistance. */ - /**************************************************************************/ /**************************************************************************/ /** */ @@ -92,6 +91,9 @@ TX_MUTEX *previous_mutex; /* Setup the thread mutex release function pointer. */ _tx_thread_mutex_release = &(_tx_mutex_thread_release); + /* Setup the thread mutex priority update function pointer. */ + _tx_thread_mutex_priority_update = &(_tx_mutex_inherit_priority_update); + /* Place the mutex on the list of created mutexes. First, check for an empty list. */ if (_tx_mutex_created_count == TX_EMPTY) diff --git a/common/src/tx_thread_initialize.c b/common/src/tx_thread_initialize.c index c9cfea339..97b1072a7 100644 --- a/common/src/tx_thread_initialize.c +++ b/common/src/tx_thread_initialize.c @@ -139,6 +139,13 @@ volatile UINT _tx_thread_preempt_disable; VOID (*_tx_thread_mutex_release)(TX_THREAD *thread_ptr); +/* Define the global function pointer that updates the priority inheritance of a mutex + after the priority of a thread waiting on it changes. This pointer is setup during + mutex initialization. */ + +VOID (*_tx_thread_mutex_priority_update)(TX_MUTEX *mutex_ptr); + + /* Define the global build options variable. This contains a bit map representing how the ThreadX library was built. The following are the bit field definitions: @@ -348,6 +355,9 @@ VOID _tx_thread_initialize(VOID) /* Initialize the thread mutex release function pointer. */ _tx_thread_mutex_release = TX_NULL; + /* Initialize the thread mutex priority update function pointer. */ + _tx_thread_mutex_priority_update = TX_NULL; + #ifdef TX_ENABLE_STACK_CHECKING /* Clear application registered stack error handler. */ diff --git a/common/src/tx_thread_priority_change.c b/common/src/tx_thread_priority_change.c index bdf2a9930..8479526d1 100644 --- a/common/src/tx_thread_priority_change.c +++ b/common/src/tx_thread_priority_change.c @@ -66,6 +66,7 @@ /* _tx_thread_system_ni_resume Non-interruptable resume thread */ /* _tx_thread_system_suspend Suspend thread */ /* _tx_thread_system_ni_suspend Non-interruptable suspend thread */ +/* _tx_thread_mutex_priority_update Update mutex inheritance */ /* _tx_thread_system_preempt_check Check for preemption */ /* */ /* CALLED BY */ @@ -79,6 +80,7 @@ UINT _tx_thread_priority_change(TX_THREAD *thread_ptr, UINT new_priority, UINT TX_INTERRUPT_SAVE_AREA TX_THREAD *execute_ptr; +TX_MUTEX *mutex_ptr; TX_THREAD *next_execute_ptr; UINT original_priority; @@ -121,8 +123,48 @@ UINT original_priority; thread_ptr -> tx_thread_preempt_threshold = thread_ptr -> tx_thread_inherit_priority; } - /* Restore interrupts. */ - TX_RESTORE + /* Determine if the thread is waiting on a mutex. */ + if ((thread_ptr -> tx_thread_state == TX_MUTEX_SUSP) && (thread_ptr -> tx_thread_suspend_cleanup != TX_NULL)) + { + + /* Yes, the mutex owner may have inherited the old priority of this thread, or + may now have to inherit the new one. The update function pointer is set, + since a mutex has been created. */ + mutex_ptr = TX_VOID_TO_MUTEX_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block); + +#ifndef TX_NOT_INTERRUPTABLE + + /* Temporarily disable preemption. */ + _tx_thread_preempt_disable++; + + /* Restore interrupts. */ + TX_RESTORE +#endif + + /* Update the priority inheritance of the mutex owner. */ + (_tx_thread_mutex_priority_update)(mutex_ptr); + +#ifndef TX_NOT_INTERRUPTABLE + + /* Disable interrupts. */ + TX_DISABLE + + /* Back off the preemption disable. */ + _tx_thread_preempt_disable--; +#endif + + /* Restore interrupts. */ + TX_RESTORE + + /* Check for preemption. */ + _tx_thread_system_preempt_check(); + } + else + { + + /* Restore interrupts. */ + TX_RESTORE + } } else { diff --git a/common_smp/inc/tx_mutex.h b/common_smp/inc/tx_mutex.h index b0b46c2f9..f062cc4a5 100644 --- a/common_smp/inc/tx_mutex.h +++ b/common_smp/inc/tx_mutex.h @@ -84,6 +84,7 @@ VOID _tx_mutex_initialize(VOID); VOID _tx_mutex_cleanup(TX_THREAD *thread_ptr, ULONG suspension_sequence); VOID _tx_mutex_thread_release(TX_THREAD *thread_ptr); VOID _tx_mutex_priority_change(TX_THREAD *thread_ptr, UINT new_priority); +VOID _tx_mutex_inherit_priority_update(TX_MUTEX *mutex_ptr); /* Mutex management component data declarations follow. */ diff --git a/common_smp/inc/tx_thread.h b/common_smp/inc/tx_thread.h index 1ffe88287..f8ce03c7d 100644 --- a/common_smp/inc/tx_thread.h +++ b/common_smp/inc/tx_thread.h @@ -315,6 +315,13 @@ THREAD_DECLARE volatile UINT _tx_thread_preempt_disable; THREAD_DECLARE VOID (*_tx_thread_mutex_release)(TX_THREAD *thread_ptr); +/* Define the global function pointer that updates the priority inheritance of a mutex + after the priority of a thread waiting on it changes. This pointer is setup during + mutex initialization. */ + +THREAD_DECLARE VOID (*_tx_thread_mutex_priority_update)(TX_MUTEX *mutex_ptr); + + /* Define the global build options variable. This contains a bit map representing how the ThreadX library was built. The following are the bit field definitions: diff --git a/common_smp/src/tx_mutex_cleanup.c b/common_smp/src/tx_mutex_cleanup.c index d32f69fc0..3adb21ada 100644 --- a/common_smp/src/tx_mutex_cleanup.c +++ b/common_smp/src/tx_mutex_cleanup.c @@ -307,3 +307,182 @@ UINT status; TX_RESTORE } + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_mutex_inherit_priority_update PORTABLE C */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function brings the priority inheritance of a mutex and of */ +/* its owner up to date after the set of threads waiting on the mutex, */ +/* or the priority of one of them, has changed other than through */ +/* tx_mutex_get or tx_mutex_put. The highest priority waiting on the */ +/* mutex is recomputed from its suspension list, the owner's inherited */ +/* priority is recomputed from every priority inheritance mutex it */ +/* owns, and the owner's priority is changed if it differs from the */ +/* higher of that and its own user priority. */ +/* */ +/* Unless TX_NOT_INTERRUPTABLE is defined, the caller must have */ +/* preemption disabled and interrupts enabled, as for */ +/* _tx_mutex_priority_change. Otherwise interrupts must be disabled. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_mutex_prioritize Move highest priority waiter to */ +/* the front of the list */ +/* _tx_mutex_priority_change Change owner's priority */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX internal code */ +/* */ +/**************************************************************************/ +VOID _tx_mutex_inherit_priority_update(TX_MUTEX *mutex_ptr) +{ + +#ifndef TX_NOT_INTERRUPTABLE +TX_INTERRUPT_SAVE_AREA +#endif + +TX_THREAD *owner_ptr; +TX_THREAD *suspended_thread; +TX_MUTEX *next_mutex; +UINT inheritance_priority; +UINT new_priority; +#ifdef TX_MISRA_ENABLE +UINT status; +#endif + + + /* Determine if there is more than one thread suspended on the mutex. */ + if (mutex_ptr -> tx_mutex_suspended_count > ((UINT) 1)) + { + + /* Yes, prioritize the list so the highest priority thread is placed at the + front of the suspension list. */ +#ifdef TX_MISRA_ENABLE + do + { + status = _tx_mutex_prioritize(mutex_ptr); + } while (status != TX_SUCCESS); +#else + _tx_mutex_prioritize(mutex_ptr); +#endif + } + +#ifndef TX_NOT_INTERRUPTABLE + + /* Disable interrupts. */ + TX_DISABLE +#endif + + /* Pickup the thread at the front of the suspension list. */ + suspended_thread = mutex_ptr -> tx_mutex_suspension_list; + + /* Determine if there still are threads suspended for this mutex. */ + if (suspended_thread != TX_NULL) + { + + /* Setup the highest priority thread waiting on this mutex. */ + mutex_ptr -> tx_mutex_highest_priority_waiting = suspended_thread -> tx_thread_priority; + } + else + { + + /* No thread is waiting on this mutex. */ + mutex_ptr -> tx_mutex_highest_priority_waiting = ((UINT) TX_MAX_PRIORITIES); + } + + /* Pickup the mutex owner. */ + owner_ptr = mutex_ptr -> tx_mutex_owner; + +#ifndef TX_NOT_INTERRUPTABLE + + /* Restore interrupts. */ + TX_RESTORE +#endif + + /* The owner is not NULL. A thread could only suspend on this mutex while a + thread owned it, since tx_mutex_get raises the owner's priority when it does, + and the owner keeps the mutex for as long as this processing can run. */ + + /* Default the inheritance priority to disabled. */ + inheritance_priority = ((UINT) TX_MAX_PRIORITIES); + + /* Search the mutexes owned by this thread to determine the highest priority + it still inherits. */ + next_mutex = owner_ptr -> tx_thread_owned_mutex_list; + while (next_mutex != TX_NULL) + { + + /* Does this mutex support priority inheritance? */ + if (next_mutex -> tx_mutex_inherit == TX_TRUE) + { + + /* Determine if highest priority field of the mutex is higher than the + inheritance priority so far. */ + if (next_mutex -> tx_mutex_highest_priority_waiting < inheritance_priority) + { + + /* Use this priority. */ + inheritance_priority = next_mutex -> tx_mutex_highest_priority_waiting; + } + } + + /* Move mutex pointer to the next mutex in the list. */ + next_mutex = next_mutex -> tx_mutex_owned_next; + + /* Are we at the end of the list? */ + if (next_mutex == owner_ptr -> tx_thread_owned_mutex_list) + { + + /* Yes, set the next mutex to NULL. */ + next_mutex = TX_NULL; + } + } + +#ifndef TX_NOT_INTERRUPTABLE + + /* Disable interrupts. */ + TX_DISABLE +#endif + + /* Set the inherit priority to that of the highest priority thread waiting on + any mutex the owner holds. */ + owner_ptr -> tx_thread_inherit_priority = inheritance_priority; + + /* The owner runs at the higher of its own priority and the inherited one. */ + new_priority = owner_ptr -> tx_thread_user_priority; + if (inheritance_priority < new_priority) + { + + /* Use the inherited priority. */ + new_priority = inheritance_priority; + } + +#ifndef TX_NOT_INTERRUPTABLE + + /* Restore interrupts. */ + TX_RESTORE +#endif + + /* Determine if the owner's priority has to change. */ + if (owner_ptr -> tx_thread_priority != new_priority) + { + + /* Yes, change the priority of the owner. */ + _tx_mutex_priority_change(owner_ptr, new_priority); + } +} diff --git a/common_smp/src/tx_mutex_create.c b/common_smp/src/tx_mutex_create.c index a1f5eba85..ed559f302 100644 --- a/common_smp/src/tx_mutex_create.c +++ b/common_smp/src/tx_mutex_create.c @@ -11,7 +11,6 @@ /* Portions of this file were generated with AI assistance. */ - /**************************************************************************/ /**************************************************************************/ /** */ @@ -92,6 +91,9 @@ TX_MUTEX *previous_mutex; /* Setup the thread mutex release function pointer. */ _tx_thread_mutex_release = &(_tx_mutex_thread_release); + /* Setup the thread mutex priority update function pointer. */ + _tx_thread_mutex_priority_update = &(_tx_mutex_inherit_priority_update); + /* Place the mutex on the list of created mutexes. First, check for an empty list. */ if (_tx_mutex_created_count == TX_EMPTY) diff --git a/common_smp/src/tx_thread_initialize.c b/common_smp/src/tx_thread_initialize.c index f37931096..11a1003db 100644 --- a/common_smp/src/tx_thread_initialize.c +++ b/common_smp/src/tx_thread_initialize.c @@ -178,6 +178,13 @@ volatile UINT _tx_thread_preempt_disable; VOID (*_tx_thread_mutex_release)(TX_THREAD *thread_ptr); +/* Define the global function pointer that updates the priority inheritance of a mutex + after the priority of a thread waiting on it changes. This pointer is setup during + mutex initialization. */ + +VOID (*_tx_thread_mutex_priority_update)(TX_MUTEX *mutex_ptr); + + /* Define the global build options variable. This contains a bit map representing how the ThreadX library was built. The following are the bit field definitions: @@ -386,6 +393,9 @@ VOID _tx_thread_initialize(VOID) /* Initialize the thread mutex release function pointer. */ _tx_thread_mutex_release = TX_NULL; + /* Initialize the thread mutex priority update function pointer. */ + _tx_thread_mutex_priority_update = TX_NULL; + #ifdef TX_ENABLE_STACK_CHECKING /* Clear application registered stack error handler. */ diff --git a/common_smp/src/tx_thread_priority_change.c b/common_smp/src/tx_thread_priority_change.c index 57d6eda79..a13d6477d 100644 --- a/common_smp/src/tx_thread_priority_change.c +++ b/common_smp/src/tx_thread_priority_change.c @@ -69,6 +69,7 @@ /* _tx_thread_system_ni_resume Non-interruptable resume */ /* _tx_thread_system_suspend Suspend thread */ /* _tx_thread_system_ni_suspend Non-interruptable suspend */ +/* _tx_thread_mutex_priority_update Update mutex inheritance */ /* _tx_thread_system_preempt_check Check for preemption */ /* */ /* CALLED BY */ @@ -82,6 +83,7 @@ UINT _tx_thread_priority_change(TX_THREAD *thread_ptr, UINT new_priority, UINT TX_INTERRUPT_SAVE_AREA TX_THREAD *execute_ptr; +TX_MUTEX *mutex_ptr; UINT core_index; UINT original_priority; UINT lowest_priority; @@ -175,8 +177,48 @@ UINT status; thread_ptr -> tx_thread_preempt_threshold = thread_ptr -> tx_thread_inherit_priority; } - /* Restore interrupts. */ - TX_RESTORE + /* Determine if the thread is waiting on a mutex. */ + if ((thread_ptr -> tx_thread_state == TX_MUTEX_SUSP) && (thread_ptr -> tx_thread_suspend_cleanup != TX_NULL)) + { + + /* Yes, the mutex owner may have inherited the old priority of this thread, or + may now have to inherit the new one. The update function pointer is set, + since a mutex has been created. */ + mutex_ptr = TX_VOID_TO_MUTEX_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block); + +#ifndef TX_NOT_INTERRUPTABLE + + /* Temporarily disable preemption. */ + _tx_thread_preempt_disable++; + + /* Restore interrupts. */ + TX_RESTORE +#endif + + /* Update the priority inheritance of the mutex owner. */ + (_tx_thread_mutex_priority_update)(mutex_ptr); + +#ifndef TX_NOT_INTERRUPTABLE + + /* Disable interrupts. */ + TX_DISABLE + + /* Back off the preemption disable. */ + _tx_thread_preempt_disable--; +#endif + + /* Restore interrupts. */ + TX_RESTORE + + /* Check for preemption. */ + _tx_thread_system_preempt_check(); + } + else + { + + /* Restore interrupts. */ + TX_RESTORE + } /* Done, return success. */ status = TX_SUCCESS; diff --git a/test/smp/cmake/regression/CMakeLists.txt b/test/smp/cmake/regression/CMakeLists.txt index cdf0c5859..2568b4516 100644 --- a/test/smp/cmake/regression/CMakeLists.txt +++ b/test/smp/cmake/regression/CMakeLists.txt @@ -47,6 +47,7 @@ set(regression_test_cases ${SOURCE_DIR}/threadx_mutex_delete_test.c ${SOURCE_DIR}/threadx_mutex_information_test.c ${SOURCE_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${SOURCE_DIR}/threadx_mutex_inherit_waiter_priority_change_test.c ${SOURCE_DIR}/threadx_mutex_no_preemption_test.c ${SOURCE_DIR}/threadx_mutex_preemption_test.c ${SOURCE_DIR}/threadx_mutex_priority_inheritance_test.c diff --git a/test/smp/regression/testcontrol.c b/test/smp/regression/testcontrol.c index c964dc03a..7781845d8 100644 --- a/test/smp/regression/testcontrol.c +++ b/test/smp/regression/testcontrol.c @@ -201,6 +201,7 @@ void threadx_mutex_preemption_application_define(void *); void threadx_mutext_no_preemption_application_define(void *); void threadx_mutex_suspension_timeout_application_define(void *); void threadx_mutex_thread_terminate_application_define(void *); +void threadx_mutex_inherit_waiter_priority_change_application_define(void *); void threadx_mutex_priority_inheritance_application_define(void *); void threadx_mutex_nested_priority_inheritance_application_define(void *); void threadx_mutex_prioritize_application_define(void *); @@ -348,6 +349,7 @@ TEST_ENTRY test_control_tests[] = threadx_mutext_no_preemption_application_define, threadx_mutex_suspension_timeout_application_define, threadx_mutex_thread_terminate_application_define, + threadx_mutex_inherit_waiter_priority_change_application_define, threadx_mutex_priority_inheritance_application_define, threadx_mutex_prioritize_application_define, threadx_mutex_nested_priority_inheritance_application_define, diff --git a/test/smp/regression/threadx_mutex_inherit_waiter_priority_change_test.c b/test/smp/regression/threadx_mutex_inherit_waiter_priority_change_test.c new file mode 100644 index 000000000..2444d0db6 --- /dev/null +++ b/test/smp/regression/threadx_mutex_inherit_waiter_priority_change_test.c @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/* This test checks that changing the priority of a thread waiting on a + priority-inheritance mutex is passed on to the mutex owner. Raising a waiter above + the owner must raise the owner, and lowering the waiter the owner inherited from + must lower the owner to the highest priority still waiting, or to its own. */ + +#include +#include "tx_api.h" + + +/* Wait a tick at a time, for at most 200 ticks, until a condition holds. A thread the + control thread starts can take more than a tick to get there on an SMP host. */ + +#define WAIT_UNTIL(condition) { UINT ticks = 0; while ((!(condition)) && (ticks < 200)) { tx_thread_sleep(1); ticks++; } } + +#define WAITERS 2 +#define STEPS 4 + + +static TX_THREAD control_thread; +static TX_THREAD owner_thread; +static TX_THREAD waiter[WAITERS]; +static CHAR *thread_stack[WAITERS + 1]; +static TX_MUTEX mutex_0; + + +/* Define thread prototypes. */ + +static void control_thread_entry(ULONG thread_input); + + +/* Prototype for test control return. */ + +void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void threadx_mutex_inherit_waiter_priority_change_application_define(void *first_unused_memory) +#endif +{ + +UINT status; +CHAR *pointer; +UINT i; + + + /* Put first available memory address into a character pointer. */ + pointer = (CHAR *) first_unused_memory; + + status = tx_thread_create(&control_thread, "control thread", control_thread_entry, 0, + pointer, TEST_STACK_SIZE_PRINTF, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + TEST_STACK_SIZE_PRINTF; + + /* The remaining threads are created by the control thread. */ + for (i = 0; i <= WAITERS; i++) + { + thread_stack[i] = pointer; + pointer = pointer + TEST_STACK_SIZE_PRINTF; + } + + status += tx_mutex_create(&mutex_0, "mutex 0", TX_INHERIT); + + /* Check status. */ + if (status != TX_SUCCESS) + { + + printf("Running Mutex Inherit Waiter Priority Change Test................... ERROR #1\n"); + test_control_return(1); + } +} + + +/* The owner takes the mutex and parks, and the waiters block on it. */ + +static void mutex_thread_entry(ULONG thread_input) +{ + + tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + tx_thread_suspend(tx_thread_identify()); +} + + +static void check(UINT condition, UINT error) +{ + + if (condition == TX_FALSE) + { + + printf("ERROR #%u\n", error); + test_control_return(1); + } +} + + +static void start_thread(TX_THREAD *thread_ptr, UINT stack, UINT priority) +{ + + check(tx_thread_create(thread_ptr, "test thread", mutex_thread_entry, 0, thread_stack[stack], TEST_STACK_SIZE_PRINTF, + priority, priority, TX_NO_TIME_SLICE, TX_AUTO_START) == TX_SUCCESS, 2); +} + + +/* The owner at 15 holds the mutex, with waiter 0 at 20 and waiter 1 at 8, so the owner + runs at 8. Each step changes the priority of one waiter, and the owner must follow + the highest priority left waiting. */ + +static void control_thread_entry(ULONG thread_input) +{ + +static const UINT step_waiter[STEPS] = {0, 0, 1, 0}; +static const UINT step_priority[STEPS] = {5, 12, 20, 20}; +static const UINT step_highest_waiting[STEPS] = {5, 8, 12, 20}; +static const UINT step_owner_priority[STEPS] = {5, 8, 12, 15}; +UINT old_priority; +UINT i; + + + /* Inform user. */ + printf("Running Mutex Inherit Waiter Priority Change Test................... "); + + start_thread(&owner_thread, WAITERS, 15); + WAIT_UNTIL((mutex_0.tx_mutex_owner == &owner_thread) && (owner_thread.tx_thread_state == TX_SUSPENDED)) + start_thread(&waiter[0], 0, 20); + start_thread(&waiter[1], 1, 8); + WAIT_UNTIL(mutex_0.tx_mutex_suspended_count == WAITERS) + check(owner_thread.tx_thread_priority == 8, 10); + + for (i = 0; i < STEPS; i++) + { + tx_thread_priority_change(&waiter[step_waiter[i]], step_priority[i], &old_priority); + check(waiter[step_waiter[i]].tx_thread_state == TX_MUTEX_SUSP, 20 + i); + check(mutex_0.tx_mutex_highest_priority_waiting == step_highest_waiting[i], 30 + i); + check(owner_thread.tx_thread_priority == step_owner_priority[i], 40 + i); + } + + /* Successful test. */ + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/tx/cmake/regression/CMakeLists.txt b/test/tx/cmake/regression/CMakeLists.txt index 6b94a8576..4900c36f6 100644 --- a/test/tx/cmake/regression/CMakeLists.txt +++ b/test/tx/cmake/regression/CMakeLists.txt @@ -44,6 +44,7 @@ set(regression_test_cases ${SOURCE_DIR}/threadx_mutex_delete_test.c ${SOURCE_DIR}/threadx_mutex_information_test.c ${SOURCE_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${SOURCE_DIR}/threadx_mutex_inherit_waiter_priority_change_test.c ${SOURCE_DIR}/threadx_mutex_no_preemption_test.c ${SOURCE_DIR}/threadx_mutex_preemption_test.c ${SOURCE_DIR}/threadx_mutex_priority_inheritance_test.c diff --git a/test/tx/regression/testcontrol.c b/test/tx/regression/testcontrol.c index bec50ee76..e9e5daa5d 100644 --- a/test/tx/regression/testcontrol.c +++ b/test/tx/regression/testcontrol.c @@ -173,6 +173,7 @@ void threadx_mutex_preemption_application_define(void *); void threadx_mutext_no_preemption_application_define(void *); void threadx_mutex_suspension_timeout_application_define(void *); void threadx_mutex_thread_terminate_application_define(void *); +void threadx_mutex_inherit_waiter_priority_change_application_define(void *); void threadx_mutex_priority_inheritance_application_define(void *); void threadx_mutex_nested_priority_inheritance_application_define(void *); void threadx_mutex_prioritize_application_define(void *); @@ -292,6 +293,7 @@ TEST_ENTRY test_control_tests[] = threadx_mutext_no_preemption_application_define, threadx_mutex_suspension_timeout_application_define, threadx_mutex_thread_terminate_application_define, + threadx_mutex_inherit_waiter_priority_change_application_define, threadx_mutex_priority_inheritance_application_define, threadx_mutex_prioritize_application_define, threadx_mutex_nested_priority_inheritance_application_define, diff --git a/test/tx/regression/threadx_mutex_inherit_waiter_priority_change_test.c b/test/tx/regression/threadx_mutex_inherit_waiter_priority_change_test.c new file mode 100644 index 000000000..2444d0db6 --- /dev/null +++ b/test/tx/regression/threadx_mutex_inherit_waiter_priority_change_test.c @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/* This test checks that changing the priority of a thread waiting on a + priority-inheritance mutex is passed on to the mutex owner. Raising a waiter above + the owner must raise the owner, and lowering the waiter the owner inherited from + must lower the owner to the highest priority still waiting, or to its own. */ + +#include +#include "tx_api.h" + + +/* Wait a tick at a time, for at most 200 ticks, until a condition holds. A thread the + control thread starts can take more than a tick to get there on an SMP host. */ + +#define WAIT_UNTIL(condition) { UINT ticks = 0; while ((!(condition)) && (ticks < 200)) { tx_thread_sleep(1); ticks++; } } + +#define WAITERS 2 +#define STEPS 4 + + +static TX_THREAD control_thread; +static TX_THREAD owner_thread; +static TX_THREAD waiter[WAITERS]; +static CHAR *thread_stack[WAITERS + 1]; +static TX_MUTEX mutex_0; + + +/* Define thread prototypes. */ + +static void control_thread_entry(ULONG thread_input); + + +/* Prototype for test control return. */ + +void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void threadx_mutex_inherit_waiter_priority_change_application_define(void *first_unused_memory) +#endif +{ + +UINT status; +CHAR *pointer; +UINT i; + + + /* Put first available memory address into a character pointer. */ + pointer = (CHAR *) first_unused_memory; + + status = tx_thread_create(&control_thread, "control thread", control_thread_entry, 0, + pointer, TEST_STACK_SIZE_PRINTF, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + TEST_STACK_SIZE_PRINTF; + + /* The remaining threads are created by the control thread. */ + for (i = 0; i <= WAITERS; i++) + { + thread_stack[i] = pointer; + pointer = pointer + TEST_STACK_SIZE_PRINTF; + } + + status += tx_mutex_create(&mutex_0, "mutex 0", TX_INHERIT); + + /* Check status. */ + if (status != TX_SUCCESS) + { + + printf("Running Mutex Inherit Waiter Priority Change Test................... ERROR #1\n"); + test_control_return(1); + } +} + + +/* The owner takes the mutex and parks, and the waiters block on it. */ + +static void mutex_thread_entry(ULONG thread_input) +{ + + tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + tx_thread_suspend(tx_thread_identify()); +} + + +static void check(UINT condition, UINT error) +{ + + if (condition == TX_FALSE) + { + + printf("ERROR #%u\n", error); + test_control_return(1); + } +} + + +static void start_thread(TX_THREAD *thread_ptr, UINT stack, UINT priority) +{ + + check(tx_thread_create(thread_ptr, "test thread", mutex_thread_entry, 0, thread_stack[stack], TEST_STACK_SIZE_PRINTF, + priority, priority, TX_NO_TIME_SLICE, TX_AUTO_START) == TX_SUCCESS, 2); +} + + +/* The owner at 15 holds the mutex, with waiter 0 at 20 and waiter 1 at 8, so the owner + runs at 8. Each step changes the priority of one waiter, and the owner must follow + the highest priority left waiting. */ + +static void control_thread_entry(ULONG thread_input) +{ + +static const UINT step_waiter[STEPS] = {0, 0, 1, 0}; +static const UINT step_priority[STEPS] = {5, 12, 20, 20}; +static const UINT step_highest_waiting[STEPS] = {5, 8, 12, 20}; +static const UINT step_owner_priority[STEPS] = {5, 8, 12, 15}; +UINT old_priority; +UINT i; + + + /* Inform user. */ + printf("Running Mutex Inherit Waiter Priority Change Test................... "); + + start_thread(&owner_thread, WAITERS, 15); + WAIT_UNTIL((mutex_0.tx_mutex_owner == &owner_thread) && (owner_thread.tx_thread_state == TX_SUSPENDED)) + start_thread(&waiter[0], 0, 20); + start_thread(&waiter[1], 1, 8); + WAIT_UNTIL(mutex_0.tx_mutex_suspended_count == WAITERS) + check(owner_thread.tx_thread_priority == 8, 10); + + for (i = 0; i < STEPS; i++) + { + tx_thread_priority_change(&waiter[step_waiter[i]], step_priority[i], &old_priority); + check(waiter[step_waiter[i]].tx_thread_state == TX_MUTEX_SUSP, 20 + i); + check(mutex_0.tx_mutex_highest_priority_waiting == step_highest_waiting[i], 30 + i); + check(owner_thread.tx_thread_priority == step_owner_priority[i], 40 + i); + } + + /* Successful test. */ + printf("SUCCESS!\n"); + test_control_return(0); +}