This commit is contained in:
shrutik pradip balwan 2026-09-19 15:26:37 +00:00 • committed by GitHub
commit 2fe1ef80b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 275 additions and 6 deletions

View file

@ -129,6 +129,8 @@ static void prvInitThreadKey( void );
static void prvMarkAsFreeRTOSThread( void );
static BaseType_t prvIsFreeRTOSThread( void );
static void prvDestroyThreadKey( void );
static void prvFatalError( const char * pcCall,
int iErrno ) __attribute__( ( __noreturn__ ) );
/*-----------------------------------------------------------*/
static void prvThreadKeyDestructor( void * pvData )
@ -139,7 +141,13 @@ static void prvThreadKeyDestructor( void * pvData )
static void prvInitThreadKey( void )
{
pthread_key_create( &xThreadKey, prvThreadKeyDestructor );
int iRet = pthread_key_create( &xThreadKey, prvThreadKeyDestructor );
if( iRet != 0 )
{
prvFatalError( "pthread_key_create", iRet );
}
/* Destroy xThreadKey when the process exits. */
atexit( prvDestroyThreadKey );
}
@ -148,15 +156,26 @@ static void prvInitThreadKey( void )
static void prvMarkAsFreeRTOSThread( void )
{
uint8_t * pucThreadData = NULL;
int iRet;
( void ) pthread_once( &hThreadKeyOnce, prvInitThreadKey );
pucThreadData = malloc( 1 );
configASSERT( pucThreadData != NULL );
if( pucThreadData == NULL )
{
prvFatalError( "malloc", ENOMEM );
}
*pucThreadData = 1;
pthread_setspecific( xThreadKey, pucThreadData );
iRet = pthread_setspecific( xThreadKey, pucThreadData );
if( iRet != 0 )
{
free( pucThreadData );
prvFatalError( "pthread_setspecific", iRet );
}
}
/*-----------------------------------------------------------*/
@ -184,9 +203,6 @@ static void prvDestroyThreadKey( void )
}
/*-----------------------------------------------------------*/
static void prvFatalError( const char * pcCall,
int iErrno ) __attribute__( ( __noreturn__ ) );
void prvFatalError( const char * pcCall,
int iErrno )
{

View file

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.15)
project(posix_port_tests C)
enable_testing()
find_package(Threads REQUIRED)
get_filename_component(KERNEL_ROOT "../../../../.." ABSOLUTE)
add_library(freertos_config INTERFACE)
target_include_directories(freertos_config INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
set(FREERTOS_PORT GCC_POSIX CACHE STRING "" FORCE)
set(FREERTOS_HEAP 4 CACHE STRING "" FORCE)
add_subdirectory("${KERNEL_ROOT}" kernel)
foreach(assert_enabled 0 1)
set(target thread_marker_${assert_enabled})
add_executable(${target} thread_marker.c)
target_include_directories(${target} PRIVATE . .. "${KERNEL_ROOT}/include")
target_compile_definitions(${target} PRIVATE TEST_ASSERT_ENABLED=${assert_enabled})
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror -ffunction-sections -fdata-sections)
target_link_libraries(${target} PRIVATE Threads::Threads)
target_link_options(${target} PRIVATE -Wl,--gc-sections
-Wl,--wrap=pthread_key_create -Wl,--wrap=pthread_setspecific
-Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=abort)
foreach(scenario success key_create key_query allocation setspecific)
add_test(NAME ${target}_${scenario} COMMAND ${target} ${scenario})
set_tests_properties(${target}_${scenario} PROPERTIES TIMEOUT 10)
endforeach()
endforeach()
add_executable(scheduler_smoke scheduler_smoke.c)
target_link_libraries(scheduler_smoke PRIVATE freertos_kernel)
target_compile_options(scheduler_smoke PRIVATE -Wall -Wextra -Werror)
add_test(NAME scheduler_smoke COMMAND scheduler_smoke)
set_tests_properties(scheduler_smoke PROPERTIES TIMEOUT 10)

View file

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: MIT */
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include <stdlib.h>
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configTICK_RATE_HZ 100
#define configMAX_PRIORITIES 4
#define configMINIMAL_STACK_SIZE 4096
#define configTOTAL_HEAP_SIZE ( 256 * 1024 )
#define configMAX_TASK_NAME_LEN 16
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configUSE_TIMERS 0
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#if TEST_ASSERT_ENABLED
#define configASSERT( x ) do { if( !( x ) ) { abort(); } } while( 0 )
#endif
#endif /* ifndef FREERTOS_CONFIG_H */

View file

@ -0,0 +1,23 @@
# POSIX thread-marker regression tests
Run on Linux with GCC, CMake, and a GNU-compatible linker:
```sh
cmake -S portable/ThirdParty/GCC/Posix/tests -B build/posix-tests
cmake --build build/posix-tests
ctest --test-dir build/posix-tests --output-on-failure
```
The failure-injection executable includes the actual port implementation to
reach its private helpers. Linker wrappers inject key-creation, allocation, and
TLS-storage failures. Each failure must terminate via the fatal handler; failed
TLS storage must first free the allocated marker. Tests run with assertions
both enabled and disabled. The success case checks thread identity, isolation
from the calling thread, and automatic destructor cleanup after pthread exit.
The separate smoke test links the full kernel and runs a real scheduled task
through a tick delay and scheduler shutdown.
For sanitizer validation, configure another build directory with
`-DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"`.
These tests are specific to the POSIX simulator; they do not validate hardware
ports. The kernel's main CMock suite lives in the parent FreeRTOS repository.

View file

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: MIT */
#include "FreeRTOS.h"
#include "task.h"
static volatile int iTaskRan;
static void prvTask( void * pvUnused )
{
( void ) pvUnused;
vTaskDelay( 2 );
iTaskRan = 1;
vTaskEndScheduler();
}
int main( void )
{
if( xTaskCreate( prvTask, "smoke", configMINIMAL_STACK_SIZE, NULL, 1, NULL ) != pdPASS )
{
return EXIT_FAILURE;
}
vTaskStartScheduler();
return iTaskRan ? EXIT_SUCCESS : EXIT_FAILURE;
}

View file

@ -0,0 +1,145 @@
/* SPDX-License-Identifier: MIT */
/* Include the implementation to exercise its private TLS helpers unchanged.
* Unused scheduler sections are discarded by the linker. */
#include "../port.c"
static const char * pcScenario;
static void * pvMarker;
static int iAllocations;
static int iFrees;
static int iStores;
extern void * __real_malloc( size_t xSize );
extern void __real_free( void * pvPointer );
extern int __real_pthread_key_create( pthread_key_t * pxKey,
void ( * pxDestructor )( void * ) );
extern int __real_pthread_setspecific( pthread_key_t xKey,
const void * pvValue );
int __wrap_pthread_key_create( pthread_key_t * pxKey,
void ( * pxDestructor )( void * ) )
{
if( ( strcmp( pcScenario, "key_create" ) == 0 ) || ( strcmp( pcScenario, "key_query" ) == 0 ) )
{
return EAGAIN;
}
return __real_pthread_key_create( pxKey, pxDestructor );
}
void * __wrap_malloc( size_t xSize )
{
if( xSize == 1 )
{
iAllocations++;
if( strcmp( pcScenario, "allocation" ) == 0 )
{
return NULL;
}
pvMarker = __real_malloc( xSize );
return pvMarker;
}
return __real_malloc( xSize );
}
void __wrap_free( void * pvPointer )
{
if( ( pvPointer != NULL ) && ( pvPointer == pvMarker ) )
{
iFrees++;
}
__real_free( pvPointer );
}
int __wrap_pthread_setspecific( pthread_key_t xKey,
const void * pvValue )
{
iStores++;
if( strcmp( pcScenario, "setspecific" ) == 0 )
{
return ENOMEM;
}
return __real_pthread_setspecific( xKey, pvValue );
}
void __wrap_abort( void )
{
int iPassed = 0;
if( ( strcmp( pcScenario, "key_create" ) == 0 ) || ( strcmp( pcScenario, "key_query" ) == 0 ) )
{
iPassed = ( iAllocations == 0 ) && ( iStores == 0 );
}
else if( strcmp( pcScenario, "allocation" ) == 0 )
{
iPassed = ( iAllocations == 1 ) && ( iStores == 0 );
}
else if( strcmp( pcScenario, "setspecific" ) == 0 )
{
iPassed = ( iAllocations == 1 ) && ( iStores == 1 ) && ( iFrees == 1 );
}
_Exit( iPassed ? EXIT_SUCCESS : EXIT_FAILURE );
}
static void * prvMarkedThread( void * pvUnused )
{
( void ) pvUnused;
if( prvIsFreeRTOSThread() != pdFALSE )
{
return ( void * ) 1;
}
prvMarkAsFreeRTOSThread();
return ( void * ) ( intptr_t ) ( prvIsFreeRTOSThread() != pdTRUE );
}
int main( int argc,
char ** argv )
{
pthread_t xThread;
void * pvResult;
if( argc != 2 )
{
return EXIT_FAILURE;
}
pcScenario = argv[ 1 ];
if( strcmp( pcScenario, "success" ) == 0 )
{
if( pthread_create( &xThread, NULL, prvMarkedThread, NULL ) != 0 )
{
return EXIT_FAILURE;
}
if( pthread_join( xThread, &pvResult ) != 0 )
{
return EXIT_FAILURE;
}
return ( ( pvResult == NULL ) && ( iAllocations == 1 ) && ( iFrees == 1 ) &&
( prvIsFreeRTOSThread() == pdFALSE ) ) ? EXIT_SUCCESS : EXIT_FAILURE;
}
if( strcmp( pcScenario, "key_query" ) == 0 )
{
( void ) prvIsFreeRTOSThread();
}
else
{
prvMarkAsFreeRTOSThread();
}
fprintf( stderr, "Continued after injected %s failure\n", pcScenario );
return EXIT_FAILURE;
}