AG_Threads
—
agar threads support
On all platforms with threads support, Agar can be compiled with support for
multithreading. Agar API calls, unless otherwise documented, then become
free-threaded (safe to use from different threads without
need for application-level synchronization).
The Agar API documentation follows the convention that all functions are
free-threaded, unless mentioned otherwise.
Under some circumstances, application-level synchronization is
required. The
AG_Object(3)
simplifies this task by providing a per-object lock (which is implicitely
acquired in some contexts, such as event handler execution). For instance,
the following code accesses a VFS in an unsafe manner:
AG_Object *myObject;
myObject = AG_ObjectFind(myRoot, "/Foo");
if (myObject != NULL) { ... } /* UNSAFE */
The following code should be used instead:
AG_Object *myObject;
AG_ObjectLock(myRoot);
myObject = AG_ObjectFind(myRoot, "/Foo");
if (myObject != NULL) { ... }
AG_ObjectUnlock(myRoot);
When compiled with threads support, Agar provides a portable, minimal interface
to the operating system's native threads interface. These functions follow
Agar's standard error-handling style (see
AG_Intro(3)).
Mutexes (MUTual EXclusion devices) are commonly used to protect shared data
structure against concurrent modifications.
void
AG_MutexInit
(AG_Mutex
*mutex);
int
AG_MutexTryInit
(AG_Mutex
*mutex);
void
AG_MutexInitRecursive
(AG_Mutex
*mutex);
int
AG_MutexTryInitRecursive
(AG_Mutex
*mutex);
void
AG_MutexDestroy
(AG_Mutex
*mutex);
void
AG_MutexLock
(AG_Mutex
*mutex);
int
AG_MutexTryLock
(AG_Mutex
*mutex);
void
AG_MutexUnlock
(AG_Mutex
*mutex);
The AG_MutexInit
() function initializes a
mutex structure. AG_MutexInitRecursive
() initializes
a recursive mutex (a mutex with a reference count), which allows nested
AG_MutexLock
() calls.
AG_MutexDestroy
() frees all resources
allocated for a mutex.
AG_MutexLock
() and
AG_MutexUnlock
() respectively acquire and release a
mutex.
AG_MutexTryLock
() tries to acquire a mutex
without blocking and immediately returns 0 on success. On failure, the
function returns -1, but does not set any error message (so
AG_GetError(3)
should not be used).
void
AG_CondInit
(AG_Cond
*cv);
int
AG_CondTryInit
(AG_Cond
*cv);
void
AG_CondDestroy
(AG_Cond
*cv);
void
AG_CondBroadcast
(AG_Cond
*cv);
void
AG_CondSignal
(AG_Cond
*cv);
int
AG_CondWait
(AG_Cond
*cv, AG_Mutex
*m);
int
AG_CondTimedWait
(AG_Cond
*cv, AG_Mutex *m,
const struct timespec
*t);
AG_CondInit
() initializes a condition
variable structure.
AG_CondDestroy
() releases resources
allocated for a condition variable.
AG_CondBroadcast
() unblock all threads
which are currently blocked waiting on cv.
AG_CondSignal
() unblocks at least one thread
currently blocked waiting on cv.
AG_CondWait
() blocks the calling thread
until cv is signaled. The
AG_CondTimedWait
() variant will not block for more
than the specified amount of time.
All of these functions will raise a fatal condition if an error is
encountered.
void
AG_ThreadCreate
(AG_Thread
*th, void *(*fn)(void
*arg), void *arg);
int
AG_ThreadTryCreate
(AG_Thread
*th, void *(*fn)(void
*arg), void
*arg);
void
AG_ThreadCancel
(AG_Thread
th);
int
AG_ThreadTryCancel
(AG_Thread
th);
void
AG_ThreadJoin
(AG_Thread
th, void
**exitVal);
int
AG_ThreadTryJoin
(AG_Thread
th, void
**exitVal);
void
AG_ThreadExit
(void
*exitVal);
void
AG_ThreadKill
(AG_Thread
th, int
signal);
AG_Thread
AG_ThreadSelf
(void);
int
AG_ThreadEqual
(AG_Thread
a, AG_Thread
b);
AG_ThreadCreate
() creates a new thread
executing fn. The optional argument
arg is passed to fn.
The AG_ThreadCancel
() routine requests
that the specified thread be cancelled. If the given thread is invalid, a
fatal error is raised.
The AG_ThreadJoin
() function suspends the
execution of the current thread until th terminates.
When it does, the value passed to AG_ThreadExit
() is
made available in exitVal.
AG_ThreadExit
() terminates the current
thread. exitVal is an optional user pointer.
AG_ThreadKill
() sends a signal to the
specified thread.
AG_ThreadSelf
() returns the identifier of
the current (caller's) thread. AG_ThreadEqual
()
returns 1 if the identifiers a and
b both refer to the same thread, or 0 if they
differ.
void
AG_ThreadKeyCreate
(AG_ThreadKey
*key, void
(*destructor)(void *));
int
AG_ThreadKeyTryCreate
(AG_ThreadKey
*key, void
(*destructor)(void *));
void
AG_ThreadKeyDelete
(AG_ThreadKey
key);
int
AG_ThreadKeyTryDelete
(AG_ThreadKey
key);
void *
AG_ThreadKeyGet
(AG_ThreadKey
key);
void
AG_ThreadKeySet
(AG_ThreadKey
key, const void
*value);
int
AG_ThreadKeyTrySet
(AG_ThreadKey
key, const void
*value);
AG_ThreadKeyCreate
() initializes a key
(i.e., a handle) to a thread-specific value. The handle itself is accessible
to all threads. The thread-specific value (i.e., the value specified by
AG_ThreadKeySet
(), and which defaults to NULL) will
persist only for the life of the thread. If an optional
destructor is given, that function will be called
(with the thread-specific value as its argument), when the thread
exists.
The AG_ThreadKeyDelete
() function releases
resources allocated for a key.
AG_ThreadKeyGet
() returns the
thread-specific value associated with key.
AG_ThreadKeySet
() sets a thread-specific
value with key.
The AG_Threads
interface first appeared in Agar 1.0