atommutex.c File Reference

#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)

Detailed Description

Mutex library.

This module implements a mutual exclusion library with the following features:

Flexible blocking APIs
Threads which wish to take a mutex lock can choose whether to block, block with timeout, or not block if the mutex is already locked by a different thread.
Interrupt-safe calls
Some APIs can be called from interrupt context, but because a mutex must be owned by a particular thread the lock/unlock calls must be performed by threads only (i.e. it does not make sense to allow calls from interrupt context). All APIs are documented with their capability of being called from interrupt context. Any attempt to make a call which cannot be made from interrupt context will be automatically and safely prevented.
Priority-based queueing
Where multiple threads are blocking on a mutex, they are woken in order of the threads' priorities. Where multiple threads of the same priority are blocking, they are woken in FIFO order.
Recursive locks
A mutex can be locked recursively by the same thread up to a maximum recursion level of 255. An internal count of locks is maintained and the mutex is only released when the count reaches zero (when the thread has been unlocked the same number of times as it was locked). This makes a mutex more suitable for use as mutual exclusions than a semaphore with initial count of 1.
Thread ownership
Once a thread has locked a mutex, only that thread may release the lock. This is another feature which makes the mutex more suitable for mutual exclusion than a semaphore with initial count 1. It prevents programming errors whereby the wrong thread is used to perform the unlock. This cannot be done for semaphores which do not have a concept of ownership (because it must be possible to use them to signal between threads).
Smart mutex deletion
Where a mutex 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.


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 Documentation

typedef struct mutex_timer MUTEX_TIMER

Function Documentation

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.

Parameters:
[in] mutex Pointer to mutex object
Return values:
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.

Parameters:
[in] mutex Pointer to mutex object
Return values:
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.

Parameters:
[in] mutex Pointer to mutex object
[in] timeout Max system ticks to block (0 = forever)
Return values:
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.

Parameters:
[in] mutex Pointer to mutex object
Return values:
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.


Generated on Fri Jun 4 01:00:01 2010 for atomthreads by  doxygen 1.6.1