libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
|
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "assert.h"
#include "helpers.h"
#include "list.h"
#include "time.h"
#include "safe_alloc.h"
Go to the source code of this file.
Data Structures | |
struct | lacf_thread_info_t |
struct | mutex_t |
struct | rwmutex_t |
struct | rwlock_waiter_t |
Macros | |
#define | thread_create(thrp, start_proc, arg) |
#define | atomic32_t volatile int32_t |
#define | atomic_inc_32(x) __sync_add_and_fetch((x), 1) |
#define | atomic_dec_32(x) __sync_add_and_fetch((x), -1) |
#define | atomic_add_32(x, y) __sync_add_and_fetch((x), (y)) |
#define | atomic_set_32(x, y) __atomic_store_n((x), (y), __ATOMIC_RELAXED) |
#define | atomic64_t volatile int64_t |
#define | atomic_inc_64(x) __sync_add_and_fetch((x), 1) |
#define | atomic_dec_64(x) __sync_add_and_fetch((x), -1) |
#define | atomic_add_64(x, y) __sync_add_and_fetch((x), (y)) |
#define | atomic_set_64(x, y) __atomic_store_n((x), (y), __ATOMIC_RELAXED) |
#define | curthread_id GetCurrentThreadId() |
#define | curthread GetCurrentThread() |
#define | VERIFY_MUTEX_HELD(mtx) (void)1 |
#define | VERIFY_MUTEX_NOT_HELD(mtx) (void)1 |
#define | THREAD_PRIO_IDLE THREAD_PRIORITY_IDLE |
#define | THREAD_PRIO_VERY_LOW THREAD_PRIORITY_LOWEST |
#define | THREAD_PRIO_LOW THREAD_PRIORITY_BELOW_NORMAL |
#define | THREAD_PRIO_NORM THREAD_PRIORITY_NORMAL |
#define | THREAD_PRIO_HIGH THREAD_PRIORITY_ABOVE_NORMAL |
#define | THREAD_PRIO_VERY_HIGH THREAD_PRIORITY_HIGHEST |
#define | THREAD_PRIO_RT THREAD_PRIORITY_TIME_CRITICAL |
#define | ASSERT_MUTEX_HELD(mtx) |
#define | ASSERT_MUTEX_NOT_HELD(mtx) |
Typedefs | |
typedef HANDLE | thread_t |
typedef DWORD | thread_id_t |
typedef CONDITION_VARIABLE | condvar_t |
Functions | |
static bool_t | thread_equal (thread_id_t tid1, thread_id_t tid2) |
static void | mutex_init (mutex_t *mtx) |
static void | mutex_destroy (mutex_t *mtx) |
static void | mutex_enter (mutex_t *mtx) |
static void | mutex_exit (mutex_t *mtx) |
static void | _lacf_thread_list_init (void) |
static void | _lacf_thread_list_fini (void) |
static void | _lacf_thread_list_add (lacf_thread_info_t *ti) |
static void | _lacf_thread_list_remove (lacf_thread_info_t *ti) |
static void | lacf_threads_fini (void) |
DWORD | lacf_thread_start_routine (void *arg) |
static bool_t | lacf_thread_create (thread_t *thrp, void(*proc)(void *), void *arg, const char *filename, int linenum) |
static void | thread_join (thread_t *thrp) |
static void | thread_set_name (const char *name) |
static void | cv_wait (condvar_t *cv, mutex_t *mtx) |
static int | cv_timedwait (condvar_t *cv, mutex_t *mtx, uint64_t limit) |
static void | cv_init (condvar_t *cv) |
static void | cv_destroy (condvar_t *cv) |
static void | cv_signal (condvar_t *cv) |
static void | cv_broadcast (condvar_t *cv) |
static void | thread_set_prio (thread_t thr, int prio) |
void | lacf_mask_sigpipe (void) |
static void | rwmutex_init (rwmutex_t *rw) |
static void | rwmutex_destroy (rwmutex_t *rw) |
static bool_t | rwmutex_held_write (rwmutex_t *rw) |
static bool_t | rwmutex_can_enter_impl (const rwmutex_t *rw, const rwlock_waiter_t *wt_self) |
static void | rwmutex_enter (rwmutex_t *rw, bool_t write) |
static void | rwmutex_exit (rwmutex_t *rw) |
static void | rwmutex_upgrade (rwmutex_t *rw) |
Variables | |
bool_t | lacf_thread_list_inited |
mutex_t | lacf_thread_list_lock |
list_t | lacf_thread_list |
Basic portable multi-threading API. We have 3 kinds of objects and associated manipulation functions here:
The actual implementation is large contained in this header and mostly works as a set of pre-processor switching on top of the OS-specific threading API (pthreads on Unix/Linux, winthreads on Win32).
Example of how to create a thread:
Example of how to wait for a thread to exit:
Example of how to use a mutex_t:
Example of how to use a condvar_t:
You can also performed a "timed" wait on a CV using cv_timedwait(). The function will exit when either the condition has been signalled, or the timer has expired. The return value of the function indicates whether the condition was signalled before the timer expired (returns zero), or if the wait timed out (returns ETIMEDOUT) or another error occurred (returns -1).
The atomic32_t and atomic64_t types describe signed 32- and 64-bit integers respectively, which are suitable for being passed to the relevant atomic_*
functions. Example of how use an atomic function:
Definition in file thread.h.
#define ASSERT_MUTEX_HELD | ( | mtx | ) |
If compiling with the DEBUG
macro set, expands to VERIFY_MUTEX_HELD(). Otherwise expands to nothing.
#define ASSERT_MUTEX_NOT_HELD | ( | mtx | ) |
If compiling with the DEBUG
macro set, expands to VERIFY_MUTEX_NOT_HELD(). Otherwise expands to nothing.
#define atomic32_t volatile int32_t |
Atomic signed 32-bit integer. You must use this type with the atomic_*_32()
family of functions, such as atomic_inc_32(), atomic_dec_32(), atomic_set_32() and atomic_add_32().
#define atomic64_t volatile int64_t |
Atomic signed 64-bit integer. You must use this type with the atomic_*_64()
family of functions, such as atomic_inc_64(), atomic_dec_64(), atomic_set_64() and atomic_add_64().
#define atomic_add_32 | ( | x, | |
y | |||
) | __sync_add_and_fetch((x), (y)) |
#define atomic_add_64 | ( | x, | |
y | |||
) | __sync_add_and_fetch((x), (y)) |
#define atomic_dec_32 | ( | x | ) | __sync_add_and_fetch((x), -1) |
#define atomic_dec_64 | ( | x | ) | __sync_add_and_fetch((x), -1) |
#define atomic_inc_32 | ( | x | ) | __sync_add_and_fetch((x), 1) |
#define atomic_inc_64 | ( | x | ) | __sync_add_and_fetch((x), 1) |
#define atomic_set_32 | ( | x, | |
y | |||
) | __atomic_store_n((x), (y), __ATOMIC_RELAXED) |
#define atomic_set_64 | ( | x, | |
y | |||
) | __atomic_store_n((x), (y), __ATOMIC_RELAXED) |
#define curthread GetCurrentThread() |
#define curthread_id GetCurrentThreadId() |
#define thread_create | ( | thrp, | |
start_proc, | |||
arg | |||
) |
Creates a new thread.
thrp | Return pointer to a thread_t, which will be filled with the thread handle if the thread was started successfully. |
proc | Start function, which will be called by the new thread. When this function returns, the thread terminates. |
arg | Optional pointer, which will be passed to the start function in its first argument. |
B_TRUE
if starting the thread was successful, B_FALSE
otherwise. You MUST check the return value and not just assume that starting the thread is always succesful. #define THREAD_PRIO_HIGH THREAD_PRIORITY_ABOVE_NORMAL |
Higher than normal thread scheduling priority.
#define THREAD_PRIO_IDLE THREAD_PRIORITY_IDLE |
Minimum thread scheduling priority - only use for threads which can can accept very long periods of not getting CPU time if the CPU is busy.
#define THREAD_PRIO_LOW THREAD_PRIORITY_BELOW_NORMAL |
Reduced thread scheduling priority, below normal priority.
#define THREAD_PRIO_NORM THREAD_PRIORITY_NORMAL |
Normal thread scheduling priority. This is the default for newly created threads.
#define THREAD_PRIO_RT THREAD_PRIORITY_TIME_CRITICAL |
Highest possible thread scheduling priority. Be very careful when using this, as if your thread does a lot of work at this priority, it can starve other threads of CPU time. Use sparingly and only for threads with a known bounded execution time between yields.
#define THREAD_PRIO_VERY_HIGH THREAD_PRIORITY_HIGHEST |
Very high thread scheduling priority.
#define THREAD_PRIO_VERY_LOW THREAD_PRIORITY_LOWEST |
Very low thread scheduling priority.
#define VERIFY_MUTEX_HELD | ( | mtx | ) | (void)1 |
#define VERIFY_MUTEX_NOT_HELD | ( | mtx | ) | (void)1 |
The opposite of VERIFY_MUTEX_HELD(). Due to varying platform support for checking mutex ownership, this cannot be done using a simple stacking such as VERIFY(!MUTEX_HELD(mtx))
, as the inner macro wouldn't know what the desired output was and would thus be error-prone when used.
typedef CONDITION_VARIABLE condvar_t |
A condition variable is an object which can be waited on by any number of threads, and signalled by another thread, to notify the waiting threads that a certain condition has been met and/or that the waiting threads' attention is required. A condition variable is always used in conjuction with a mutex. The waiting thread(s) first acquire a mutex to protect a critical section of code. They then wait on a condition variable, which also atomatically relinquishes the mutex, allowing another thread to come acquire the lock and signal the condition. Once signalled, the waiting thread wakes up and atomically acquires the lock (once the signalling thread has relinquished it).
Use cv_init() to initialize a condition variable. Once initialized, the object must be destroyed using cv_destroy(). Waiting on the condition variable can be accomplished using either cv_wait() or cv_timedwait(). Signalling the condition is performed using either cv_signal() or cv_broadcast().
Example of how to use a condvar_t:
typedef DWORD thread_id_t |
Thread ID object. This gets returned by macros such as curthread_id. Certain operations require the thread ID, instead of the handle, so this type encapsulates that detail on platforms where there is a distinction between thread handles and IDs (Windows).
typedef HANDLE thread_t |
Thread handle object. This gets initialized with a thread handle by a call to thread_create() and is consumed by a variety of other thread-related operations (such as thread_destroy()).
|
static |
|
static |
|
static |
|
static |
|
static |
Signals a condition variable to ALL threads waiting on that condition variable (either using cv_wait() or cv_timedwait()). The threads will wake up (in an unpredictable order), and re-acquire the mutex associated with their cv_wait() or cv_timedwait() call.
|
static |
|
static |
Initializes a condition variable. Once initialized, a condition variable MUST be destroyed using cv_destroy().
|
static |
Signals a condition variable to a single waiting thread. If there are multiple threads currently blocked waiting on the condition variable (either using cv_wait() or cv_timedwait()), only ONE of the threads is signalled (which one is unpredictable).
Blocks the calling thread until the condition variable is signalled, or until a timeout limit is reached.
cv | The condition variable to wait on. |
mtx | A mutex_t which MUST be currently held by the calling thread. The calling thread atomically relinquishes this mutex and starts monitoring the condition variable. Once the condition is signalled, the thread atomically wakes up and acquires the mutex again, so that when cv_wait() returns, the lock is acquired again by only the calling thread. |
limit | A deadline in microseconds, by which time the thread will wake up, regardless if the condition has been signalled or not. The limit must be calculated from the time value as returned by the microclock() function. |
Blocks the calling thread until the condition variable is signalled.
cv | The condition variable to wait on. |
mtx | A mutex_t which MUST be currently held by the calling thread. The calling thread atomically relinquishes this mutex and starts monitoring the condition variable. Once the condition is signalled, the thread atomically wakes up and acquires the mutex again, so that when cv_wait() returns, the lock is acquired again by only the calling thread. |
void lacf_mask_sigpipe | ( | void | ) |
|
static |
Implementation of thread_create(). Do not call directly, use the thread_create() macro.
DWORD lacf_thread_start_routine | ( | void * | arg | ) |
|
static |
Checks to see if all threads that were created using thread_create() have been properly disposed of. If not, this trips an assertion failure and lists all threads (including filenames and line numbers where they have been spawned) that weren't properly stopped. You should call this just as your plugin is exiting, to check for leaked threads.
|
static |
Destroys mutex_t object previously initialized by a call to mutex_init().
|
static |
Acquires a mutex, which has previously been initialized using mutex_init(). If the mutex cannot be acquired exclusively by the calling thread, the thread blocks until it can be acquired. Once acquired, the mutex MUST be relinquished by a call to mutex_exit().
|
static |
Relinquishes a mutex previously acquired by a call to mutex_enter().
|
static |
Initializes a new mutex_t object. The mutex MUST be destroyed using mutex_destroy() to avoid leaking memory.
|
static |
|
static |
Destroys an rwmutex_t that was previously initialized rwmutex_init().
|
static |
Acquires an rwmutex_t in either read or write mode. The lock can be simultaneously held in read mode by any number of threads. However, in write mode, the lock can only be held by a single thread. If the lock cannot be acquired immediately, the calling thread is blocked until successful.
rwmutex_t implements deterministic locking order. If the lock is currently held by one or more readers and another thread attempts to acquire the lock in write (exclusive) mode, the calling thread is blocked until all existing read locks are relinquished. Furthermore, any newly arriving locking attempts will queue up "behind" any preceding attempts and block. The queue of pending locks is then cleared in order of arrival. Writers can only enter one by one, while a "batched up" group of readers can enter simultaneously. This prevents lock starvation of writers in the presence of a large number of readers and vice versa.
rw | The mutex to acquire. |
write | Sets whether the lock should be acquired in write (write=B_TRUE ) or read (write=B_FALSE ) mode. |
|
static |
|
static |
|
static |
Initializes a new rwmutex_t. The mutex must be destroyed using rwmutex_destroy().
|
static |
|
static |
|
static |
Waits for a thread to exit. After this function returns, the passed thread has exited and its resources can be safely disposed of.
thrp | A pointer to a thread_t identifying the thread for which to wait. |
|
static |
Sets the name of the calling thread. This is useful for debugging purposes, since the thread name is easily visible in a debugger or process analysis tool.
|
static |
Sets the scheduling priority of a thread. The exact implementation is platform dependant:
pthread_setschedparam()
with a policy of SCHED_OTHER
.SetThreadPriority()
.prio | Must be one of the THREAD_PRIO_* constants. |