LAR Library
1.14
|
Macros | |
#define | TASK_INFINITE_TIMEOUT (0xFFFFFFFFLU) |
Use this constant to wait forever when a timeout is necessary. More... | |
Typedefs | |
typedef struct taskThread_t | taskThread_t |
A handle to a running thread. | |
typedef void(* | taskThreadFunction_t) (void *param) |
Type of a function pointer to be used as entry point of a new thread. More... | |
typedef struct taskQueue_t | taskQueue_t |
Handle to a multi-threading queue. | |
typedef struct taskSemaphore_t | taskSemaphore_t |
Handle to a semaphore. | |
typedef struct taskEvent_t | taskEvent_t |
Handle to an event. | |
Functions | |
taskThread_t * | taskThreadCreate (taskThreadFunction_t fn, void *param, uint32_t stackSize) |
Create a new execution thread with entry point fn . More... | |
void | taskSleep (uint32_t timeout) |
Sleep the current running thread. More... | |
taskThread_t * | taskThreadCurrent (void) |
Return the handle of the current running thread. More... | |
taskQueue_t * | taskQueueCreate (int maxItems) |
Create a new queue with up to maxItems allowed. More... | |
void | taskQueueDestroy (taskQueue_t *q) |
Destroy a queue and discards all pending elements. More... | |
int | taskQueuePost (taskQueue_t *q, void *item) |
Insert an element on the back of the queue q . More... | |
int | taskQueuePop (taskQueue_t *q, void **item, uint32_t timeout) |
Extract the element from the front of the queue. More... | |
taskSemaphore_t * | taskSemaphoreCreate (int limit) |
Create a semaphore with a maximum entry count of limit . More... | |
void | taskSemaphoreDestroy (taskSemaphore_t *s) |
Destroy an existing semaphore. More... | |
int | taskSemaphoreAcquire (taskSemaphore_t *s, uint32_t timeout) |
Acquire (also called wait or down) the semaphore. More... | |
int | taskSemaphoreRelease (taskSemaphore_t *s) |
Release (also called signal or up) a previously acquired semaphore. More... | |
taskEvent_t * | taskEventCreate (void) |
Create a new event handler. More... | |
taskEvent_t * | taskEventCreateSystem (uint32_t bitmask) |
Creates an event handler to wait for system peripheral events. More... | |
void | taskEventDestroy (taskEvent_t *e) |
Release the resources associated with an event handler. More... | |
int | taskEventSignal (taskEvent_t *e) |
Set an event to the signaled state. More... | |
int | taskEventCheck (taskEvent_t *e) |
Check if an event if signaled, without waiting or clearing the signal. More... | |
int | taskEventWait (taskEvent_t *e, uint32_t timeout) |
Wait for e to change to signaled state. More... | |
int | taskEventWaitAny (taskEvent_t *events[], int nevents, uint32_t timeout) |
Wait for at least one among a list of events to be signaled. More... | |
int | taskEventClear (taskEvent_t *e) |
Set an event as non-signaled. More... | |
Detailed Description
Rationale
Multi-threading capabilities are necessary for cases such as background dialing, controlling of external devices and background processing. Unfortunately OS capabilities and APIs vary widely, so a common ground is necessary to ease porting.
Introduction
This module deals with multi-threading, or the creation of simultaneous threads of execution, and synchronization of those threads. The following objects are given:
- Threads: an application may create parallel threads of execution;
- Queues: FIFO (first-in-first-out) sequences of values designed so one thread can communicate with other in an orderly manner;
- Events: signals between threads, that carry no other information other than the event has been triggered;
- Semaphores: restrict simultaneous access to resources.
Threads
Each thread is a parallel point of execution that has access to the same global environment (global variables, APIs) as the application as a whole, but a local stack (for local variables).
Because all threads access the same set of global variables great care must be taken so two threads do not attempt to modify the same memory location at the same time.
Each thread has an associated amount of memory for its stack, the application may "suggest" a minimum size for this stack on the call to taskThreadCreate(), but the size of the stack is ultimately platform-dependent.
After a thread has finished running (i.e. the user provided function has returned) its taskThread_t handle becomes invalid, it is the user responsibility to guarantee that the handle is not used in this case.
This module does not provide any form of thread-local storage, if such is required the application must implement this itself.
- Attention
- Each platform may have an unspecified limit on the number of concurrent tasks that may be created at each time!
See http://en.wikipedia.org/wiki/Thread_(computer_science) and http://en.wikipedia.org/wiki/Thread_safety
Queues
A queue is a variable-sized array of elements with first-in-first-out semantics. The two basic operations provided are taskQueuePost() which adds an element to the front of the queue, and taskQueuePop() which removes an element from the back of the queue.
This implementation is limited to queue elements of type void*
, they can be used to directly encode integer values (small enough to fit on an address) or point themselves to the actual value.
Queues are the recommended method of intra-thread communication, but the application must be careful to enforce move semantics: i.e. when a value (specially in case of pointers) is sent from one thread to another using a queue, the sender thread is in effect relinquishing ownership of this element and passing to the receiver thread. Carefully applying this methodology has the potential of reducing errors caused by simultaneous access to memory locations (see reference below).
Note that, as in the Double-ended queue module, the queue does no special handling of the void*
pointers stored in it, more specifically, it will not call memFree() on any pointer, the application must be careful to release any memory itself (specially when taskQueueDestroy() is called).
See http://en.wikipedia.org/wiki/Message_passing
Events
Events signal the occurrence of something. They are a binary marker with the states signaled and non-signaled. When created, a taskEvent_t is in non-signaled state, and only a call to taskEventSignal() will change this.
Threads may wait until an event is in signaled state by calling taskEventWait() or taskEventWaitAny(). The difference is that the first is a simpler API when it is necessary to wait for only one event handle, while the second allows to wait for multiple event handles on the same call.
Device Events
Both supported platforms (Unicapt32, Telium) use a bitmap (encoded in a 32-bit unsigned integer) to mark which events have occurred. This API support interface with those OS-provided event facilities by using taskEventCreateSystem().
A taskEvent_t created this way works as any other event when used with taskEventWait() or taskEventWaitAny(), but will not work when used with taskEventSignal(), only OS APIs will work.
Note that since which bits are used for which peripherals changes from platform to platform, this in itself is not a completely portable solution.
- Note
- On Unicapt32 only device events are supported, user events generated by
psyEventSend()
and such are not supported, this is because the bitmap associated with each event defines the first parameter topsyPeripheralResultWait()
, but do not handle the case ofPSY_EVENT_RECEIVED
.
Semaphores
Semaphores limit the number of threads that can simultaneously enter a given region of code, called a critical-region. The most common case being of allowing only one thread to enter a critical region at a time.
This is useful to control access to resources where a queue or an event would not apply, for example access to a file.
For example, consider the (very) constrained case where two threads are repeatedly appending information to a same file using a function appendToFile()
that receives as parameter the name of the file and a string message to append to it:
The semaphore mutex
is used to forbid the two tasks from access the file "global.txt"
at the same time. (Note that this convoluted example is un-optimal in many respects. If you find yourself writing this sort of architecture on a real application, please reconsider! A more scalable approach would be having an append-to-file thread that receives requests of strings to write to the global file from a taskQueue_t).
See http://en.wikipedia.org/wiki/Semaphore_(programming)
Macro Definition Documentation
#define TASK_INFINITE_TIMEOUT (0xFFFFFFFFLU) |
Use this constant to wait forever when a timeout is necessary.
Typedef Documentation
typedef void(* taskThreadFunction_t) (void *param) |
Type of a function pointer to be used as entry point of a new thread.
- Parameters
-
param The user-provided param
as given to taskThreadCreate()
Function Documentation
int taskEventCheck | ( | taskEvent_t * | e | ) |
Check if an event if signaled, without waiting or clearing the signal.
- Attention
- This does not work with events created by taskEventCreateSystem().
- Parameters
-
e Handle to event
- Returns
- BASE_ERR_OK if
e
is signaled. -
BASE_ERR_TIMEOUT if
e
was not signaled. -
BASE_ERR_INVALID_HANDLE if
e
is invalid ir was created by taskEventCreateSystem(). - BASE_ERR_RESOURCE_PB in case of internal error.
int taskEventClear | ( | taskEvent_t * | e | ) |
Set an event as non-signaled.
This call may be used to "clean the slate" before other operations, discarding previous signals.
- Attention
- This does not work with events created by taskEventCreateSystem().
- Parameters
-
e Handle to event.
- Returns
- BASE_ERR_OK on success.
-
BASE_ERR_INVALID_HANDLE if
e
is invalid. - BASE_ERR_RESOURCE_PB in case of internal error
taskEvent_t* taskEventCreate | ( | void | ) |
Create a new event handler.
The event is created in non-signaled state. Multiple events can be created and the limit on number of created events is not related to the platform event handling primitives.
- Returns
- An event handler or
NULL
on error.
taskEvent_t* taskEventCreateSystem | ( | uint32_t | bitmask | ) |
Creates an event handler to wait for system peripheral events.
Both Unicapt32 and Telium platforms use a bitmap to check for device events (see psyPeripheralResultWait()
and ttestall()
). This call provides a bridge between taskEvent_t and system peripheral events.
Internally any call to taskEventWait() or taskEventWaitAny() with events created this way will call the OS functionality to wait for peripherals.
For platforms where this form of event handling does not exist, this call will return NULL
.
Event handlers created in this way also need to be destroyed by calling taskEventDestroy().
- Note
- Signaling an event created this way (by taskEventSignal()) is not supported. Those events can only be triggered by the OS, either internally or by calling OS APIs.
- Parameters
-
bitmask Which system event bits to wait for. The 31st bit (0x80000000) is reserved for internal use, trying to create an event for it will return NULL
instead.
- Returns
- A valid taskEvent_t handle, or
NULL
ifbitmask
is invalid or peripheral events are not supported in this platform.
void taskEventDestroy | ( | taskEvent_t * | e | ) |
Release the resources associated with an event handler.
What happens to threads blocked waiting for this event is undefined and platform-dependent.
- Parameters
-
e Handle to event.
int taskEventSignal | ( | taskEvent_t * | e | ) |
Set an event to the signaled state.
If the event is already signaled, nothing changes.
- Attention
- This does not work with events created by taskEventCreateSystem().
- Parameters
-
e Handle to event.
- Returns
- BASE_ERR_OK on success.
-
BASE_ERR_INVALID_HANDLE if
e
is invalid or created with taskEventCreateSystem().
int taskEventWait | ( | taskEvent_t * | e, |
uint32_t | timeout | ||
) |
Wait for e
to change to signaled state.
If e
is already signaled, will return immediately.
Non-system events are automatically changed back to non-signaled state. For system events this is platform-dependent.
- Parameters
-
e Handle to event. timeout Max time to wait for e
to signal, in hundreds of second.
- Returns
- BASE_ERR_OK if
e
was signaled. -
BASE_ERR_TIMEOUT if
e
was not signaled. -
BASE_ERR_INVALID_HANDLE if
e
is invalid. - BASE_ERR_RESOURCE_PB in case of internal error
int taskEventWaitAny | ( | taskEvent_t * | events[], |
int | nevents, | ||
uint32_t | timeout | ||
) |
Wait for at least one among a list of events to be signaled.
Calling this function with nevents == 1
is equivalent to a call to taskEventWait().
Example:
- Parameters
-
events Array of event handles to wait for. nevents Number of elements in events
.timeout Max time to wait for an event to signal, in hundreds of second.
- Returns
- The (zero-based) index (inside
events
) of the first event to be detected. - BASE_ERR_TIMEOUT if no event was signaled.
-
BASE_ERR_INVALID_HANDLE if any element in
events
is invalid. - BASE_ERR_RESOURCE_PB in case of internal error
taskQueue_t* taskQueueCreate | ( | int | maxItems | ) |
Create a new queue with up to maxItems
allowed.
- Parameters
-
maxItems Max number of items that fit on this queue.
- Returns
- Handle to new queue or
NULL
on error.
void taskQueueDestroy | ( | taskQueue_t * | q | ) |
Destroy a queue and discards all pending elements.
- Parameters
-
q Queue to be discarded.
- Note
- The queue elements are discarded with no further processing, which may cause resource and/or memory leaks. If a taskQueuePop() is waiting the behavior is undefined and platform-dependent.
int taskQueuePop | ( | taskQueue_t * | q, |
void ** | item, | ||
uint32_t | timeout | ||
) |
Extract the element from the front of the queue.
Example:
- Parameters
-
q Handle to queue. [out] item Where to store the element extracted from q
(note the double indirection!).timeout Time to wait for element, in hundreds of seconds.
- Returns
- BASE_ERR_OK on success
-
BASE_ERR_INVALID_HANDLE if
q
is invalid -
BASE_ERR_INVALID_PARAMETER if
item
isNULL
-
BASE_ERR_TIMEOUT if no element was received in
timeout
int taskQueuePost | ( | taskQueue_t * | q, |
void * | item | ||
) |
Insert an element on the back of the queue q
.
Example:
- Parameters
-
q Handle to queue. item Item to be inserted.
- Returns
- BASE_ERR_OK on success
-
BASE_ERR_INVALID_HANDLE if
q
is invalid. -
BASE_ERR_OVERFLOW if
q
already has the max number of elements allowed.
int taskSemaphoreAcquire | ( | taskSemaphore_t * | s, |
uint32_t | timeout | ||
) |
Acquire (also called wait or down) the semaphore.
May block if the s
access count is full, waiting for a slot to vacant.
- Parameters
-
s Handle to semaphore timeout Max time to wait for a vacancy, in hundreds of seconds.
- Returns
- BASE_ERR_OK on success (semaphore was acquired)
-
BASE_ERR_TIMEOUT if could no acquire semaphore in
timeout
-
BASE_ERR_INVALID_HANDLE if
s
is invalid
taskSemaphore_t* taskSemaphoreCreate | ( | int | limit | ) |
Create a semaphore with a maximum entry count of limit
.
- Parameters
-
limit Number of threads that may simultaneously acquire the returned semaphore.
- Returns
- Handle to semaphore or
NULL
on error.
void taskSemaphoreDestroy | ( | taskSemaphore_t * | s | ) |
Destroy an existing semaphore.
The effects of destroying a semaphore that is being waited on is undefined and platform-dependent.
- Parameters
-
s Handle to semaphore.
int taskSemaphoreRelease | ( | taskSemaphore_t * | s | ) |
Release (also called signal or up) a previously acquired semaphore.
- Parameters
-
s Handle to semaphore.
- Returns
- BASE_ERR_OK on success
-
BASE_ERR_INVALID_HANDLE if
s
is invalid
void taskSleep | ( | uint32_t | timeout | ) |
Sleep the current running thread.
This will cause the current thread to stop executing for timeout
hundreds of second, but other threads will keep running.
- Parameters
-
timeout Time to sleep the current thread, in hundreds of seconds.
taskThread_t* taskThreadCreate | ( | taskThreadFunction_t | fn, |
void * | param, | ||
uint32_t | stackSize | ||
) |
Create a new execution thread with entry point fn
.
- Parameters
-
fn Entry point for thread execution. param Parameter to be passed to fn
.stackSize A hint on the minimum stack size required for this thread. Depending on the platform, different limitations on minimum and maximum stack size will apply. Use 0 for a default value that is at least 2KiB in all supported platforms.
- Returns
- Handle to newly created thread, or
NULL
on error.
taskThread_t* taskThreadCurrent | ( | void | ) |
Return the handle of the current running thread.
- Returns
- Handle to current thread.
Generated on Mon Mar 27 2017 15:42:53 for LAR Library by
