Change the Linux Port to use condition variables instead of Signals (#156)
* Posix port with pthread cond instead of signals
* Comment: replace signal with pthread_cond
Co-authored-by: Alfred Gedeon <gedeonag@amazon.com>
diff --git a/portable/ThirdParty/GCC/Posix/port.c b/portable/ThirdParty/GCC/Posix/port.c
index 7816608..b7923f4 100644
--- a/portable/ThirdParty/GCC/Posix/port.c
+++ b/portable/ThirdParty/GCC/Posix/port.c
@@ -33,8 +33,8 @@
* running are blocked in sigwait().
*
* Task switch is done by resuming the thread for the next task by
- * sending it the resume signal (SIGUSR1) and then suspending the
- * current thread.
+ * signaling the condition variable and then waiting on a condition variable
+ * with the current thread.
*
* The timer interrupt uses SIGALRM and care is taken to ensure that
* the signal handler runs only on the thread for the current task.
@@ -44,9 +44,6 @@
* deadlocks as the FreeRTOS kernel can switch tasks while they're
* holding a pthread mutex.
*
- * Replacement malloc(), free(), calloc(), and realloc() are provided
- * for glibc (see below for more information).
- *
* stdio (printf() and friends) should be called from a single task
* only or serialized with a FreeRTOS primitive such as a binary
* semaphore or mutex.
@@ -65,6 +62,7 @@
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
+#include "utils/wait_for_event.h"
/*-----------------------------------------------------------*/
#define SIG_RESUME SIGUSR1
@@ -75,6 +73,7 @@
pdTASK_CODE pxCode;
void *pvParams;
BaseType_t xDying;
+ struct event *ev;
} Thread_t;
/*
@@ -104,82 +103,14 @@
static void prvSetupSignalsAndSchedulerPolicy( void );
static void prvSetupTimerInterrupt( void );
static void *prvWaitForStart( void * pvParams );
-static void prvSwitchThread( Thread_t *xThreadToResume, Thread_t *xThreadToSuspend );
-static void prvSuspendSelf( void );
-static void prvResumeThread( pthread_t xThreadId );
+static void prvSwitchThread( Thread_t * xThreadToResume,
+ Thread_t *xThreadToSuspend );
+static void prvSuspendSelf( Thread_t * thread);
+static void prvResumeThread( Thread_t * xThreadId );
static void vPortSystemTickHandler( int sig );
static void vPortStartFirstTask( void );
/*-----------------------------------------------------------*/
-/*
- * The standard glibc malloc(), free() etc. take an internal lock so
- * it is not safe to switch tasks while calling them.
- *
- * Requiring the application use the safe xPortMalloc() and
- * vPortFree() is not sufficient as malloc() is used internally by
- * glibc (e.g., by strdup() and the pthread library.)
- *
- * To further complicate things malloc() and free() may be called
- * outside of task context during pthread destruction so using
- * vTaskSuspend() and xTaskResumeAll() cannot be used.
- * vPortEnterCritical() and vPortExitCritical() cannot be used either
- * as they use global state for the critical section nesting (this
- * cannot be fixed by using TLS as pthread destruction needs to free
- * the TLS).
- *
- * Explicitly save/disable and restore the signal mask to block the
- * timer (SIGALRM) and other signals.
- */
-
-extern void *__libc_malloc(size_t);
-extern void __libc_free(void *);
-extern void *__libc_calloc(size_t, size_t);
-extern void *__libc_realloc(void *ptr, size_t);
-
-void *malloc(size_t size)
-{
-sigset_t xSavedSignals;
-void *ptr;
-
- pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
- ptr = __libc_malloc( size );
- pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
-
- return ptr;
-}
-
-void free(void *ptr)
-{
-sigset_t xSavedSignals;
-
- pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
- __libc_free( ptr );
- pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
-}
-
-void *calloc(size_t nmemb, size_t size)
-{
-sigset_t xSavedSignals;
-void *ptr;
-
- pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
- ptr = __libc_calloc( nmemb, size );
- pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
-
- return ptr;
-}
-
-void *realloc(void *ptr, size_t size)
-{
-sigset_t xSavedSignals;
-
- pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
- ptr = __libc_realloc( ptr, size );
- pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
-
- return ptr;
-}
-
static void prvFatalError( const char *pcCall, int iErrno )
{
fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
@@ -214,6 +145,8 @@
pthread_attr_init( &xThreadAttributes );
pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
+ thread->ev = event_create();
+
vPortEnterCritical();
iRet = pthread_create( &thread->pthread, &xThreadAttributes,
@@ -234,7 +167,7 @@
Thread_t *pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
/* Start the first task. */
- prvResumeThread( pxFirstThread->pthread );
+ prvResumeThread( pxFirstThread );
}
/*-----------------------------------------------------------*/
@@ -248,8 +181,8 @@
hMainThread = pthread_self();
- /* Start the timer that generates the tick ISR. Interrupts are disabled
- here already. */
+ /* Start the timer that generates the tick ISR(SIGALRM).
+ Interrupts are disabled here already. */
prvSetupTimerInterrupt();
/* Start the first task. */
@@ -275,6 +208,7 @@
{
struct itimerval itimer;
struct sigaction sigtick;
+Thread_t *xCurrentThread;
/* Stop the timer and ignore any pending SIGALRMs that would end
* up running on the main thread when it is resumed. */
@@ -282,19 +216,20 @@
itimer.it_value.tv_usec = 0;
itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = 0;
+ itimer.it_interval.tv_usec = 0;
(void)setitimer( ITIMER_REAL, &itimer, NULL );
sigtick.sa_flags = 0;
sigtick.sa_handler = SIG_IGN;
- sigemptyset( &sigtick.sa_mask );
+ sigemptyset( &sigtick.sa_mask );
sigaction( SIGALRM, &sigtick, NULL );
/* Signal the scheduler to exit its loop. */
xSchedulerEnd = pdTRUE;
(void)pthread_kill( hMainThread, SIG_RESUME );
- prvSuspendSelf();
+ xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
+ prvSuspendSelf(xCurrentThread);
}
/*-----------------------------------------------------------*/
@@ -425,7 +360,9 @@
uxCriticalNesting++; /* Signals are blocked in this signal handler. */
+#if ( configUSE_PREEMPTION == 1 )
pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
+#endif
/* Tick Increment, accounting for any lost signals or drift in
* the timer. */
@@ -461,8 +398,7 @@
Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
/*
- * The thread has already been suspended so it can be safely
- * cancelled.
+ * The thread has already been suspended so it can be safely cancelled.
*/
pthread_cancel( pxThreadToCancel->pthread );
pthread_join( pxThreadToCancel->pthread, NULL );
@@ -473,7 +409,7 @@
{
Thread_t *pxThread = pvParams;
- prvSuspendSelf();
+ prvSuspendSelf(pxThread);
/* Resumed for the first time, unblocks all signals. */
uxCriticalNesting = 0;
@@ -502,24 +438,25 @@
*/
uxSavedCriticalNesting = uxCriticalNesting;
- prvResumeThread( pxThreadToResume->pthread );
+ prvResumeThread( pxThreadToResume );
if ( pxThreadToSuspend->xDying )
{
+ event_delete(pxThreadToSuspend->ev);
pthread_exit( NULL );
}
- prvSuspendSelf();
+ prvSuspendSelf( pxThreadToSuspend );
uxCriticalNesting = uxSavedCriticalNesting;
}
}
/*-----------------------------------------------------------*/
-static void prvSuspendSelf( void )
+static void prvSuspendSelf( Thread_t *thread )
{
int iSig;
/*
- * Suspend this thread by waiting for a SIG_RESUME signal.
+ * Suspend this thread by waiting for a pthread_cond_signal event.
*
* A suspended thread must not handle signals (interrupts) so
* all signals must be blocked by calling this from:
@@ -530,17 +467,17 @@
* - From a signal handler that has all signals masked.
*
* - A thread with all signals blocked with pthread_sigmask().
- */
- sigwait( &xResumeSignals, &iSig );
+ */
+ event_wait(thread->ev);
}
/*-----------------------------------------------------------*/
-static void prvResumeThread( pthread_t xThreadId )
+static void prvResumeThread( Thread_t *xThreadId )
{
- if ( pthread_self() != xThreadId )
+ if ( pthread_self() != xThreadId->pthread )
{
- pthread_kill( xThreadId, SIG_RESUME );
+ event_signal(xThreadId->ev);
}
}
/*-----------------------------------------------------------*/
diff --git a/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c b/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
new file mode 100644
index 0000000..896604a
--- /dev/null
+++ b/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c
@@ -0,0 +1,76 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "wait_for_event.h"
+
+struct event
+{
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ bool event_triggered;
+};
+
+struct event * event_create()
+{
+ struct event * ev = malloc( sizeof( struct event ) );
+
+ ev->event_triggered = false;
+ pthread_mutex_init( &ev->mutex, NULL );
+ pthread_cond_init( &ev->cond, NULL );
+ return ev;
+}
+
+void event_delete( struct event * ev )
+{
+ pthread_mutex_destroy( &ev->mutex );
+ pthread_cond_destroy( &ev->cond );
+ free( ev );
+}
+
+bool event_wait( struct event * ev )
+{
+ pthread_mutex_lock( &ev->mutex );
+
+ while( ev->event_triggered == false )
+ {
+ pthread_cond_wait( &ev->cond, &ev->mutex );
+ }
+
+ ev->event_triggered = false;
+ pthread_mutex_unlock( &ev->mutex );
+ return true;
+}
+bool event_wait_timed( struct event * ev,
+ time_t ms )
+{
+ struct timespec ts;
+ int ret = 0;
+
+ clock_gettime( CLOCK_REALTIME, &ts );
+ //ts.tv_sec += ms;
+ ts.tv_nsec += (ms * 1000000);
+ pthread_mutex_lock( &ev->mutex );
+
+ while( (ev->event_triggered == false) && (ret == 0) )
+ {
+ ret = pthread_cond_timedwait( &ev->cond, &ev->mutex, &ts );
+
+ if( ( ret == -1 ) && ( errno == ETIMEDOUT ) )
+ {
+ return false;
+ }
+ }
+
+ ev->event_triggered = false;
+ pthread_mutex_unlock( &ev->mutex );
+ return true;
+}
+
+void event_signal( struct event * ev )
+{
+ pthread_mutex_lock( &ev->mutex );
+ ev->event_triggered = true;
+ pthread_cond_signal( &ev->cond );
+ pthread_mutex_unlock( &ev->mutex );
+}
diff --git a/portable/ThirdParty/GCC/Posix/utils/wait_for_event.h b/portable/ThirdParty/GCC/Posix/utils/wait_for_event.h
new file mode 100644
index 0000000..2693a0d
--- /dev/null
+++ b/portable/ThirdParty/GCC/Posix/utils/wait_for_event.h
@@ -0,0 +1,18 @@
+#ifndef _WAIT_FOR_EVENT_H_
+#define _WAIT_FOR_EVENT_H_
+
+#include <stdbool.h>
+#include <time.h>
+
+struct event;
+
+struct event * event_create();
+void event_delete( struct event * );
+bool event_wait( struct event * ev );
+bool event_wait_timed( struct event * ev,
+ time_t ms );
+void event_signal( struct event * ev );
+
+
+
+#endif /* ifndef _WAIT_FOR_EVENT_H_ */