DYT/Tool/OpenSceneGraph-3.6.5/include/jasper/jas_thread.h

796 lines
20 KiB
C
Raw Normal View History

2024-12-24 23:49:36 +00:00
/*
* Copyright (c) 1999-2000 Image Power, Inc. and the University of
* British Columbia.
* Copyright (c) 2001-2003 Michael David Adams.
* All rights reserved.
*/
/* __START_OF_JASPER_LICENSE__
*
* JasPer License Version 2.0
*
* Copyright (c) 2001-2006 Michael David Adams
* Copyright (c) 1999-2000 Image Power, Inc.
* Copyright (c) 1999-2000 The University of British Columbia
*
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person (the
* "User") obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* 1. The above copyright notices and this permission notice (which
* includes the disclaimer below) shall be included in all copies or
* substantial portions of the Software.
*
* 2. The name of a copyright holder shall not be used to endorse or
* promote products derived from the Software without specific prior
* written permission.
*
* THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS
* LICENSE. NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
* THIS DISCLAIMER. THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
* "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
* INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. NO ASSURANCES ARE
* PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE
* THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY.
* EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS
* BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL
* PROPERTY RIGHTS OR OTHERWISE. AS A CONDITION TO EXERCISING THE RIGHTS
* GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE
* ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY. THE SOFTWARE
* IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL
* SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES,
* AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL
* SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH
* THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH,
* PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH
* RISK ACTIVITIES"). THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY
* EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
*
* __END_OF_JASPER_LICENSE__
*/
/*!
* @file jas_thread.h
* @brief Threads
*/
#ifndef JAS_THREAD_H
#define JAS_THREAD_H
/******************************************************************************\
* Includes
\******************************************************************************/
/* The configuration header file should be included first. */
#include <jasper/jas_config.h>
#include "jasper/jas_compiler.h"
#include "jasper/jas_types.h"
#if defined(JAS_THREADS)
#include <stdlib.h>
#include <assert.h>
#if defined(JAS_THREADS_C11)
#include <threads.h>
#include <stdatomic.h>
#elif defined(JAS_THREADS_PTHREAD)
#include <pthread.h>
#include <sched.h>
#elif defined(JAS_THREADS_WIN32)
#include <process.h>
#include <windows.h>
#include <processthreadsapi.h>
#endif
#endif
/******************************************************************************\
\******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
/*!
* @cond DOXYGEN_EXCLUDE
* @addtogroup module_threads
* @{
*/
#if defined(JAS_THREADS)
#if defined(JAS_FOR_INTERNAL_USE_ONLY) || defined(JAS_FOR_JASPER_APP_USE_ONLY)
/******************************************************************************\
* Types
\******************************************************************************/
#if defined(JAS_THREADS_C11)
# define JAS_THREADS_IMPL "C11"
# define JAS_USE_SPINLOCK
#elif defined(JAS_THREADS_PTHREAD)
# define JAS_THREADS_IMPL "PTHREAD"
# undef JAS_USE_SPINLOCK
#elif defined(JAS_THREADS_WIN32)
# define JAS_THREADS_IMPL "WIN32"
# define JAS_USE_SPINLOCK
#endif
/**************************************\
* Spinlock
\**************************************/
/*! Spinlock type. */
#if defined(JAS_THREADS_C11)
# define JAS_USE_SPINLOCK
typedef struct {
atomic_flag flag;
} jas_spinlock_t;
#elif defined(JAS_THREADS_PTHREAD)
/* There is no pthread_spinlock_t type on MacOS. */
# undef JAS_USE_SPINLOCK
#elif defined(JAS_THREADS_WIN32)
# define JAS_USE_SPINLOCK
typedef struct {
LONG flag;
} jas_spinlock_t;
#endif
/*! Spinlock initializer. */
#if defined(JAS_THREADS_C11)
#define JAS_SPINLOCK_INITIALIZER {ATOMIC_FLAG_INIT}
#elif defined(JAS_THREADS_PTHREAD)
/* There is no pthread_spinlock_t type on MacOS. */
#elif defined(JAS_THREADS_WIN32)
#define JAS_SPINLOCK_INITIALIZER {0}
#endif
/**************************************\
* Basic Mutex
\**************************************/
/*! Mutex type. */
#if defined(JAS_THREADS_C11)
typedef mtx_t jas_basicmutex_t;
#elif defined(JAS_THREADS_PTHREAD)
typedef pthread_mutex_t jas_basicmutex_t;
#elif defined(JAS_THREADS_WIN32)
typedef CRITICAL_SECTION jas_basicmutex_t;
#endif
/*! Mutex initializer. */
#if defined(JAS_THREADS_C11)
#undef JAS_BASICMUTEX_INITIALIZER
#elif defined(JAS_THREADS_PTHREAD)
#define JAS_BASICMUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
#elif defined(JAS_THREADS_WIN32)
#define JAS_BASICMUTEX_INITIALIZER
#endif
/**************************************\
* Mutex (Allowing Static Initialization)
\**************************************/
#if defined(JAS_USE_SPINLOCK)
# define jas_mutex_t jas_spinlock_t
# define JAS_MUTEX_INITIALIZER JAS_SPINLOCK_INITIALIZER
# define jas_mutex_init jas_spinlock_init
# define jas_mutex_cleanup jas_spinlock_cleanup
# define jas_mutex_lock jas_spinlock_lock
# define jas_mutex_unlock jas_spinlock_unlock
#else
# define jas_mutex_t jas_basicmutex_t
# define JAS_MUTEX_INITIALIZER JAS_BASICMUTEX_INITIALIZER
# define jas_mutex_init jas_basicmutex_init
# define jas_mutex_cleanup jas_basicmutex_cleanup
# define jas_mutex_lock jas_basicmutex_lock
# define jas_mutex_unlock jas_basicmutex_unlock
#endif
/**************************************\
* Once Flag
\**************************************/
/*! Once-flag type. */
#if defined(JAS_THREADS_C11)
typedef once_flag jas_once_flag_t;
#elif defined(JAS_THREADS_PTHREAD)
typedef pthread_once_t jas_once_flag_t;
#elif defined(JAS_THREADS_WIN32)
typedef struct {
volatile LONG status;
} jas_once_flag_t;
#endif
/*! Once-flag initializer. */
#if defined(JAS_THREADS_C11)
# define JAS_ONCE_FLAG_INIT ONCE_FLAG_INIT
#elif defined(JAS_THREADS_PTHREAD)
# define JAS_ONCE_FLAG_INIT PTHREAD_ONCE_INIT
#elif defined(JAS_THREADS_WIN32)
# define JAS_ONCE_FLAG_INIT {0}
#endif
/**************************************\
* Threads
\**************************************/
#if defined(JAS_FOR_INTERNAL_USE_ONLY) || defined(JAS_FOR_JASPER_APP_USE_ONLY)
/*! Thread ID type. */
#if defined(JAS_THREADS_C11)
typedef thrd_t jas_thread_id_t;
#elif defined(JAS_THREADS_PTHREAD)
typedef pthread_t jas_thread_id_t;
#elif defined(JAS_THREADS_WIN32)
typedef HANDLE jas_thread_id_t;
#endif
/*! Thread type. */
#if defined(JAS_THREADS_C11)
typedef thrd_t jas_thread_t;
#elif defined(JAS_THREADS_PTHREAD)
typedef struct {
pthread_t id;
int (*func)(void *);
void *arg;
int result;
} jas_thread_t;
#elif defined(JAS_THREADS_WIN32)
typedef struct {
jas_thread_id_t id;
int (*func)(void *);
void *arg;
} jas_thread_t;
#endif
static inline void jas_thread_yield(void);
#endif
/**************************************\
* Thread-Specific Storage (TSS)
\**************************************/
/*! Thread-specific storage type. */
#if defined(JAS_THREADS_C11)
typedef tss_t jas_tss_t;
#elif defined(JAS_THREADS_PTHREAD)
typedef pthread_key_t jas_tss_t;
#elif defined(JAS_THREADS_WIN32)
typedef DWORD jas_tss_t;
#endif
/******************************************************************************\
* Spinlock
\******************************************************************************/
#if defined(JAS_USE_SPINLOCK)
/*!
@brief
Initialize a spinlock.
@param mtx
A pointer to the spinlock to be initialized.
@returns
If the spinlock is successfully initialized, zero is returned.
Otherwise, a nonzero value is returned.
*/
static inline int jas_spinlock_init(jas_spinlock_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
atomic_flag_clear(&mtx->flag);
return 0;
#elif defined(JAS_THREADS_PTHREAD)
JAS_UNUSED(mtx);
abort();
return -1;
#elif defined(JAS_THREADS_WIN32)
InterlockedExchange(&mtx->flag, 0);
return 0;
#endif
}
/*!
@brief
Clean up a spinlock mutex.
@param mtx
A pointer to the spinlock to be cleaned up.
@returns
If the spinlock is successfully cleaned up, zero is returned.
Otherwise, a nonzero value is returned.
*/
static inline int jas_spinlock_cleanup(jas_spinlock_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
JAS_UNUSED(mtx);
return 0;
#elif defined(JAS_THREADS_PTHREAD)
JAS_UNUSED(mtx);
abort();
return -1;
#elif defined(JAS_THREADS_WIN32)
JAS_UNUSED(mtx);
return 0;
#endif
}
/*!
@brief
Acquire a spinlock.
@param mtx
A pointer to the spinlock to be acquired.
@returns
If successful, zero is returned.
Otherwise, a nonzero value is returned.
*/
static inline int jas_spinlock_lock(jas_spinlock_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
while (atomic_flag_test_and_set(&mtx->flag)) {}
return 0;
#elif defined(JAS_THREADS_PTHREAD)
JAS_UNUSED(mtx);
abort();
return -1;
#elif defined(JAS_THREADS_WIN32)
while (InterlockedCompareExchange(&mtx->flag, 1, 0)) {}
return 0;
#endif
}
/*!
@brief
Release a spinlock.
@param mtx
A pointer to the spinlock to be released.
@returns
If the operation is successful, zero is returned.
Otherwise, a nonzero value is returned.
*/
static inline int jas_spinlock_unlock(jas_spinlock_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
atomic_flag_clear(&mtx->flag);
return 0;
#elif defined(JAS_THREADS_PTHREAD)
JAS_UNUSED(mtx);
abort();
return -1;
#elif defined(JAS_THREADS_WIN32)
InterlockedExchange(&mtx->flag, 0);
return 0;
#endif
}
#endif
/******************************************************************************\
* Basic Mutex
\******************************************************************************/
/* For internal use only. */
static inline int jas_basicmutex_init(jas_basicmutex_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
return mtx_init(mtx, mtx_plain) == thrd_success ? 0 : -1;
#elif defined(JAS_THREADS_PTHREAD)
return pthread_mutex_init(mtx, 0);
#elif defined(JAS_THREADS_WIN32)
InitializeCriticalSection(mtx);
return 0;
#endif
}
/* For internal use only. */
static inline int jas_basicmutex_cleanup(jas_basicmutex_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
mtx_destroy(mtx);
return 0;
#elif defined(JAS_THREADS_PTHREAD)
return pthread_mutex_destroy(mtx);
#elif defined(JAS_THREADS_WIN32)
DeleteCriticalSection(mtx);
return 0;
#endif
}
/* For internal use only. */
static inline int jas_basicmutex_lock(jas_basicmutex_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
return mtx_lock(mtx);
#elif defined(JAS_THREADS_PTHREAD)
return pthread_mutex_lock(mtx);
#elif defined(JAS_THREADS_WIN32)
EnterCriticalSection(mtx);
return 0;
#endif
}
/* For internal use only. */
static inline int jas_basicmutex_unlock(jas_basicmutex_t *mtx)
{
assert(mtx);
#if defined(JAS_THREADS_C11)
return mtx_unlock(mtx);
#elif defined(JAS_THREADS_PTHREAD)
return pthread_mutex_unlock(mtx);
#elif defined(JAS_THREADS_WIN32)
LeaveCriticalSection(mtx);
return 0;
#endif
}
/******************************************************************************\
* Thread-Specific Storage (TSS)
\******************************************************************************/
/*!
@brief
Create thread-specific storage.
@param tss
A pointer to the TSS object to be initialized.
@param destructor
A function to be called when the TSS is deallocated when the thread exits.
@returns
If the operation is successful, zero is returned.
Otherwise, a nonzero value is returned.
*/
static inline
int jas_tss_create(jas_tss_t *tss, void (*destructor)(void *))
{
assert(tss);
#if defined(JAS_THREADS_C11)
return tss_create(tss, destructor) == thrd_success ? 0 : -1;
#elif defined(JAS_THREADS_PTHREAD)
return pthread_key_create(tss, destructor);
#elif defined(JAS_THREADS_WIN32)
if (destructor) {
return -1;
}
DWORD id;
if ((id = TlsAlloc()) == TLS_OUT_OF_INDEXES) {
return -2;
}
*tss = id;
return 0;
#endif
}
/*!
@brief Delete thread-specific storage.
@param tss
The TSS to be destroyed.
@returns
If the operation is successful, zero is returned.
Otherwise, a nonzero value is returned.
*/
static inline
void jas_tss_delete(jas_tss_t tss)
{
#if defined(JAS_THREADS_C11)
tss_delete(tss);
#elif defined(JAS_THREADS_PTHREAD)
pthread_key_delete(tss);
#elif defined(JAS_THREADS_WIN32)
TlsFree(tss);
#endif
}
/*!
@brief Get the thread-specific storage instance for the calling thread.
@param tss
The TSS to be queried.
@returns
A pointer to the TSS is returned.
*/
static inline
void *jas_tss_get(jas_tss_t tss)
{
#if defined(JAS_THREADS_C11)
return tss_get(tss);
#elif defined(JAS_THREADS_PTHREAD)
return pthread_getspecific(tss);
#elif defined(JAS_THREADS_WIN32)
return TlsGetValue(tss);
#endif
}
/*!
@brief Set the thread-specific storage instance for the calling thread.
@param tss
The TSS object.
@returns
If the operation is successful, zero is returned.
Otherwise, a nonzero value is returned.
*/
static inline
int jas_tss_set(jas_tss_t tss, void *value)
{
#if defined(JAS_THREADS_C11)
return tss_set(tss, value) == thrd_success ? 0 : -1;
#elif defined(JAS_THREADS_PTHREAD)
return pthread_setspecific(tss, value);
#elif defined(JAS_THREADS_WIN32)
return TlsSetValue(tss, value) ? 0 : -1;
#endif
}
/******************************************************************************\
* Once Flag
\******************************************************************************/
/*!
@brief Register to call a function once.
@param flag
A pointer to a flag to track whether the function has been called yet.
@param func
A pointer to the function to be called.
@returns
*/
static inline int jas_call_once(jas_once_flag_t *flag, void (*func)(void))
{
assert(flag);
assert(func);
#if defined(JAS_THREADS_C11)
call_once(flag, func);
return 0;
#elif defined(JAS_THREADS_PTHREAD)
return pthread_once(flag, func);
#elif defined(JAS_THREADS_WIN32)
if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
(func)();
InterlockedExchange(&flag->status, 2);
} else {
while (flag->status == 1) {
/* Perform a busy wait. This is ugly. */
/* Yield processor. */
SwitchToThread();
}
}
return 0;
#endif
}
/******************************************************************************\
* Threads
\******************************************************************************/
#if defined(JAS_FOR_INTERNAL_USE_ONLY) || defined(JAS_FOR_JASPER_APP_USE_ONLY)
#if defined(JAS_THREADS_PTHREAD)
static void *thread_func_wrapper(void *thread_ptr)
{
jas_thread_t *thread = JAS_CAST(jas_thread_t *, thread_ptr);
int result = (thread->func)(thread->arg);
thread->result = result;
return thread;
}
#elif defined(JAS_THREADS_WIN32)
static unsigned __stdcall thread_func_wrapper(void *thread_ptr)
{
jas_thread_t *thread = JAS_CAST(jas_thread_t *, thread_ptr);
int result = (thread->func)(thread->arg);
return JAS_CAST(unsigned, result);
}
#endif
/*!
@brief Compare two thread IDs.
@warning
This function is only for internal use by the JasPer software.
This function may be changed/removed without any notice in future versions
of JasPer.
*/
static inline
int jas_thread_compare(jas_thread_id_t x, jas_thread_id_t y)
{
#if defined(JAS_THREADS_C11)
return thrd_equal(x, y);
#elif defined(JAS_THREADS_PTHREAD)
return pthread_equal(x, y);
#elif defined(JAS_THREADS_WIN32)
return GetThreadId(x) == GetThreadId(y);
#endif
}
/*!
@brief Create a thread.
@warning
This function is only for internal use by the JasPer software.
This function may be changed/removed without any notice in future versions
of JasPer.
*/
static inline
int jas_thread_create(jas_thread_t *thread, int (*func)(void *), void *arg)
{
assert(thread);
assert(func);
#if defined(JAS_THREADS_C11)
return thrd_create(thread, func, arg) == thrd_success ? 0 : -1;
#elif defined(JAS_THREADS_PTHREAD)
thread->func = func;
thread->arg = arg;
thread->result = 0;
return pthread_create(&thread->id, 0, thread_func_wrapper, thread);
#elif defined(JAS_THREADS_WIN32)
uintptr_t handle;
thread->func = func;
thread->arg = arg;
if (!(handle = _beginthreadex(0, 0, thread_func_wrapper, thread, 0, 0))) {
return -1;
}
thread->id = JAS_CAST(jas_thread_id_t, handle);
return 0;
#endif
}
/*!
@brief Join a thread.
@warning
This function is only for internal use by the JasPer software.
This function may be changed/removed without any notice in future versions
of JasPer.
*/
static inline
int jas_thread_join(jas_thread_t *thread, int *result)
{
assert(thread);
#if defined(JAS_THREADS_C11)
return thrd_join(*thread, result) == thrd_success ? 0 : -1;
#elif defined(JAS_THREADS_PTHREAD)
void *result_buf;
int ret = pthread_join(thread->id, &result_buf);
if (!ret) {
jas_thread_t *other_thread = JAS_CAST(jas_thread_t *, result_buf);
if (result) {
/* A null pointer is probably a bug. */
assert(other_thread);
*result = other_thread ? other_thread->result : 0;
}
}
return ret;
#elif defined(JAS_THREADS_WIN32)
DWORD w;
DWORD code;
if ((w = WaitForSingleObject(thread->id, INFINITE)) != WAIT_OBJECT_0) {
return -1;
}
if (result) {
if (!GetExitCodeThread(thread->id, &code)) {
CloseHandle(thread->id);
return -1;
}
*result = JAS_CAST(int, code);
}
CloseHandle(thread->id);
return 0;
#endif
}
/*!
@brief
Yield the processor.
@warning
This function is only for internal use by the JasPer software.
This function may be changed/removed without any notice in future versions
of JasPer.
*/
static inline void jas_thread_yield(void)
{
#if defined(JAS_THREADS_C11)
thrd_yield();
#elif defined(JAS_THREADS_PTHREAD)
sched_yield();
#elif defined(JAS_THREADS_WIN32)
SwitchToThread();
#endif
}
#if 0
/* This functionality is not available for all threading support libraries. */
static inline
void jas_thread_exit(int result)
{
#if defined(JAS_THREADS_C11)
thrd_exit(result);
#elif defined(JAS_THREADS_PTHREAD)
/* This does not have a trivial implementation, as far as I can see. */
/* There is no jas_thread_find function. */
jas_thread_t *thread = jas_thread_find(pthread_self());
thread->result = result;
pthread_exit(JAS_CAST(void *, thread));
#endif
}
#endif
#if 0
/*!
@brief Get the ID of the calling thread.
*/
static inline
jas_thread_id_t jas_thread_current(void)
{
#if defined(JAS_THREADS_C11)
return thrd_current();
#elif defined(JAS_THREADS_PTHREAD)
return pthread_self();
#elif defined(JAS_THREADS_WIN32)
/* FIXME - NOT YET IMPLEMENTED. */
abort();
#endif
}
#endif
#endif
/******************************************************************************\
*
\******************************************************************************/
#endif
#else
/******************************************************************************\
* No Threading Support.
\******************************************************************************/
#endif
/*!
* @}
* @endcond
*/
#ifdef __cplusplus
}
#endif
#endif