Mark mutex as robust to prevent deadlocks (#1233)
Mark mutex as robust to prevent deadlocks
Prevent application hangs that occur when a thread dies while holding a
mutex, particularly during vTaskEndScheduler or exit calls. This is
achieved by setting the PTHREAD_MUTEX_ROBUST attribute on the mutex.
Fixes:
- GitHub issue: FreeRTOS/FreeRTOS-Kernel#1217
- Forum thread: freertos.org/t/22287
Signed-off-by: Gaurav Aggarwal <aggarg@amazon.com>
diff --git a/portable/ThirdParty/GCC/Posix/port.c b/portable/ThirdParty/GCC/Posix/port.c
index c4eacb2..4f7d8b6 100644
--- a/portable/ThirdParty/GCC/Posix/port.c
+++ b/portable/ThirdParty/GCC/Posix/port.c
@@ -119,14 +119,20 @@
static void vPortSystemTickHandler( int sig );
static void vPortStartFirstTask( void );
static void prvPortYieldFromISR( void );
+static void prvThreadKeyDestructor( void * pvData );
+static void prvInitThreadKey( void );
+static void prvMarkAsFreeRTOSThread( void );
+static BaseType_t prvIsFreeRTOSThread( void );
+static void prvDestroyThreadKey( void );
/*-----------------------------------------------------------*/
-void prvThreadKeyDestructor( void * data )
+static void prvThreadKeyDestructor( void * pvData )
{
- free( data );
+ free( pvData );
}
+/*-----------------------------------------------------------*/
-static void prvInitThreadKey()
+static void prvInitThreadKey( void )
{
pthread_mutex_lock( &xThreadMutex );
@@ -137,24 +143,39 @@
pthread_mutex_unlock( &xThreadMutex );
}
+/*-----------------------------------------------------------*/
-static void prvMarkAsFreeRTOSThread( pthread_t thread )
+static void prvMarkAsFreeRTOSThread( void )
{
+ uint8_t * pucThreadData = NULL;
+
prvInitThreadKey();
- uint8_t * thread_data = malloc( 1 );
- configASSERT( thread_data != NULL );
- *thread_data = 1;
- pthread_setspecific( xThreadKey, thread_data );
-}
-static BaseType_t prvIsFreeRTOSThread( pthread_t thread )
+ pucThreadData = malloc( 1 );
+ configASSERT( pucThreadData != NULL );
+
+ *pucThreadData = 1;
+
+ pthread_setspecific( xThreadKey, pucThreadData );
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvIsFreeRTOSThread( void )
{
- uint8_t * thread_data = ( uint8_t * ) pthread_getspecific( xThreadKey );
+ uint8_t * pucThreadData = NULL;
+ BaseType_t xRet = pdFALSE;
- return thread_data != NULL && *thread_data == 1;
+ pucThreadData = ( uint8_t * ) pthread_getspecific( xThreadKey );
+ if( ( pucThreadData != NULL ) && ( *pucThreadData == 1 ) )
+ {
+ xRet = pdTRUE;
+ }
+
+ return xRet;
}
+/*-----------------------------------------------------------*/
-static void prvDestroyThreadKey()
+static void prvDestroyThreadKey( void )
{
pthread_key_delete( xThreadKey );
}
@@ -309,7 +330,7 @@
( void ) pthread_kill( hMainThread, SIG_RESUME );
/* Waiting to be deleted here. */
- if( prvIsFreeRTOSThread( pthread_self() ) == pdTRUE )
+ if( prvIsFreeRTOSThread() == pdTRUE )
{
pxCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
event_wait( pxCurrentThread->ev );
@@ -369,7 +390,7 @@
void vPortDisableInterrupts( void )
{
- if( prvIsFreeRTOSThread( pthread_self() ) == pdTRUE )
+ if( prvIsFreeRTOSThread() == pdTRUE )
{
pthread_sigmask(SIG_BLOCK, &xAllSignals, NULL);
}
@@ -378,9 +399,9 @@
void vPortEnableInterrupts( void )
{
- if( prvIsFreeRTOSThread( pthread_self() ) == pdTRUE )
+ if( prvIsFreeRTOSThread() == pdTRUE )
{
- pthread_sigmask(SIG_UNBLOCK, &xAllSignals, NULL);
+ pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
}
}
/*-----------------------------------------------------------*/
@@ -417,9 +438,9 @@
{
( void ) arg;
- prvMarkAsFreeRTOSThread( pthread_self() );
+ prvMarkAsFreeRTOSThread();
- prvPortSetCurrentThreadName("Scheduler timer");
+ prvPortSetCurrentThreadName( "Scheduler timer" );
while( xTimerTickThreadShouldRun )
{
@@ -451,7 +472,7 @@
static void vPortSystemTickHandler( int sig )
{
- if( prvIsFreeRTOSThread( pthread_self() ) == pdTRUE )
+ if( prvIsFreeRTOSThread() == pdTRUE )
{
Thread_t * pxThreadToSuspend;
Thread_t * pxThreadToResume;
@@ -473,7 +494,9 @@
}
uxCriticalNesting--;
- } else {
+ }
+ else
+ {
fprintf( stderr, "vPortSystemTickHandler called from non-FreeRTOS thread\n" );
}
}
@@ -508,7 +531,7 @@
{
Thread_t * pxThread = pvParams;
- prvMarkAsFreeRTOSThread( pthread_self() );
+ prvMarkAsFreeRTOSThread();
prvSuspendSelf( pxThread );
diff --git a/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c b/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
index bf744e2..55fd7bb 100644
--- a/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
+++ b/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
@@ -35,9 +35,11 @@
struct event
{
pthread_mutex_t mutex;
+ pthread_mutexattr_t mutexattr;
pthread_cond_t cond;
bool event_triggered;
};
+/*-----------------------------------------------------------*/
struct event * event_create( void )
{
@@ -46,23 +48,36 @@
if( ev != NULL )
{
ev->event_triggered = false;
- pthread_mutex_init( &ev->mutex, NULL );
+ pthread_mutexattr_init( &ev->mutexattr );
+ #ifndef __APPLE__
+ pthread_mutexattr_setrobust( &ev->mutexattr, PTHREAD_MUTEX_ROBUST );
+ #endif
+ pthread_mutex_init( &ev->mutex, &ev->mutexattr );
pthread_cond_init( &ev->cond, NULL );
}
return ev;
}
+/*-----------------------------------------------------------*/
void event_delete( struct event * ev )
{
pthread_mutex_destroy( &ev->mutex );
+ pthread_mutexattr_destroy( &ev->mutexattr );
pthread_cond_destroy( &ev->cond );
free( ev );
}
+/*-----------------------------------------------------------*/
bool event_wait( struct event * ev )
{
- pthread_mutex_lock( &ev->mutex );
+ if( pthread_mutex_lock( &ev->mutex ) == EOWNERDEAD )
+ {
+ #ifndef __APPLE__
+ /* If the thread owning the mutex died, make the mutex consistent. */
+ pthread_mutex_consistent( &ev->mutex );
+ #endif
+ }
while( ev->event_triggered == false )
{
@@ -73,6 +88,8 @@
pthread_mutex_unlock( &ev->mutex );
return true;
}
+/*-----------------------------------------------------------*/
+
bool event_wait_timed( struct event * ev,
time_t ms )
{
@@ -82,7 +99,13 @@
clock_gettime( CLOCK_REALTIME, &ts );
ts.tv_sec += ms / 1000;
ts.tv_nsec += ( ( ms % 1000 ) * 1000000 );
- pthread_mutex_lock( &ev->mutex );
+ if( pthread_mutex_lock( &ev->mutex ) == EOWNERDEAD )
+ {
+ #ifndef __APPLE__
+ /* If the thread owning the mutex died, make the mutex consistent. */
+ pthread_mutex_consistent( &ev->mutex );
+ #endif
+ }
while( ( ev->event_triggered == false ) && ( ret == 0 ) )
{
@@ -98,11 +121,19 @@
pthread_mutex_unlock( &ev->mutex );
return true;
}
+/*-----------------------------------------------------------*/
void event_signal( struct event * ev )
{
- pthread_mutex_lock( &ev->mutex );
+ if( pthread_mutex_lock( &ev->mutex ) == EOWNERDEAD )
+ {
+ #ifndef __APPLE__
+ /* If the thread owning the mutex died, make the mutex consistent. */
+ pthread_mutex_consistent( &ev->mutex );
+ #endif
+ }
ev->event_triggered = true;
pthread_cond_signal( &ev->cond );
pthread_mutex_unlock( &ev->mutex );
}
+/*-----------------------------------------------------------*/