#include <stdio.h>
#include "atom.h"
#include "atommutex.h"
#include "atomtimer.h"
Data Structures | |
struct | mutex_timer |
Typedefs | |
typedef struct mutex_timer | MUTEX_TIMER |
Functions | |
uint8_t | atomMutexCreate (ATOM_MUTEX *mutex) |
uint8_t | atomMutexDelete (ATOM_MUTEX *mutex) |
uint8_t | atomMutexGet (ATOM_MUTEX *mutex, int32_t timeout) |
uint8_t | atomMutexPut (ATOM_MUTEX *mutex) |
Mutex library.
This module implements a mutual exclusion library with the following features:
Usage instructions:
All mutex objects must be initialised before use by calling atomMutexCreate(). Once initialised atomMutexGet() and atomMutexPut() are used to lock and unlock the mutex respectively. A mutex may be locked recursively by the same thread, allowing for simplified code structure.
While a thread owns the lock on a mutex, no other thread can take the lock. These other threads will block until the mutex is released by the current owner (unless the calling parameters request no blocking, in which case the lock request will return with an error). If a mutex is released while threads are blocking on it, 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 mutex which is no longer required can be deleted using atomMutexDelete(). This function automatically wakes up any threads which are waiting on the deleted mutex.
typedef struct mutex_timer MUTEX_TIMER |
uint8_t atomMutexCreate | ( | ATOM_MUTEX * | mutex | ) |
atomMutexCreate
Initialises a mutex object.
Must be called before calling any other mutex library routines on a mutex. Objects can be deleted later using atomMutexDelete().
Does not set the owner of a mutex. atomMutexGet() must be called after creation in order to actually take ownership.
Does not allocate storage, the caller provides the mutex object.
This function can be called from interrupt context.
[in] | mutex | Pointer to mutex object |
ATOM_OK | Success | |
ATOM_ERR_PARAM | Bad parameters |
References ATOM_ERR_PARAM, ATOM_OK, atom_mutex::count, atom_mutex::owner, atom_mutex::suspQ, and uint8_t.
uint8_t atomMutexDelete | ( | ATOM_MUTEX * | mutex | ) |
atomMutexDelete
Deletes a mutex object.
Any threads currently suspended on the mutex 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 mutex, so the potential execution cycles cannot be determined in advance.
[in] | mutex | Pointer to mutex 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_mutex::suspQ, tcbDequeueHead(), tcbEnqueuePriority(), tcbReadyQ, TRUE, and uint8_t.
uint8_t atomMutexGet | ( | ATOM_MUTEX * | mutex, | |
int32_t | timeout | |||
) |
atomMutexGet
Take the lock on a mutex.
This takes ownership of a mutex if it is not currently owned. Ownership is held by this thread until a corresponding call to atomMutexPut() by the same thread.
Can be called recursively by the original locking thread (owner). Recursive calls are counted, and ownership is not relinquished until the number of unlock (atomMutexPut()) calls by the owner matches the number of lock (atomMutexGet()) calls.
No thread other than the owner can lock or unlock the mutex while it is locked by another thread.
Depending on the timeout
value specified the call will do one of the following if the mutex is already locked by another thread:
timeout
== 0 : Call will block until the mutex is available
timeout
> 0 : Call will block until available up to the specified timeout
timeout
== -1 : Return immediately if mutex is locked by another thread
If the call needs to block and timeout
is zero, it will block indefinitely until the owning thread calls atomMutexPut() or atomMutexDelete() is called on the mutex.
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 thread context. A mutex has the concept of an owner thread, so it is never valid to make a mutex call from interrupt context when there is no thread to associate with.
[in] | mutex | Pointer to mutex object |
[in] | timeout | Max system ticks to block (0 = forever) |
ATOM_OK | Success | |
ATOM_TIMEOUT | Mutex timed out before being woken | |
ATOM_WOULDBLOCK | Called with timeout == -1 but count is zero | |
ATOM_ERR_DELETED | Mutex 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 | |
ATOM_ERR_OVF | The recursive lock count would have overflowed (>255) |
Check we are at thread context. Because mutexes have the concept of owner threads, it is never valid to call here from an ISR, regardless of whether we will block.
Store the timer details in the TCB so that we can cancel the timer callback if the mutex 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 atomMutexPut() wakeups will set ATOM_OK status, while timeouts will set ATOM_TIMEOUT and mutex deletions will set ATOM_ERR_DELETED.
If we were woken up by another thread relinquishing the mutex and handing this thread ownership, then the relinquishing thread will set status to ATOM_OK and will make this thread the owner. Setting the owner before waking the thread ensures that no other thread can preempt and take ownership of the mutex between this thread being made ready to run, and actually being scheduled back in here.
Since this thread has just gained ownership, the lock count is zero and should be incremented once for this call.
References ATOM_ERR_CONTEXT, ATOM_ERR_OVF, 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_mutex::count, CRITICAL_END, CRITICAL_START, CRITICAL_STORE, FALSE, mutex_timer::mutex_ptr, atom_mutex::owner, POINTER, atom_tcb::suspend_timo_cb, atom_tcb::suspend_wake_status, atom_tcb::suspended, atom_mutex::suspQ, mutex_timer::tcb_ptr, tcbDequeueEntry(), tcbEnqueuePriority(), TRUE, and uint8_t.
uint8_t atomMutexPut | ( | ATOM_MUTEX * | mutex | ) |
atomMutexPut
Give back the lock on a mutex.
This checks that the mutex is owned by the calling thread, and decrements the recursive lock count. Once the lock count reaches zero, the lock is considered relinquished and no longer owned by this thread.
If the lock is relinquished and there are threads blocking on the mutex, the call will wake up the highest priority thread suspended. Only one thread is woken per call to atomMutexPut(). If multiple threads of the same priority are suspended, they are woken in order of suspension (FIFO).
This function can only be called from thread context. A mutex has the concept of an owner thread, so it is never valid to make a mutex call from interrupt context when there is no thread to associate with.
[in] | mutex | Pointer to mutex object |
ATOM_OK | Success | |
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 | |
ATOM_ERR_OWNERSHIP | Attempt to unlock mutex not owned by this 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. We already know we are in thread context so can call the scheduler from here.
Relinquished ownership and no threads waiting. Nothing to do.
Decremented lock but still retain ownership due to recursion. Nothing to do.
References ATOM_ERR_OWNERSHIP, ATOM_ERR_PARAM, ATOM_ERR_QUEUE, ATOM_ERR_TIMER, ATOM_OK, atomCurrentContext(), atomSched(), atomTimerCancel(), atom_mutex::count, CRITICAL_END, CRITICAL_START, CRITICAL_STORE, FALSE, atom_mutex::owner, atom_tcb::suspend_timo_cb, atom_tcb::suspend_wake_status, atom_mutex::suspQ, tcbDequeueHead(), tcbEnqueuePriority(), tcbReadyQ, and uint8_t.