| /* |
| Copyright (C) 2011 Corvus Corax from OpenPilot.org |
| based on linux port from William Davy |
| |
| This file is part of the FreeRTOS.org distribution. |
| |
| FreeRTOS.org is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License (version 2) as published |
| by the Free Software Foundation and modified by the FreeRTOS exception. |
| |
| FreeRTOS.org is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| more details. |
| |
| You should have received a copy of the GNU General Public License along |
| with FreeRTOS.org; if not, write to the Free Software Foundation, Inc., 59 |
| Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
| |
| A special exception to the GPL is included to allow you to distribute a |
| combined work that includes FreeRTOS.org without being obliged to provide |
| the source code for any proprietary components. See the licensing section |
| of http://www.FreeRTOS.org for full details. |
| |
| |
| *************************************************************************** |
| * * |
| * Get the FreeRTOS eBook! See http://www.FreeRTOS.org/Documentation * |
| * * |
| * This is a concise, step by step, 'hands on' guide that describes both * |
| * general multitasking concepts and FreeRTOS specifics. It presents and * |
| * explains numerous examples that are written using the FreeRTOS API. * |
| * Full source code for all the examples is provided in an accompanying * |
| * .zip file. * |
| * * |
| *************************************************************************** |
| |
| 1 tab == 4 spaces! |
| |
| Please ensure to read the configuration and relevant port sections of the |
| online documentation. |
| |
| http://www.FreeRTOS.org - Documentation, latest information, license and |
| contact details. |
| |
| http://www.SafeRTOS.com - A version that is certified for use in safety |
| critical systems. |
| |
| http://www.OpenRTOS.com - Commercial support, development, porting, |
| licensing and training services. |
| */ |
| |
| /*----------------------------------------------------------- |
| * Implementation of functions defined in portable.h for the Posix port. |
| *----------------------------------------------------------*/ |
| |
| |
| /** Description of scheduler: |
| |
| This scheduler is based on posix signals to halt or preempt tasks, and on |
| pthread conditions to resume them. |
| |
| Each FreeRTOS thread is created as a posix thread, with a signal handler to |
| SIGUSR1 (SIG_SUSPEND) signals. |
| |
| Suspension of a thread is done by setting the threads state to |
| "YIELDING/PREEMPTING", then signaling the thread until the signal handler |
| changes that state to "SLEEPING", thus acknowledging the suspend. |
| |
| The thread will then wait within the signal handler for a thread specific |
| condition to be set, which allows it to resume operation, setting its state to |
| "RUNNING" |
| |
| The running thread also always holds a mutex (xRunningThreadMutex) which is |
| given up only when the thread suspends. |
| |
| On thread creation the new thread will acquire this mutex, then yield. |
| |
| Both preemption and yielding is done using the same mechanism, sending a |
| SIG_SUSPEND to the preempted thread respectively to itself, however different |
| synchronization safeguards apply depending if a thread suspends itself or is |
| suspended remotely |
| |
| Preemption is done by the main scheduler thread which attempts to run a tick |
| handler at accurate intervals using nanosleep and gettimeofday, which allows |
| more accurate high frequency ticks than a timer signal handler. |
| |
| All public functions in this port are protected by a safeguard mutex which |
| assures priority access on all data objects |
| |
| This approach is tested and works both on Linux and BSD style Unix (MAC OS X) |
| |
| */ |
| |
| #include <pthread.h> |
| #include <sched.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <sys/time.h> |
| #include <time.h> |
| #include <sys/times.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <limits.h> |
| |
| /* Scheduler includes. */ |
| #include "FreeRTOS.h" |
| #include "task.h" |
| /*-----------------------------------------------------------*/ |
| |
| #define MAX_NUMBER_OF_TASKS ( _POSIX_THREAD_THREADS_MAX ) |
| /*-----------------------------------------------------------*/ |
| |
| #define PORT_PRINT(...) fprintf(stderr,__VA_ARGS__) |
| #define PORT_ASSERT(assertion) if ( !(assertion) ) { PORT_PRINT("Assertion failed in %s:%i " #assertion "\n",__FILE__,__LINE__); int volatile assfail=0; assfail=assfail/assfail; } |
| |
| #ifdef DEBUG_SCHEDULER |
| #define PORT_LOCK(mutex) printf("Locking %llx by %llx at %d\n", (unsigned long long)&mutex, (unsigned long long)pthread_self(), __LINE__);PORT_ASSERT( 0 == pthread_mutex_lock(&(mutex)) ); printf("Locked %llx by %llx\n", (unsigned long long)&mutex, (unsigned long long)pthread_self()); |
| #define PORT_TRYLOCK(mutex) printf("Try Locking %llx by %llx\n", (unsigned long long)&mutex, (unsigned long long)pthread_self());pthread_mutex_trylock(&(mutex)); printf("Locked %llx by %llx\n", (unsigned long long)&mutex, (unsigned long long)pthread_self()); |
| #define PORT_UNLOCK(mutex) printf("Unlocking %llx by %llx at %d\n", (unsigned long long)&mutex, (unsigned long long)pthread_self(), __LINE__); PORT_ASSERT( 0 == pthread_mutex_unlock(&(mutex)) ); printf("Unlocked %llx by %llx\n", (unsigned long long)&mutex, (unsigned long long)pthread_self()); |
| #else |
| #define PORT_LOCK(mutex) PORT_ASSERT( 0 == pthread_mutex_lock(&(mutex)) ); |
| #define PORT_TRYLOCK(mutex) pthread_mutex_trylock(&(mutex)); |
| #define PORT_UNLOCK(mutex) PORT_ASSERT( 0 == pthread_mutex_unlock(&(mutex)) ); |
| #endif |
| |
| /* Parameters to pass to the newly created pthread. */ |
| typedef struct XPARAMS |
| { |
| pdTASK_CODE pxCode; |
| void *pvParams; |
| } xParams; |
| |
| /* Each task maintains its own interrupt status in the critical nesting variable. */ |
| typedef struct THREAD_SUSPENSIONS |
| { |
| pthread_t hThread; |
| xTaskHandle hTask; |
| unsigned portBASE_TYPE uxCriticalNesting; |
| pthread_mutex_t threadSleepMutex; |
| pthread_cond_t threadSleepCond; |
| volatile enum {THREAD_SLEEPING,THREAD_RUNNING,THREAD_STARTING,THREAD_YIELDING,THREAD_PREEMPTING,THREAD_WAKING} threadStatus; |
| } xThreadState; |
| /*-----------------------------------------------------------*/ |
| |
| /* Needed to keep track of critical section depth before scheduler got started */ |
| static xThreadState xDummyThread = { .uxCriticalNesting=0, .threadStatus=THREAD_RUNNING }; |
| |
| static xThreadState *pxThreads = NULL; |
| static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT; |
| static pthread_attr_t xThreadAttributes; |
| static pthread_mutex_t xRunningThreadMutex = PTHREAD_MUTEX_INITIALIZER; |
| static pthread_mutex_t xYieldingThreadMutex = PTHREAD_MUTEX_INITIALIZER; |
| static pthread_mutex_t xResumingThreadMutex = PTHREAD_MUTEX_INITIALIZER; |
| static pthread_mutex_t xGuardMutex = PTHREAD_MUTEX_INITIALIZER; |
| /*-----------------------------------------------------------*/ |
| |
| static volatile portBASE_TYPE xSchedulerEnd = pdFALSE; |
| static volatile portBASE_TYPE xSchedulerStarted = pdFALSE; |
| static volatile portBASE_TYPE xInterruptsEnabled = pdFALSE; |
| static volatile portBASE_TYPE xSchedulerNesting = 0; |
| static volatile portBASE_TYPE xPendYield = pdFALSE; |
| static volatile portLONG lIndexOfLastAddedTask = 0; |
| /*-----------------------------------------------------------*/ |
| |
| /* |
| * Setup the timer to generate the tick interrupts. |
| */ |
| static void *prvWaitForStart( void * pvParams ); |
| static void prvSuspendSignalHandler(int sig); |
| static void prvSetupSignalsAndSchedulerPolicy( void ); |
| static void prvResumeThread( xThreadState* xThreadId ); |
| static xThreadState* prvGetThreadHandle( xTaskHandle hTask ); |
| static xThreadState* prvGetThreadHandleByThread( pthread_t hThread ); |
| static portLONG prvGetFreeThreadState( void ); |
| static void prvDeleteThread( void *xThreadId ); |
| static void prvPortYield( void ); |
| /*-----------------------------------------------------------*/ |
| |
| /* |
| * Exception handlers. |
| */ |
| void vPortYield( void ); |
| void vPortSystemTickHandler( void ); |
| |
| /* |
| * Start first task is a separate function so it can be tested in isolation. |
| */ |
| void vPortStartFirstTask( void ); |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * inline macro functions |
| * (easierto debug than macros) |
| */ |
| static inline void PORT_ENTER( void ) { |
| |
| xThreadState *selfThreadState = prvGetThreadHandleByThread(pthread_self()); |
| while( selfThreadState && selfThreadState->threadStatus!=THREAD_RUNNING) sched_yield(); |
| |
| // Block SUSPEND from interrupting |
| sigset_t xSignalToBlock; |
| sigemptyset(&xSignalToBlock); |
| sigaddset(&xSignalToBlock,SIG_SUSPEND); |
| (void)pthread_sigmask(SIG_BLOCK, &xSignalToBlock, NULL); |
| |
| while (1) |
| { |
| #ifdef DEBUG_SCHEDULER |
| printf("Locking xGuardMutex by %llx\n", (unsigned long long)pthread_self()); |
| #endif |
| if (pthread_mutex_trylock(&(xGuardMutex)) == 0) |
| { |
| #ifdef DEBUG_SCHEDULER |
| printf("Locked xGuardMutex by %llx\n", (unsigned long long)pthread_self()); |
| #endif |
| break; |
| } |
| |
| // Allow it to interrupt again |
| (void)pthread_sigmask(SIG_UNBLOCK, &xSignalToBlock, NULL); |
| |
| // Yield |
| sched_yield(); |
| |
| // Block SUSPEND from interrupting |
| (void)pthread_sigmask(SIG_BLOCK, &xSignalToBlock, NULL); |
| } |
| |
| // Allow it to interrupt again |
| (void)pthread_sigmask(SIG_UNBLOCK, &xSignalToBlock, NULL); |
| |
| PORT_ASSERT( xSchedulerStarted?( (selfThreadState==NULL) || (selfThreadState==prvGetThreadHandle(xTaskGetCurrentTaskHandle())) ):pdTRUE ) |
| } |
| |
| static inline void PORT_LEAVE( void ) { |
| xThreadState *selfThreadState = prvGetThreadHandleByThread(pthread_self()); |
| PORT_ASSERT( xSchedulerStarted?( (selfThreadState==NULL) || (selfThreadState==prvGetThreadHandle(xTaskGetCurrentTaskHandle())) ):pdTRUE ); |
| PORT_ASSERT( selfThreadState == NULL || selfThreadState->threadStatus==THREAD_RUNNING ); |
| PORT_UNLOCK( xGuardMutex ); |
| } |
| |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * Creates a new thread. |
| */ |
| portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) |
| { |
| /* Should actually keep this struct on the stack. */ |
| xParams *pxThisThreadParams = pvPortMalloc( sizeof( xParams ) ); |
| |
| /* Initialize scheduler during first call */ |
| (void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy ); |
| |
| /** |
| * port enter needs to be delayed since pvPortMalloc() and |
| * SetupSignalsAndSchedulerPolicy() both call critical section code |
| */ |
| PORT_ENTER(); |
| |
| /* No need to join the threads. */ |
| pthread_attr_init( &xThreadAttributes ); |
| pthread_attr_setdetachstate( &xThreadAttributes, PTHREAD_CREATE_DETACHED ); |
| |
| /* Add the task parameters. */ |
| pxThisThreadParams->pxCode = pxCode; |
| pxThisThreadParams->pvParams = pvParameters; |
| |
| lIndexOfLastAddedTask = prvGetFreeThreadState(); |
| |
| PORT_LOCK( xYieldingThreadMutex ); |
| |
| pxThreads[ lIndexOfLastAddedTask ].threadStatus = THREAD_STARTING; |
| pxThreads[ lIndexOfLastAddedTask ].uxCriticalNesting = 0; |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("Bleh Set threadStatus = %d for %llx\n", pxThreads[ lIndexOfLastAddedTask ].threadStatus, (unsigned long long)&pxThreads[ lIndexOfLastAddedTask ]); |
| #endif |
| |
| /* create the thead */ |
| PORT_ASSERT( 0 == pthread_create( &( pxThreads[ lIndexOfLastAddedTask ].hThread ), &xThreadAttributes, prvWaitForStart, (void *)pxThisThreadParams ) ); |
| |
| /* Let the task run a bit and wait until it suspends. */ |
| while ( pxThreads[ lIndexOfLastAddedTask ].threadStatus == THREAD_STARTING ) sched_yield(); |
| |
| /* this ensures the sleeping thread reached deep sleep (and not more) */ |
| PORT_UNLOCK( xYieldingThreadMutex ); |
| |
| PORT_LEAVE(); |
| |
| return pxTopOfStack; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * Initially the schedulers main thread holds the running thread mutex. |
| * it needs to be given up, to allow the first running task to execute |
| */ |
| void vPortStartFirstTask( void ) |
| { |
| /* Mark scheduler as started */ |
| xSchedulerStarted = pdTRUE; |
| |
| /* Start the first task. */ |
| prvResumeThread( prvGetThreadHandle( xTaskGetCurrentTaskHandle() ) ); |
| |
| /* Give up running thread handle */ |
| PORT_UNLOCK(xRunningThreadMutex); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * After tasks have been set up the main thread goes into a sleeping loop, but |
| * allows to be interrupted by timer ticks. |
| */ |
| portBASE_TYPE xPortStartScheduler( void ) |
| { |
| sigset_t xSignalToBlock; |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("xGuardMutex = %llx\n", (unsigned long long)&xGuardMutex); |
| #endif |
| |
| /** |
| * note: NO PORT_ENTER ! - this is the supervisor thread which runs outside |
| * of the schedulers context |
| */ |
| |
| /* do not respond to SUSPEND signal (but all others) */ |
| sigemptyset( &xSignalToBlock ); |
| (void)pthread_sigmask( SIG_SETMASK, &xSignalToBlock, NULL ); |
| sigemptyset(&xSignalToBlock); |
| sigaddset(&xSignalToBlock,SIG_SUSPEND); |
| (void)pthread_sigmask(SIG_BLOCK, &xSignalToBlock, NULL); |
| |
| /* Start the first task. This gives up the RunningThreadMutex*/ |
| vPortStartFirstTask(); |
| |
| /** |
| * Main scheduling loop. Call the tick handler every |
| * portTICK_RATE_MICROSECONDS |
| */ |
| portTickType sleepTimeUS = portTICK_RATE_MICROSECONDS; |
| portTickType actualSleepTime; |
| struct timeval lastTime,currentTime; |
| gettimeofday( &lastTime, NULL ); |
| struct timespec wait; |
| |
| while ( pdTRUE != xSchedulerEnd ) |
| { |
| /* wait for the specified wait time */ |
| wait.tv_sec = sleepTimeUS / 1000000; |
| wait.tv_nsec = 1000 * ( sleepTimeUS % 1000000 ); |
| nanosleep( &wait, NULL ); |
| |
| /* check the time */ |
| gettimeofday( ¤tTime, NULL); |
| actualSleepTime = 1000000 * ( currentTime.tv_sec - lastTime.tv_sec ) + ( currentTime.tv_usec - lastTime.tv_usec ); |
| |
| /* only hit the tick if we slept at least half the period */ |
| if ( actualSleepTime >= sleepTimeUS/2 ) { |
| |
| vPortSystemTickHandler(); |
| |
| /* check the time again */ |
| gettimeofday( ¤tTime, NULL); |
| actualSleepTime = 1000000 * ( currentTime.tv_sec - lastTime.tv_sec ) + ( currentTime.tv_usec - lastTime.tv_usec ); |
| |
| /* sleep until the next tick is due */ |
| sleepTimeUS += portTICK_RATE_MICROSECONDS; |
| } |
| |
| /* reduce remaining sleep time by the slept time */ |
| sleepTimeUS -= actualSleepTime; |
| lastTime = currentTime; |
| |
| /* safety checks */ |
| if (sleepTimeUS <=0 || sleepTimeUS >= 3 * portTICK_RATE_MICROSECONDS) sleepTimeUS = portTICK_RATE_MICROSECONDS; |
| |
| } |
| |
| PORT_PRINT( "Cleaning Up, Exiting.\n" ); |
| /* Cleanup the mutexes */ |
| pthread_mutex_destroy( &xRunningThreadMutex ); |
| pthread_mutex_destroy( &xYieldingThreadMutex ); |
| pthread_mutex_destroy( &xGuardMutex ); |
| vPortFree( (void *)pxThreads ); |
| |
| /* Should not get here! */ |
| return 0; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * quickly clean up all running threads, without asking them first |
| */ |
| void vPortEndScheduler( void ) |
| { |
| portBASE_TYPE xNumberOfThreads; |
| for ( xNumberOfThreads = 0; xNumberOfThreads < MAX_NUMBER_OF_TASKS; xNumberOfThreads++ ) |
| { |
| if ( ( pthread_t )NULL != pxThreads[ xNumberOfThreads ].hThread ) |
| { |
| /* Kill all of the threads, they are in the detached state. */ |
| pthread_cancel( pxThreads[ xNumberOfThreads ].hThread ); |
| } |
| } |
| |
| /* Signal the scheduler to exit its loop. */ |
| xSchedulerEnd = pdTRUE; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * we must assume this one is called from outside the schedulers context |
| * (ISR's, signal handlers, or non-freertos threads) |
| * we cannot safely assume mutual exclusion |
| */ |
| void vPortYieldFromISR( void ) |
| { |
| xPendYield = pdTRUE; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * enter a critical section (public) |
| */ |
| void vPortEnterCritical( void ) |
| { |
| PORT_ENTER(); |
| xInterruptsEnabled = pdFALSE; |
| prvGetThreadHandleByThread(pthread_self())->uxCriticalNesting++; |
| PORT_LEAVE(); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * leave a critical section (public) |
| */ |
| void vPortExitCritical( void ) |
| { |
| PORT_ENTER(); |
| |
| /* Check for unmatched exits. */ |
| PORT_ASSERT( prvGetThreadHandleByThread(pthread_self())->uxCriticalNesting > 0 ); |
| if ( prvGetThreadHandleByThread(pthread_self())->uxCriticalNesting > 0 ) |
| { |
| prvGetThreadHandleByThread(pthread_self())->uxCriticalNesting--; |
| } |
| |
| /* If we have reached 0 then re-enable the interrupts. */ |
| if( prvGetThreadHandleByThread(pthread_self())->uxCriticalNesting == 0 ) |
| { |
| /* Have we missed ticks? This is the equivalent of pending an interrupt. */ |
| if ( pdTRUE == xPendYield ) |
| { |
| xPendYield = pdFALSE; |
| prvPortYield(); |
| } |
| |
| xInterruptsEnabled = pdTRUE; |
| |
| } |
| |
| PORT_LEAVE(); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * code to self-yield a task |
| * (without the mutex encapsulation) |
| * for internal use |
| */ |
| void prvPortYield( void ) |
| { |
| xThreadState *xTaskToSuspend, *xTaskToResume; |
| |
| /* timer handler should NOT get in our way (just in case) */ |
| xInterruptsEnabled = pdFALSE; |
| |
| /* suspend the current task */ |
| xTaskToSuspend = prvGetThreadHandleByThread( pthread_self() ); |
| |
| /** |
| * make sure not to suspend threads that are already trying to do so |
| */ |
| PORT_ASSERT( xTaskToSuspend->threadStatus == THREAD_RUNNING ); |
| |
| /** |
| * FreeRTOS switch context |
| */ |
| vTaskSwitchContext(); |
| |
| /** |
| * find out which task to resume |
| */ |
| xTaskToResume = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); |
| if ( xTaskToSuspend != xTaskToResume ) |
| { |
| /* Resume the other thread first */ |
| prvResumeThread( xTaskToResume ); |
| |
| /* prepare the current task for yielding */ |
| xTaskToSuspend->threadStatus = THREAD_YIELDING; |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("Suspending thread (yield) %llx\n", (unsigned long long)xTaskToSuspend->hThread); |
| #endif |
| /** |
| * Send signals until the signal handler acknowledges. How long that takes |
| * depends on the systems signal implementation. During a preemption we |
| * will see the actual THREAD_SLEEPING STATE - but when yielding we |
| * would only see a future THREAD_RUNNING after having woken up - both is |
| * OK |
| */ |
| while ( xTaskToSuspend->threadStatus == THREAD_YIELDING ) { |
| pthread_kill( xTaskToSuspend->hThread, SIG_SUSPEND ); |
| sched_yield(); |
| } |
| |
| /** |
| * mark: once we reach this point, the task has already slept and awaken anew |
| */ |
| |
| } else { |
| /** |
| * no context switch - keep running |
| */ |
| if (xTaskToResume->uxCriticalNesting==0) { |
| xInterruptsEnabled = pdTRUE; |
| } |
| } |
| |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * public yield function - secure |
| */ |
| void vPortYield( void ) |
| { |
| PORT_ENTER(); |
| |
| prvPortYield(); |
| |
| PORT_LEAVE(); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * public function to disable interrupts |
| */ |
| void vPortDisableInterrupts( void ) |
| { |
| PORT_ENTER(); |
| |
| xInterruptsEnabled = pdFALSE; |
| |
| PORT_LEAVE(); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * public function to enable interrupts |
| */ |
| void vPortEnableInterrupts( void ) |
| { |
| PORT_ENTER(); |
| |
| /** |
| * It is bad practice to enable interrupts explicitly while in a critical section |
| * most likely this is a bug - better prevent the userspace from being stupid |
| */ |
| xThreadState *selfThreadState = prvGetThreadHandleByThread(pthread_self()); |
| if (selfThreadState) |
| PORT_ASSERT( selfThreadState->uxCriticalNesting == 0 ); |
| |
| xInterruptsEnabled = pdTRUE; |
| |
| PORT_LEAVE(); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * set and clear interrupt masks are used by FreeRTOS to enter and leave critical sections |
| * with unknown nexting level - but we DO know the nesting level |
| */ |
| portBASE_TYPE xPortSetInterruptMask( void ) |
| { |
| portBASE_TYPE xReturn; |
| xThreadState *selfThreadState = prvGetThreadHandleByThread(pthread_self()); |
| |
| PORT_ENTER(); |
| |
| xReturn = xInterruptsEnabled; |
| |
| xInterruptsEnabled = pdFALSE; |
| |
| if (selfThreadState) |
| selfThreadState->uxCriticalNesting++; |
| |
| PORT_LEAVE(); |
| |
| return xReturn; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * sets the "interrupt mask back to a stored setting |
| */ |
| void vPortClearInterruptMask( portBASE_TYPE xMask ) |
| { |
| PORT_ENTER(); |
| |
| /** |
| * we better make sure the calling code behaves |
| * if it doesn't it might indicate something went seriously wrong |
| */ |
| xThreadState *selfThreadState = prvGetThreadHandleByThread(pthread_self()); |
| PORT_ASSERT( xMask == pdTRUE || xMask == pdFALSE ); |
| PORT_ASSERT( |
| ( selfThreadState == NULL ) |
| || |
| ( selfThreadState->uxCriticalNesting == 1 && xMask==pdTRUE ) |
| || |
| ( selfThreadState->uxCriticalNesting > 1 && xMask==pdFALSE ) |
| ); |
| |
| xInterruptsEnabled = xMask; |
| |
| if (selfThreadState) |
| { |
| if (prvGetThreadHandleByThread(pthread_self())->uxCriticalNesting>0) { |
| prvGetThreadHandleByThread(pthread_self())->uxCriticalNesting--; |
| } |
| } |
| |
| PORT_LEAVE(); |
| } |
| |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * the tick handler is just an ordinary function, called by the supervisor thread periodically |
| */ |
| void vPortSystemTickHandler() |
| { |
| /** |
| * the problem with the tick handler is, that it runs outside of the schedulers domain - worse, |
| * on a multi core machine there might be a task running *right now* |
| * - we need to stop it in order to do anything. However we need to make sure we are able to first |
| */ |
| PORT_LOCK( xGuardMutex ); |
| |
| /* thread MUST be running */ |
| if (prvGetThreadHandle(xTaskGetCurrentTaskHandle()) == NULL) |
| { |
| printf("NULL thread handle in systick!\n"); |
| return; |
| } |
| if ( prvGetThreadHandle(xTaskGetCurrentTaskHandle())->threadStatus!=THREAD_RUNNING ) { |
| xPendYield = pdTRUE; |
| PORT_UNLOCK( xGuardMutex ); |
| return; |
| } |
| |
| /* interrupts MUST be enabled */ |
| if ( xInterruptsEnabled != pdTRUE ) { |
| xPendYield = pdTRUE; |
| PORT_UNLOCK( xGuardMutex ); |
| return; |
| } |
| |
| /* this should always be true, but it can't harm to check */ |
| PORT_ASSERT( prvGetThreadHandle(xTaskGetCurrentTaskHandle())->uxCriticalNesting==0 ); |
| |
| /* acquire switching mutex for synchronization */ |
| PORT_LOCK(xYieldingThreadMutex); |
| |
| xThreadState *xTaskToSuspend = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("Suspending thread (tick) %llx\n", (unsigned long long)xTaskToSuspend->hThread); |
| #endif |
| |
| /** |
| * halt current task - this means NO task is running! |
| * Send signals until the signal handler acknowledges. how long that takes |
| * depends on the systems signal implementation. During a preemption we |
| * will see the actual THREAD_SLEEPING STATE when yielding we would only |
| * see a future THREAD_RUNNING after having woken up both is OK |
| * note: we do NOT give up switchingThreadMutex! |
| */ |
| xTaskToSuspend->threadStatus = THREAD_PREEMPTING; |
| while ( xTaskToSuspend->threadStatus != THREAD_SLEEPING ) { |
| pthread_kill( xTaskToSuspend->hThread, SIG_SUSPEND ); |
| sched_yield(); |
| } |
| |
| /** |
| * synchronize and acquire the running thread mutex |
| */ |
| PORT_UNLOCK( xYieldingThreadMutex ); |
| PORT_LOCK( xRunningThreadMutex ); |
| |
| /** |
| * now the tick handler runs INSTEAD of the currently active thread |
| * - even on a multicore system |
| * failure to do so can lead to unexpected results during |
| * vTaskIncrementTick()... |
| */ |
| |
| /** |
| * call tick handler |
| */ |
| vTaskIncrementTick(); |
| |
| |
| #if ( configUSE_PREEMPTION == 1 ) |
| /** |
| * while we are here we can as well switch the running thread |
| */ |
| vTaskSwitchContext(); |
| |
| xTaskToSuspend = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); |
| #endif |
| |
| /** |
| * wake up the task (again) |
| */ |
| prvResumeThread( xTaskToSuspend ); |
| |
| /** |
| * give control to the userspace task |
| */ |
| PORT_UNLOCK( xRunningThreadMutex ); |
| |
| /* finish up */ |
| PORT_UNLOCK( xGuardMutex ); |
| |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * thread kill implementation |
| */ |
| void vPortForciblyEndThread( void *pxTaskToDelete ) |
| { |
| xTaskHandle hTaskToDelete = ( xTaskHandle )pxTaskToDelete; |
| xThreadState* xTaskToDelete; |
| xThreadState* xTaskToResume; |
| |
| PORT_ENTER(); |
| |
| xTaskToDelete = prvGetThreadHandle( hTaskToDelete ); |
| xTaskToResume = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); |
| |
| PORT_ASSERT( xTaskToDelete ); |
| PORT_ASSERT( xTaskToResume ); |
| |
| if ( xTaskToResume == xTaskToDelete ) |
| { |
| /* This is a suicidal thread, need to select a different task to run. */ |
| vTaskSwitchContext(); |
| xTaskToResume = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); |
| } |
| |
| if ( pthread_self() != xTaskToDelete->hThread ) |
| { |
| /* Cancelling a thread that is not me. */ |
| |
| /* Send a signal to wake the task so that it definitely cancels. */ |
| pthread_testcancel(); |
| pthread_cancel( xTaskToDelete->hThread ); |
| |
| } |
| else |
| { |
| /* Resume the other thread. */ |
| prvResumeThread( xTaskToResume ); |
| /* Pthread Clean-up function will note the cancellation. */ |
| /* Release the execution. */ |
| |
| PORT_UNLOCK( xRunningThreadMutex ); |
| |
| //PORT_LEAVE(); |
| PORT_UNLOCK( xGuardMutex ); |
| /* Commit suicide */ |
| pthread_exit( (void *)1 ); |
| } |
| PORT_LEAVE(); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * any new thread first acquires the runningThreadMutex, but then suspends |
| * immediately, giving control back to the thread starting the new one |
| */ |
| void *prvWaitForStart( void * pvParams ) |
| { |
| xParams * pxParams = ( xParams * )pvParams; |
| pdTASK_CODE pvCode = pxParams->pxCode; |
| void * pParams = pxParams->pvParams; |
| sigset_t xSignalToBlock; |
| xThreadState * myself = prvGetThreadHandleByThread( pthread_self() ); |
| |
| pthread_cleanup_push( prvDeleteThread, (void *)pthread_self() ); |
| |
| /* do respond to signals */ |
| sigemptyset( &xSignalToBlock ); |
| (void)pthread_sigmask( SIG_SETMASK, &xSignalToBlock, NULL ); |
| |
| /** |
| * Suspend ourselves. It's important to do that first |
| * because until we come back from this we run outside the schedulers scope |
| * and can't call functions like vPortFree() safely |
| */ |
| while ( myself->threadStatus == THREAD_STARTING ) { |
| pthread_kill( myself->hThread, SIG_SUSPEND ); |
| sched_yield(); |
| } |
| |
| /** |
| * now we have returned from the dead - reborn as a real thread inside the |
| * schedulers scope. |
| */ |
| vPortFree( pvParams ); |
| |
| /* run the actual payload */ |
| pvCode( pParams ); |
| |
| pthread_cleanup_pop( 1 ); |
| return (void *)NULL; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * The suspend signal handler is called when a thread gets a SIGSUSPEND |
| * signal, which is supposed to send it to sleep |
| */ |
| void prvSuspendSignalHandler(int sig) |
| { |
| |
| // quell "unused parameter" warning |
| (void) sig; |
| |
| portBASE_TYPE hangover; |
| |
| /* make sure who we are */ |
| xThreadState* myself = prvGetThreadHandleByThread(pthread_self()); |
| PORT_ASSERT( myself ); |
| |
| /* make sure we are actually supposed to sleep */ |
| if (myself->threadStatus != THREAD_YIELDING && myself->threadStatus != THREAD_STARTING && myself->threadStatus != THREAD_PREEMPTING ) { |
| /* Spurious signal has arrived, we are not really supposed to halt. |
| * Not a real problem, we can safely ignore that. */ |
| return; |
| } |
| |
| /* we need that to wake up later (cond_wait needs a mutex locked) */ |
| PORT_LOCK(myself->threadSleepMutex); |
| |
| /* even waking up is a bit different depending on how we went to sleep */ |
| hangover = myself->threadStatus; |
| |
| myself->threadStatus = THREAD_SLEEPING; |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("Set threadStatus = %d for %llx\n", myself->threadStatus, (unsigned long long)myself); |
| #endif |
| |
| if ( hangover == THREAD_STARTING ) { |
| /** |
| * Synchronization with spawning thread through YieldingMutex |
| * This thread does NOT have the running thread mutex |
| * because it never officially ran before. |
| * It will get that mutex on wakeup though. |
| */ |
| PORT_LOCK(xYieldingThreadMutex); |
| PORT_UNLOCK(xYieldingThreadMutex); |
| } else if ( hangover == THREAD_YIELDING) { |
| /** |
| * The caller is the same thread as the signal handler. |
| * No synchronization possible or needed. |
| * But we need to unlock the mutexes it holds, so |
| * other threads can run. |
| */ |
| PORT_UNLOCK(xRunningThreadMutex ); |
| PORT_UNLOCK(xGuardMutex ); |
| } else if ( hangover == THREAD_PREEMPTING) { |
| /** |
| * The caller is the tick handler. |
| * Use YieldingMutex for synchronization |
| * Give up RunningThreadMutex, so the tick handler |
| * can take it and start another thread. |
| */ |
| PORT_LOCK(xYieldingThreadMutex); |
| PORT_UNLOCK(xRunningThreadMutex ); |
| PORT_UNLOCK(xYieldingThreadMutex); |
| } |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("Thread suspended %llx\n", (unsigned long long)myself->hThread); |
| #endif |
| |
| /* deep sleep until wake condition is met*/ |
| pthread_cond_wait( &myself->threadSleepCond, &myself->threadSleepMutex ); |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("Thread resumed %llx\n", (unsigned long long)myself->hThread); |
| #endif |
| |
| /* waking */ |
| myself->threadStatus = THREAD_WAKING; |
| |
| /* synchronize with waker - quick assertion if the right thread got the condition sent to*/ |
| PORT_LOCK(xResumingThreadMutex); |
| PORT_ASSERT(prvGetThreadHandle( xTaskGetCurrentTaskHandle())==myself); |
| PORT_UNLOCK(xResumingThreadMutex); |
| |
| /* we don't need that condition mutex anymore */ |
| PORT_UNLOCK(myself->threadSleepMutex); |
| |
| #if ( INCLUDE_pcTaskGetTaskName == 1 ) |
| // Set the name of the thread |
| xTaskHandle xTask = myself->hTask; |
| if (xTask) |
| { |
| signed char *taskName = pcTaskGetTaskName(xTask); |
| #if defined(__APPLE__) |
| pthread_setname_np((const char *)taskName); |
| #else |
| taskName = taskName; |
| #endif |
| } |
| #endif |
| |
| /* we ARE the running thread now (the one and only) */ |
| PORT_LOCK(xRunningThreadMutex); |
| |
| /** |
| * and we have important stuff to do, nobody should interfere with |
| * ( GuardMutex is usually set by PORT_ENTER() ) |
| * */ |
| PORT_LOCK( xGuardMutex ); |
| if ( myself->uxCriticalNesting == 0 ) |
| { |
| xInterruptsEnabled = pdTRUE; |
| } |
| else |
| { |
| xInterruptsEnabled = pdFALSE; |
| } |
| |
| myself->threadStatus = THREAD_RUNNING; |
| |
| /** |
| * if we jump back to user code, we are done with important stuff, |
| * but if we had yielded we are still in protected code after returning. |
| **/ |
| if (hangover!=THREAD_YIELDING) { |
| PORT_UNLOCK( xGuardMutex ); |
| } |
| |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * Signal the condition. |
| * Unlike pthread_kill this actually is supposed to be reliable, so we need no |
| * checks on the outcome. |
| */ |
| void prvResumeThread( xThreadState* xThreadId ) |
| { |
| PORT_ASSERT( xThreadId ); |
| PORT_LOCK( xResumingThreadMutex ); |
| |
| PORT_ASSERT(xThreadId->threadStatus == THREAD_SLEEPING); |
| |
| #ifdef DEBUG_SCHEDULER |
| printf("Resuming thread %llx\n", (unsigned long long)xThreadId->hThread); |
| #endif |
| |
| /** |
| * Unfortunately "is supposed to" does not hold on all Posix-ish systems |
| * but sending the cond_signal again doesn't hurt anyone. |
| */ |
| while ( xThreadId->threadStatus != THREAD_WAKING ) { |
| pthread_cond_signal(& xThreadId->threadSleepCond); |
| sched_yield(); |
| } |
| |
| PORT_UNLOCK( xResumingThreadMutex ); |
| |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * this is init code executed the first time a thread is created |
| */ |
| void prvSetupSignalsAndSchedulerPolicy( void ) |
| { |
| /* The following code would allow for configuring the scheduling of this task as a Real-time task. |
| * The process would then need to be run with higher privileges for it to take affect. |
| int iPolicy; |
| int iResult; |
| int iSchedulerPriority; |
| iResult = pthread_getschedparam( pthread_self(), &iPolicy, &iSchedulerPriority ); |
| iResult = pthread_attr_setschedpolicy( &xThreadAttributes, SCHED_FIFO ); |
| iPolicy = SCHED_FIFO; |
| iResult = pthread_setschedparam( pthread_self(), iPolicy, &iSchedulerPriority ); */ |
| |
| struct sigaction sigsuspendself; |
| portLONG lIndex; |
| |
| pxThreads = ( xThreadState *)pvPortMalloc( sizeof( xThreadState ) * MAX_NUMBER_OF_TASKS ); |
| |
| const pthread_cond_t cinit = PTHREAD_COND_INITIALIZER; |
| const pthread_mutex_t minit = PTHREAD_MUTEX_INITIALIZER; |
| for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) |
| { |
| pxThreads[ lIndex ].hThread = ( pthread_t )NULL; |
| pxThreads[ lIndex ].hTask = ( xTaskHandle )NULL; |
| pxThreads[ lIndex ].uxCriticalNesting = 0; |
| pxThreads[ lIndex ].threadSleepMutex = minit; |
| pxThreads[ lIndex ].threadSleepCond = cinit; |
| } |
| |
| sigsuspendself.sa_flags = 0; |
| sigsuspendself.sa_handler = prvSuspendSignalHandler; |
| sigfillset( &sigsuspendself.sa_mask ); |
| |
| if ( 0 != sigaction( SIG_SUSPEND, &sigsuspendself, NULL ) ) |
| { |
| PORT_PRINT( "Problem installing SIG_SUSPEND_SELF\n" ); |
| } |
| PORT_PRINT( "Running as PID: %d\n", getpid() ); |
| |
| /* When scheduler is set up main thread first claims the running thread mutex */ |
| PORT_LOCK( xRunningThreadMutex ); |
| |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * get a thread handle based on a task handle |
| */ |
| xThreadState* prvGetThreadHandle( xTaskHandle hTask ) |
| { |
| portLONG lIndex; |
| if (!pxThreads) |
| return NULL; |
| for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) |
| { |
| if ( pxThreads[ lIndex ].hTask == hTask ) |
| { |
| return &pxThreads[ lIndex ]; |
| break; |
| } |
| } |
| return NULL; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * get a thread handle based on a posix thread handle |
| */ |
| xThreadState* prvGetThreadHandleByThread( pthread_t hThread ) |
| { |
| portLONG lIndex; |
| /** |
| * if the scheduler is NOT yet started, we can give back a dummy thread handle |
| * to allow keeping track of interrupt nesting. |
| * However once the scheduler is started we return a NULL, |
| * so any misbehaving code can nicely segfault. |
| */ |
| if (!xSchedulerStarted && !pxThreads) return &xDummyThread; |
| if (!pxThreads) return NULL; |
| for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) |
| { |
| if ( pxThreads[ lIndex ].hThread == hThread ) |
| { |
| return &pxThreads[ lIndex ]; |
| } |
| } |
| if (!xSchedulerStarted) return &xDummyThread; |
| return NULL; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /* next free task handle */ |
| portLONG prvGetFreeThreadState( void ) |
| { |
| portLONG lIndex; |
| for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) |
| { |
| if ( pxThreads[ lIndex ].hThread == ( pthread_t )NULL ) |
| { |
| break; |
| } |
| } |
| |
| if ( MAX_NUMBER_OF_TASKS == lIndex ) |
| { |
| PORT_PRINT( "No more free threads, please increase the maximum.\n" ); |
| lIndex = 0; |
| vPortEndScheduler(); |
| } |
| |
| return lIndex; |
| } |
| |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * delete a thread from the list |
| */ |
| void prvDeleteThread( void *xThreadId ) |
| { |
| portLONG lIndex; |
| for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) |
| { |
| if ( pxThreads[ lIndex ].hThread == ( pthread_t )xThreadId ) |
| { |
| #ifdef DEBUG_SCHEDULER |
| printf("Deleting thread %llx\n", (unsigned long long)xThreadId); |
| #endif |
| pxThreads[ lIndex ].hThread = (pthread_t)NULL; |
| pxThreads[ lIndex ].hTask = (xTaskHandle)NULL; |
| if ( pxThreads[ lIndex ].uxCriticalNesting > 0 ) |
| { |
| //vPortEnableInterrupts(); |
| xInterruptsEnabled = pdTRUE; |
| } |
| pxThreads[ lIndex ].uxCriticalNesting = 0; |
| PORT_UNLOCK( pxThreads[ lIndex].threadSleepMutex ); |
| break; |
| } |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * add a thread to the list |
| */ |
| void vPortAddTaskHandle( void *pxTaskHandle ) |
| { |
| portLONG lIndex; |
| |
| pxThreads[ lIndexOfLastAddedTask ].hTask = ( xTaskHandle )pxTaskHandle; |
| |
| for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) |
| { |
| if ( pxThreads[ lIndex ].hThread == pxThreads[ lIndexOfLastAddedTask ].hThread ) |
| { |
| if ( pxThreads[ lIndex ].hTask != pxThreads[ lIndexOfLastAddedTask ].hTask ) |
| { |
| #ifdef DEBUG_SCHEDULER |
| printf("Deleting thread weirdly %llx\n", (unsigned long long)pxThreads[ lIndex ].hThread); |
| #endif |
| pxThreads[ lIndex ].hThread = ( pthread_t )NULL; |
| pxThreads[ lIndex ].hTask = NULL; |
| pxThreads[ lIndex ].uxCriticalNesting = 0; |
| PORT_UNLOCK( pxThreads[ lIndex].threadSleepMutex ); |
| } |
| } |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * find out system speed |
| */ |
| void vPortFindTicksPerSecond( void ) |
| { |
| /* Needs to be reasonably high for accuracy. */ |
| unsigned long ulTicksPerSecond = sysconf(_SC_CLK_TCK); |
| PORT_PRINT( "Timer Resolution for Run TimeStats is %ld ticks per second.\n", ulTicksPerSecond ); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| /** |
| * timer stuff |
| */ |
| unsigned long ulPortGetTimerValue( void ) |
| { |
| struct tms xTimes; |
| unsigned long ulTotalTime = times( &xTimes ); |
| /* Return the application code times. |
| * The timer only increases when the application code is actually running |
| * which means that the total execution times should add up to 100%. |
| */ |
| return ( unsigned long ) xTimes.tms_utime; |
| |
| /* Should check ulTotalTime for being clock_t max minus 1. */ |
| (void)ulTotalTime; |
| } |
| /*-----------------------------------------------------------*/ |
| |