.\" Copyright (c) 2007-2022 Julien Nadeau Carriere .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, .\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES .\" (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR .\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, .\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 21, 2022 .Dt AG_THREADS 3 .Os Agar 1.7 .Sh NAME .Nm AG_Threads .Nd agar threads support .Sh SYNOPSIS .Bd -literal #include .Ed .Sh DESCRIPTION On platforms with threads support, Agar can be compiled with support for multithreading. In a threaded build, Agar API calls can be considered .Em free-threaded (safe to use from different threads without need for application-level synchronization) unless documented otherwise. .Pp Even though calls are free-threaded, application-level synchronization (calls to .Xr AG_ObjectLock 3 ) may still be needed in some cases. See .Sx EXAMPLES for some examples of thread-unsafe vs. thread-safe usages. .Sh CONVENTIONS Agar function calls are free-threaded unless mentioned otherwise. .Pp The .Xr AG_Object 3 system provides a per-object recursive mutex which is implicitely acquired before invoking object methods or processing events. .Sh THREADS INTERFACE 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 .Xr AG_Intro 3 ) . .Sh MUTEXES .\" MANLINK(AG_Mutex) Mutexes (MUTual EXclusion devices) are commonly used to protect shared data structure against concurrent modifications. .Pp .nr nS 1 .Ft "void" .Fn AG_MutexInit "AG_Mutex *mutex" .Pp .Ft "int" .Fn AG_MutexTryInit "AG_Mutex *mutex" .Pp .Ft "void" .Fn AG_MutexInitRecursive "AG_Mutex *mutex" .Pp .Ft "int" .Fn AG_MutexTryInitRecursive "AG_Mutex *mutex" .Pp .Ft "void" .Fn AG_MutexDestroy "AG_Mutex *mutex" .Pp .Ft "void" .Fn AG_MutexLock "AG_Mutex *mutex" .Pp .Ft "int" .Fn AG_MutexTryLock "AG_Mutex *mutex" .Pp .Ft "void" .Fn AG_MutexUnlock "AG_Mutex *mutex" .Pp .nr nS 0 The .Fn AG_MutexInit function initializes a mutex structure. .Fn AG_MutexInitRecursive initializes a recursive mutex (a mutex with a reference count), which allows nested .Fn AG_MutexLock calls. .Pp .Fn AG_MutexDestroy frees all resources allocated for a mutex. .Pp .Fn AG_MutexLock and .Fn AG_MutexUnlock respectively acquire and release a mutex. .Pp .Fn 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 .Xr AG_GetError 3 should not be used). .Sh CONDITION VARIABLES .\" MANLINK(AG_Cond) .nr nS 1 .Ft "void" .Fn AG_CondInit "AG_Cond *cv" .Pp .Ft "int" .Fn AG_CondTryInit "AG_Cond *cv" .Pp .Ft "void" .Fn AG_CondDestroy "AG_Cond *cv" .Pp .Ft "void" .Fn AG_CondBroadcast "AG_Cond *cv" .Pp .Ft "void" .Fn AG_CondSignal "AG_Cond *cv" .Pp .Ft "int" .Fn AG_CondWait "AG_Cond *cv" "AG_Mutex *m" .Pp .Ft "int" .Fn AG_CondTimedWait "AG_Cond *cv" "AG_Mutex *m" "const struct timespec *t" .Pp .nr nS 0 .Fn AG_CondInit initializes a condition variable structure. .Pp .Fn AG_CondDestroy releases resources allocated for a condition variable. .Pp .Fn AG_CondBroadcast unblock all threads which are currently blocked waiting on .Fa cv . .Fn AG_CondSignal unblocks at least one thread currently blocked waiting on .Fa cv . .Pp .Fn AG_CondWait blocks the calling thread until .Fa cv is signaled. The .Fn AG_CondTimedWait variant will not block for more than the specified amount of time. .Pp All of these functions will raise a fatal condition if an error is encountered. .Sh THREADS .\" MANLINK(AG_Thread) .nr nS 1 .Ft void .Fn AG_ThreadCreate "AG_Thread *th" "void *(*fn)(void *arg)" "void *arg" .Pp .Ft int .Fn AG_ThreadTryCreate "AG_Thread *th" "void *(*fn)(void *arg)" "void *arg" .Pp .Ft void .Fn AG_ThreadCancel "AG_Thread th" .Pp .Ft int .Fn AG_ThreadTryCancel "AG_Thread th" .Pp .Ft void .Fn AG_ThreadJoin "AG_Thread th" "void **exitVal" .Pp .Ft int .Fn AG_ThreadTryJoin "AG_Thread th" "void **exitVal" .Pp .Ft void .Fn AG_ThreadExit "void *exitVal" .Pp .Ft void .Fn AG_ThreadKill "AG_Thread th" "int signal" .Pp .Ft AG_Thread .Fn AG_ThreadSelf "void" .Pp .Ft int .Fn AG_ThreadEqual "AG_Thread a" "AG_Thread b" .Pp .nr nS 0 .Fn AG_ThreadCreate creates a new thread executing .Fa fn . The optional argument .Fa arg is passed to .Fa fn . .Pp The .Fn AG_ThreadCancel routine requests that the specified thread be cancelled. If the given thread is invalid, a fatal error is raised. .Pp The .Fn AG_ThreadJoin function suspends the execution of the current thread until .Fa th terminates. When it does, the value passed to .Fn AG_ThreadExit is made available in .Fa exitVal . .Pp .Fn AG_ThreadExit terminates the current thread. .Fa exitVal is an optional user pointer. .Pp .Fn AG_ThreadKill sends a signal to the specified thread. .Pp .Fn AG_ThreadSelf returns the identifier of the current (caller's) thread. .Fn AG_ThreadEqual returns 1 if the identifiers .Fa a and .Fa b both refer to the same thread, or 0 if they differ. .Sh THREAD-SPECIFIC VARIABLES .nr nS 1 .\" MANLINK(AG_ThreadKey) .Ft void .Fn AG_ThreadKeyCreate "AG_ThreadKey *key" "void (*destructor)(void *)" .Pp .Ft int .Fn AG_ThreadKeyTryCreate "AG_ThreadKey *key" "void (*destructor)(void *)" .Pp .Ft void .Fn AG_ThreadKeyDelete "AG_ThreadKey key" .Pp .Ft int .Fn AG_ThreadKeyTryDelete "AG_ThreadKey key" .Pp .Ft "void *" .Fn AG_ThreadKeyGet "AG_ThreadKey key" .Pp .Ft "void" .Fn AG_ThreadKeySet "AG_ThreadKey key" "const void *value" .Pp .Ft "int" .Fn AG_ThreadKeyTrySet "AG_ThreadKey key" "const void *value" .Pp .nr nS 0 .Fn 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 .Fn AG_ThreadKeySet , and which defaults to NULL) will persist only for the life of the thread. If an optional .Fa destructor is given, that function will be called (with the thread-specific value as its argument), when the thread exists. .Pp The .Fn AG_ThreadKeyDelete function releases resources allocated for a key. .Pp .Fn AG_ThreadKeyGet returns the thread-specific value associated with .Fa key . .Pp .Fn AG_ThreadKeySet sets a thread-specific value with .Fa key . .Sh EXAMPLES The following code uses the return value of a VFS lookup in a manner which is .Em not thread-safe. A race condition exists between the .Fn AG_ObjectFind call and the following access: .Bd -literal -offset indent .\" SYNTAX(c) AG_Object *o; o = AG_ObjectFind(root, "/Foo"); if (o != NULL) { /* ... */ } /* UNSAFE access */ .Ed .Pp The following code accesses the returned object safely by acquiring the mutex of the VFS root object (which protects the entire VFS linkage): .Bd -literal -offset indent .\" SYNTAX(c) AG_Object *o; AG_ObjectLock(root); o = AG_ObjectFind(root, "/Foo"); if (o != NULL) { /* ... */ } /* Safe access */ AG_ObjectUnlock(root); .Ed .\" .\" TODO more examples .\" .Sh SEE ALSO .Xr AG_Intro 3 , .Xr AG_Object 3 .Sh HISTORY The .Nm interface first appeared in Agar 1.0