mirror of
https://github.com/kelvinlawson/atomthreads.git
synced 2026-01-11 18:33:16 +01:00
678 lines
22 KiB
C
Executable File
678 lines
22 KiB
C
Executable File
/*
|
|
* Copyright (c) 2010, Kelvin Lawson. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. No personal names or organizations' names associated with the
|
|
* Atomthreads project may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
/**
|
|
* \file
|
|
* Semaphore library.
|
|
*
|
|
*
|
|
* This module implements a counting semaphore library with the following
|
|
* features:
|
|
*
|
|
* \par Flexible blocking APIs
|
|
* Threads which wish to decrement a semaphore can choose whether to block,
|
|
* block with timeout, or not block if the semaphore has reached zero.
|
|
*
|
|
* \par Interrupt-safe calls
|
|
* All APIs can be called from interrupt context. Any calls which could
|
|
* potentially block have optional parameters to prevent blocking if you
|
|
* wish to call them from interrupt context. Any attempt to make a call
|
|
* which would block from interrupt context will be automatically and
|
|
* safely prevented.
|
|
*
|
|
* \par Priority-based queueing
|
|
* Where multiple threads are blocking on a semaphore, they are woken in
|
|
* order of the threads' priorities. Where multiple threads of the same
|
|
* priority are blocking, they are woken in FIFO order.
|
|
*
|
|
* \par Count up to 255
|
|
* Semaphore counts can be initialised and incremented up to a maximum of 255.
|
|
*
|
|
* \par Smart semaphore deletion
|
|
* Where a semaphore is deleted while threads are blocking on it, all blocking
|
|
* threads are woken and returned a status code to indicate the reason for
|
|
* being woken.
|
|
*
|
|
*
|
|
* \n <b> Usage instructions: </b> \n
|
|
*
|
|
* All semaphore objects must be initialised before use by calling
|
|
* atomSemCreate(). Once initialised atomSemGet() and atomSemPut() are used to
|
|
* decrement and increment the semaphore count respectively.
|
|
*
|
|
* If a semaphore count reaches zero, further calls to atomSemGet() will block
|
|
* the calling thread (unless the calling parameters request no blocking). If
|
|
* a call is made to atomSemPut() while threads are blocking on a zero-count
|
|
* semaphore, the highest priority thread is woken. Where multiple threads of
|
|
* the same priority are blocking, they are woken in the order in which the
|
|
* threads started blocking.
|
|
*
|
|
* A semaphore which is no longer required can be deleted using
|
|
* atomSemDelete(). This function automatically wakes up any threads which are
|
|
* waiting on the deleted semaphore.
|
|
*
|
|
*
|
|
* \n <b> Notes: </b> \n
|
|
*
|
|
* Note that those considering using a semaphore initialised to 1 for mutual
|
|
* exclusion purposes may wish to investigate the mutex library available in
|
|
* Atomthreads.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "atom.h"
|
|
#include "atomsem.h"
|
|
#include "atomtimer.h"
|
|
|
|
|
|
/* Local data types */
|
|
|
|
typedef struct sem_timer
|
|
{
|
|
ATOM_TCB *tcb_ptr; /* Thread which is suspended with timeout */
|
|
ATOM_SEM *sem_ptr; /* Semaphore the thread is suspended on */
|
|
} SEM_TIMER;
|
|
|
|
|
|
/* Forward declarations */
|
|
|
|
static void atomSemTimerCallback (POINTER cb_data);
|
|
|
|
|
|
/**
|
|
* \b atomSemCreate
|
|
*
|
|
* Initialises a semaphore object.
|
|
*
|
|
* Must be called before calling any other semaphore library routines on a
|
|
* semaphore. Objects can be deleted later using atomSemDelete().
|
|
*
|
|
* Does not allocate storage, the caller provides the semaphore object.
|
|
*
|
|
* This function can be called from interrupt context.
|
|
*
|
|
* @param[in] sem Pointer to semaphore object
|
|
* @param[in] initial_count Initial count value
|
|
*
|
|
* @retval ATOM_OK Success
|
|
* @retval ATOM_ERR_PARAM Bad parameters
|
|
*/
|
|
uint8_t atomSemCreate (ATOM_SEM *sem, uint8_t initial_count)
|
|
{
|
|
uint8_t status;
|
|
|
|
/* Parameter check */
|
|
if (sem == NULL)
|
|
{
|
|
/* Bad semaphore pointer */
|
|
status = ATOM_ERR_PARAM;
|
|
}
|
|
else
|
|
{
|
|
/* Set the initial count */
|
|
sem->count = initial_count;
|
|
|
|
/* Initialise the suspended threads queue */
|
|
sem->suspQ = NULL;
|
|
|
|
/* Successful */
|
|
status = ATOM_OK;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/**
|
|
* \b atomSemDelete
|
|
*
|
|
* Deletes a semaphore object.
|
|
*
|
|
* Any threads currently suspended on the semaphore will be woken up with
|
|
* return status ATOM_ERR_DELETED. If called at thread context then the
|
|
* scheduler will be called during this function which may schedule in one
|
|
* of the woken threads depending on relative priorities.
|
|
*
|
|
* This function can be called from interrupt context, but loops internally
|
|
* waking up all threads blocking on the semaphore, so the potential
|
|
* execution cycles cannot be determined in advance.
|
|
*
|
|
* @param[in] sem Pointer to semaphore object
|
|
*
|
|
* @retval ATOM_OK Success
|
|
* @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue
|
|
* @retval ATOM_ERR_TIMER Problem cancelling a timeout on a woken thread
|
|
*/
|
|
uint8_t atomSemDelete (ATOM_SEM *sem)
|
|
{
|
|
uint8_t status;
|
|
CRITICAL_STORE;
|
|
ATOM_TCB *tcb_ptr;
|
|
uint8_t woken_threads = FALSE;
|
|
|
|
/* Parameter check */
|
|
if (sem == NULL)
|
|
{
|
|
/* Bad semaphore pointer */
|
|
status = ATOM_ERR_PARAM;
|
|
}
|
|
else
|
|
{
|
|
/* Default to success status unless errors occur during wakeup */
|
|
status = ATOM_OK;
|
|
|
|
/* Wake up all suspended tasks */
|
|
while (1)
|
|
{
|
|
/* Enter critical region */
|
|
CRITICAL_START ();
|
|
|
|
/* Check if any threads are suspended */
|
|
tcb_ptr = tcbDequeueHead (&sem->suspQ);
|
|
|
|
/* A thread is suspended on the semaphore */
|
|
if (tcb_ptr)
|
|
{
|
|
/* Return error status to the waiting thread */
|
|
tcb_ptr->suspend_wake_status = ATOM_ERR_DELETED;
|
|
|
|
/* Put the thread on the ready queue */
|
|
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
|
{
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* Quit the loop, returning error */
|
|
status = ATOM_ERR_QUEUE;
|
|
break;
|
|
}
|
|
|
|
/* If there's a timeout on this suspension, cancel it */
|
|
if (tcb_ptr->suspend_timo_cb)
|
|
{
|
|
/* Cancel the callback */
|
|
if (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK)
|
|
{
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* Quit the loop, returning error */
|
|
status = ATOM_ERR_TIMER;
|
|
break;
|
|
}
|
|
|
|
/* Flag as no timeout registered */
|
|
tcb_ptr->suspend_timo_cb = NULL;
|
|
|
|
}
|
|
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* Request a reschedule */
|
|
woken_threads = TRUE;
|
|
}
|
|
|
|
/* No more suspended threads */
|
|
else
|
|
{
|
|
/* Exit critical region and quit the loop */
|
|
CRITICAL_END ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Call scheduler if any threads were woken up */
|
|
if (woken_threads == TRUE)
|
|
{
|
|
/**
|
|
* Only call the scheduler if we are in thread context, otherwise
|
|
* it will be called on exiting the ISR by atomIntExit().
|
|
*/
|
|
if (atomCurrentContext())
|
|
atomSched (FALSE);
|
|
}
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/**
|
|
* \b atomSemGet
|
|
*
|
|
* Perform a get operation on a semaphore.
|
|
*
|
|
* This decrements the current count value for the semaphore and returns.
|
|
* If the count value is already zero then the call will block until the
|
|
* count is incremented by another thread, or until the specified \c timeout
|
|
* is reached. Blocking threads will also be woken if the semaphore is
|
|
* deleted by another thread while blocking.
|
|
*
|
|
* Depending on the \c timeout value specified the call will do one of
|
|
* the following if the count value is zero:
|
|
*
|
|
* \c timeout == 0 : Call will block until the count is non-zero \n
|
|
* \c timeout > 0 : Call will block until non-zero up to the specified timeout \n
|
|
* \c timeout == -1 : Return immediately if the count is zero \n
|
|
*
|
|
* If the call needs to block and \c timeout is zero, it will block
|
|
* indefinitely until atomSemPut() or atomSemDelete() is called on the
|
|
* semaphore.
|
|
*
|
|
* If the call needs to block and \c timeout is non-zero, the call will only
|
|
* block for the specified number of system ticks after which time, if the
|
|
* thread was not already woken, the call will return with \c ATOM_TIMEOUT.
|
|
*
|
|
* If the call would normally block and \c timeout is -1, the call will
|
|
* return immediately with \c ATOM_WOULDBLOCK.
|
|
*
|
|
* This function can only be called from interrupt context if the \c timeout
|
|
* parameter is -1 (in which case it does not block).
|
|
*
|
|
* @param[in] sem Pointer to semaphore object
|
|
* @param[in] timeout Max system ticks to block (0 = forever)
|
|
*
|
|
* @retval ATOM_OK Success
|
|
* @retval ATOM_TIMEOUT Semaphore timed out before being woken
|
|
* @retval ATOM_WOULDBLOCK Called with timeout == -1 but count is zero
|
|
* @retval ATOM_ERR_DELETED Semaphore was deleted while suspended
|
|
* @retval ATOM_ERR_CONTEXT Not called in thread context and attempted to block
|
|
* @retval ATOM_ERR_PARAM Bad parameter
|
|
* @retval ATOM_ERR_QUEUE Problem putting the thread on the suspend queue
|
|
* @retval ATOM_ERR_TIMER Problem registering the timeout
|
|
*/
|
|
uint8_t atomSemGet (ATOM_SEM *sem, int32_t timeout)
|
|
{
|
|
CRITICAL_STORE;
|
|
uint8_t status;
|
|
SEM_TIMER timer_data;
|
|
ATOM_TIMER timer_cb;
|
|
ATOM_TCB *curr_tcb_ptr;
|
|
|
|
/* Check parameters */
|
|
if (sem == NULL)
|
|
{
|
|
/* Bad semaphore pointer */
|
|
status = ATOM_ERR_PARAM;
|
|
}
|
|
else
|
|
{
|
|
/* Protect access to the semaphore object and OS queues */
|
|
CRITICAL_START ();
|
|
|
|
/* If count is zero, block the calling thread */
|
|
if (sem->count == 0)
|
|
{
|
|
/* If called with timeout >= 0, we should block */
|
|
if (timeout >= 0)
|
|
{
|
|
/* Count is zero, block the calling thread */
|
|
|
|
/* Get the current TCB */
|
|
curr_tcb_ptr = atomCurrentContext();
|
|
|
|
/* Check we are actually in thread context */
|
|
if (curr_tcb_ptr)
|
|
{
|
|
/* Add current thread to the suspend list on this semaphore */
|
|
if (tcbEnqueuePriority (&sem->suspQ, curr_tcb_ptr) != ATOM_OK)
|
|
{
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* There was an error putting this thread on the suspend list */
|
|
status = ATOM_ERR_QUEUE;
|
|
}
|
|
else
|
|
{
|
|
/* Set suspended status for the current thread */
|
|
curr_tcb_ptr->suspended = TRUE;
|
|
|
|
/* Track errors */
|
|
status = ATOM_OK;
|
|
|
|
/* Register a timer callback if requested */
|
|
if (timeout)
|
|
{
|
|
/* Fill out the data needed by the callback to wake us up */
|
|
timer_data.tcb_ptr = curr_tcb_ptr;
|
|
timer_data.sem_ptr = sem;
|
|
|
|
/* Fill out the timer callback request structure */
|
|
timer_cb.cb_func = atomSemTimerCallback;
|
|
timer_cb.cb_data = (POINTER)&timer_data;
|
|
timer_cb.cb_ticks = timeout;
|
|
|
|
/**
|
|
* Store the timer details in the TCB so that we can
|
|
* cancel the timer callback if the semaphore is put
|
|
* before the timeout occurs.
|
|
*/
|
|
curr_tcb_ptr->suspend_timo_cb = &timer_cb;
|
|
|
|
/* Register a callback on timeout */
|
|
if (atomTimerRegister (&timer_cb) != ATOM_OK)
|
|
{
|
|
/* Timer registration failed */
|
|
status = ATOM_ERR_TIMER;
|
|
|
|
/* Clean up and return to the caller */
|
|
(void)tcbDequeueEntry (&sem->suspQ, curr_tcb_ptr);
|
|
curr_tcb_ptr->suspended = FALSE;
|
|
curr_tcb_ptr->suspend_timo_cb = NULL;
|
|
}
|
|
}
|
|
|
|
/* Set no timeout requested */
|
|
else
|
|
{
|
|
/* No need to cancel timeouts on this one */
|
|
curr_tcb_ptr->suspend_timo_cb = NULL;
|
|
}
|
|
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* Check no errors have occurred */
|
|
if (status == ATOM_OK)
|
|
{
|
|
/**
|
|
* Current thread now blocking, schedule in a new
|
|
* one. We already know we are in thread context
|
|
* so can call the scheduler from here.
|
|
*/
|
|
atomSched (FALSE);
|
|
|
|
/**
|
|
* Normal atomSemPut() wakeups will set ATOM_OK status,
|
|
* while timeouts will set ATOM_TIMEOUT and semaphore
|
|
* deletions will set ATOM_ERR_DELETED.
|
|
*/
|
|
status = curr_tcb_ptr->suspend_wake_status;
|
|
|
|
/**
|
|
* If we have been woken up with ATOM_OK then
|
|
* another thread incremented the semaphore and
|
|
* handed control to this thread. In theory the
|
|
* the posting thread increments the counter and
|
|
* as soon as this thread wakes up we decrement
|
|
* the counter here, but to prevent another
|
|
* thread preempting this thread and decrementing
|
|
* the semaphore before this section was
|
|
* scheduled back in, we emulate the increment
|
|
* and decrement by not incrementing in the
|
|
* atomSemPut() and not decrementing here. The
|
|
* count remains zero throughout preventing other
|
|
* threads preempting before we decrement the
|
|
* count again.
|
|
*/
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* Not currently in thread context, can't suspend */
|
|
status = ATOM_ERR_CONTEXT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* timeout == -1, requested not to block and count is zero */
|
|
CRITICAL_END();
|
|
status = ATOM_WOULDBLOCK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Count is non-zero, just decrement it and return to calling thread */
|
|
sem->count--;
|
|
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* Successful */
|
|
status = ATOM_OK;
|
|
}
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/**
|
|
* \b atomSemPut
|
|
*
|
|
* Perform a put operation on a semaphore.
|
|
*
|
|
* This increments the current count value for the semaphore and returns.
|
|
*
|
|
* If the count value was previously zero and there are threads blocking on the
|
|
* semaphore, the call will wake up the highest priority thread suspended. Only
|
|
* one thread is woken per call to atomSemPut(). If multiple threads of the
|
|
* same priority are suspended, they are woken in order of suspension (FIFO).
|
|
*
|
|
* This function can be called from interrupt context.
|
|
*
|
|
* @param[in] sem Pointer to semaphore object
|
|
*
|
|
* @retval ATOM_OK Success
|
|
* @retval ATOM_ERR_OVF The semaphore count would have overflowed (>255)
|
|
* @retval ATOM_ERR_PARAM Bad parameter
|
|
* @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue
|
|
* @retval ATOM_ERR_TIMER Problem cancelling a timeout for a woken thread
|
|
*/
|
|
uint8_t atomSemPut (ATOM_SEM * sem)
|
|
{
|
|
uint8_t status;
|
|
CRITICAL_STORE;
|
|
ATOM_TCB *tcb_ptr;
|
|
|
|
/* Check parameters */
|
|
if (sem == NULL)
|
|
{
|
|
/* Bad semaphore pointer */
|
|
status = ATOM_ERR_PARAM;
|
|
}
|
|
else
|
|
{
|
|
/* Protect access to the semaphore object and OS queues */
|
|
CRITICAL_START ();
|
|
|
|
/* If any threads are blocking on the semaphore, wake up one */
|
|
if (sem->suspQ)
|
|
{
|
|
/**
|
|
* Threads are woken up in priority order, with a FIFO system
|
|
* used on same priority threads. We always take the head,
|
|
* ordering is taken care of by an ordered list enqueue.
|
|
*/
|
|
tcb_ptr = tcbDequeueHead (&sem->suspQ);
|
|
if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK)
|
|
{
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/* There was a problem putting the thread on the ready queue */
|
|
status = ATOM_ERR_QUEUE;
|
|
}
|
|
else
|
|
{
|
|
/* Set OK status to be returned to the waiting thread */
|
|
tcb_ptr->suspend_wake_status = ATOM_OK;
|
|
|
|
/* If there's a timeout on this suspension, cancel it */
|
|
if ((tcb_ptr->suspend_timo_cb != NULL)
|
|
&& (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK))
|
|
{
|
|
/* There was a problem cancelling a timeout on this semaphore */
|
|
status = ATOM_ERR_TIMER;
|
|
}
|
|
else
|
|
{
|
|
/* Flag as no timeout registered */
|
|
tcb_ptr->suspend_timo_cb = NULL;
|
|
|
|
/* Successful */
|
|
status = ATOM_OK;
|
|
}
|
|
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/**
|
|
* The scheduler may now make a policy decision to thread
|
|
* switch if we are currently in thread context. If we are
|
|
* in interrupt context it will be handled by atomIntExit().
|
|
*/
|
|
if (atomCurrentContext())
|
|
atomSched (FALSE);
|
|
}
|
|
}
|
|
|
|
/* If no threads waiting, just increment the count and return */
|
|
else
|
|
{
|
|
/* Check for count overflow */
|
|
if (sem->count == 255)
|
|
{
|
|
/* Don't increment, just return error status */
|
|
status = ATOM_ERR_OVF;
|
|
}
|
|
else
|
|
{
|
|
/* Increment the count and return success */
|
|
sem->count++;
|
|
status = ATOM_OK;
|
|
}
|
|
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
}
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/**
|
|
* \b atomSemResetCount
|
|
*
|
|
* Set a new count value on a semaphore.
|
|
*
|
|
* Care must be taken when using this function, as there may be threads
|
|
* suspended on the semaphore. In general it should only be used once a
|
|
* semaphore is out of use.
|
|
*
|
|
* This function can be called from interrupt context.
|
|
*
|
|
* @param[in] sem Pointer to semaphore object
|
|
* @param[in] count New count value
|
|
*
|
|
* @retval ATOM_OK Success
|
|
* @retval ATOM_ERR_PARAM Bad parameter
|
|
*/
|
|
uint8_t atomSemResetCount (ATOM_SEM *sem, uint8_t count)
|
|
{
|
|
uint8_t status;
|
|
|
|
/* Parameter check */
|
|
if (sem == NULL)
|
|
{
|
|
/* Bad semaphore pointer */
|
|
status = ATOM_ERR_PARAM;
|
|
}
|
|
else
|
|
{
|
|
/* Set the count */
|
|
sem->count = count;
|
|
|
|
/* Successful */
|
|
status = ATOM_OK;
|
|
}
|
|
|
|
return (status);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* \b atomSemTimerCallback
|
|
*
|
|
* This is an internal function not for use by application code.
|
|
*
|
|
* Timeouts on suspended threads are notified by the timer system through
|
|
* this generic callback. The timer system calls us back with a pointer to
|
|
* the relevant \c SEM_TIMER object which is used to retrieve the
|
|
* semaphore details.
|
|
*
|
|
* @param[in] cb_data Pointer to a SEM_TIMER object
|
|
*/
|
|
static void atomSemTimerCallback (POINTER cb_data)
|
|
{
|
|
SEM_TIMER *timer_data_ptr;
|
|
CRITICAL_STORE;
|
|
|
|
/* Get the SEM_TIMER structure pointer */
|
|
timer_data_ptr = (SEM_TIMER *)cb_data;
|
|
|
|
/* Check parameter is valid */
|
|
if (timer_data_ptr)
|
|
{
|
|
/* Enter critical region */
|
|
CRITICAL_START ();
|
|
|
|
/* Set status to indicate to the waiting thread that it timed out */
|
|
timer_data_ptr->tcb_ptr->suspend_wake_status = ATOM_TIMEOUT;
|
|
|
|
/* Flag as no timeout registered */
|
|
timer_data_ptr->tcb_ptr->suspend_timo_cb = NULL;
|
|
|
|
/* Remove this thread from the semaphore's suspend list */
|
|
(void)tcbDequeueEntry (&timer_data_ptr->sem_ptr->suspQ, timer_data_ptr->tcb_ptr);
|
|
|
|
/* Put the thread on the ready queue */
|
|
(void)tcbEnqueuePriority (&tcbReadyQ, timer_data_ptr->tcb_ptr);
|
|
|
|
/* Exit critical region */
|
|
CRITICAL_END ();
|
|
|
|
/**
|
|
* Note that we don't call the scheduler now as it will be called
|
|
* when we exit the ISR by atomIntExit().
|
|
*/
|
|
}
|
|
}
|