#include <stdio.h>
#include "atom.h"
#include "atomsem.h"
#include "atomtimer.h"
Data Structures | |
struct | sem_timer |
Typedefs | |
typedef struct sem_timer | SEM_TIMER |
Functions | |
uint8_t | atomSemCreate (ATOM_SEM *sem, uint8_t initial_count) |
uint8_t | atomSemDelete (ATOM_SEM *sem) |
uint8_t | atomSemGet (ATOM_SEM *sem, int32_t timeout) |
uint8_t | atomSemPut (ATOM_SEM *sem) |
uint8_t | atomSemResetCount (ATOM_SEM *sem, uint8_t count) |
Semaphore library.
This module implements a counting semaphore library with the following features:
Usage instructions:
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.
Notes:
Note that those considering using a semaphore initialised to 1 for mutual exclusion purposes may wish to investigate the mutex library available in Atomthreads.
uint8_t atomSemCreate | ( | ATOM_SEM * | sem, | |
uint8_t | initial_count | |||
) |
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.
[in] | sem | Pointer to semaphore object |
[in] | initial_count | Initial count value |
ATOM_OK | Success | |
ATOM_ERR_PARAM | Bad parameters |
References ATOM_ERR_PARAM, ATOM_OK, atom_sem::count, atom_sem::suspQ, and uint8_t.
uint8_t atomSemDelete | ( | ATOM_SEM * | sem | ) |
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.
[in] | sem | Pointer to semaphore object |
ATOM_OK | Success | |
ATOM_ERR_QUEUE | Problem putting a woken thread on the ready queue | |
ATOM_ERR_TIMER | Problem cancelling a timeout on a woken thread |
Only call the scheduler if we are in thread context, otherwise it will be called on exiting the ISR by atomIntExit().
References ATOM_ERR_DELETED, ATOM_ERR_PARAM, ATOM_ERR_QUEUE, ATOM_ERR_TIMER, ATOM_OK, atomCurrentContext(), atomSched(), atomTimerCancel(), CRITICAL_END, CRITICAL_START, CRITICAL_STORE, FALSE, atom_tcb::suspend_timo_cb, atom_tcb::suspend_wake_status, atom_sem::suspQ, tcbDequeueHead(), tcbEnqueuePriority(), tcbReadyQ, TRUE, and uint8_t.
uint8_t atomSemGet | ( | ATOM_SEM * | sem, | |
int32_t | timeout | |||
) |
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 timeout
is reached. Blocking threads will also be woken if the semaphore is deleted by another thread while blocking.
Depending on the timeout
value specified the call will do one of the following if the count value is zero:
timeout
== 0 : Call will block until the count is non-zero
timeout
> 0 : Call will block until non-zero up to the specified timeout
timeout
== -1 : Return immediately if the count is zero
If the call needs to block and timeout
is zero, it will block indefinitely until atomSemPut() or atomSemDelete() is called on the semaphore.
If the call needs to block and 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 ATOM_TIMEOUT
.
If the call would normally block and timeout
is -1, the call will return immediately with ATOM_WOULDBLOCK
.
This function can only be called from interrupt context if the timeout
parameter is -1 (in which case it does not block).
[in] | sem | Pointer to semaphore object |
[in] | timeout | Max system ticks to block (0 = forever) |
ATOM_OK | Success | |
ATOM_TIMEOUT | Semaphore timed out before being woken | |
ATOM_WOULDBLOCK | Called with timeout == -1 but count is zero | |
ATOM_ERR_DELETED | Semaphore was deleted while suspended | |
ATOM_ERR_CONTEXT | Not called in thread context and attempted to block | |
ATOM_ERR_PARAM | Bad parameter | |
ATOM_ERR_QUEUE | Problem putting the thread on the suspend queue | |
ATOM_ERR_TIMER | Problem registering the 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.
Current thread now blocking, schedule in a new one. We already know we are in thread context so can call the scheduler from here.
Normal atomSemPut() wakeups will set ATOM_OK status, while timeouts will set ATOM_TIMEOUT and semaphore deletions will set ATOM_ERR_DELETED.
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.
References ATOM_ERR_CONTEXT, ATOM_ERR_PARAM, ATOM_ERR_QUEUE, ATOM_ERR_TIMER, ATOM_OK, ATOM_WOULDBLOCK, atomCurrentContext(), atomSched(), atomTimerRegister(), atom_timer::cb_data, atom_timer::cb_func, atom_timer::cb_ticks, atom_sem::count, CRITICAL_END, CRITICAL_START, CRITICAL_STORE, FALSE, POINTER, sem_timer::sem_ptr, atom_tcb::suspend_timo_cb, atom_tcb::suspend_wake_status, atom_tcb::suspended, atom_sem::suspQ, sem_timer::tcb_ptr, tcbDequeueEntry(), tcbEnqueuePriority(), TRUE, and uint8_t.
uint8_t atomSemPut | ( | ATOM_SEM * | sem | ) |
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.
[in] | sem | Pointer to semaphore object |
ATOM_OK | Success | |
ATOM_ERR_OVF | The semaphore count would have overflowed (>255) | |
ATOM_ERR_PARAM | Bad parameter | |
ATOM_ERR_QUEUE | Problem putting a woken thread on the ready queue | |
ATOM_ERR_TIMER | Problem cancelling a timeout for a woken thread |
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.
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().
References ATOM_ERR_OVF, ATOM_ERR_PARAM, ATOM_ERR_QUEUE, ATOM_ERR_TIMER, ATOM_OK, atomCurrentContext(), atomSched(), atomTimerCancel(), atom_sem::count, CRITICAL_END, CRITICAL_START, CRITICAL_STORE, FALSE, atom_tcb::suspend_timo_cb, atom_tcb::suspend_wake_status, atom_sem::suspQ, tcbDequeueHead(), tcbEnqueuePriority(), tcbReadyQ, and uint8_t.
uint8_t atomSemResetCount | ( | ATOM_SEM * | sem, | |
uint8_t | count | |||
) |
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.
[in] | sem | Pointer to semaphore object |
[in] | count | New count value |
ATOM_OK | Success | |
ATOM_ERR_PARAM | Bad parameter |
References ATOM_ERR_PARAM, ATOM_OK, atom_sem::count, and uint8_t.