diff --git a/src/hl.h b/src/hl.h index 05e92cbb4..64c81dc65 100644 --- a/src/hl.h +++ b/src/hl.h @@ -671,9 +671,13 @@ HL_API vdynamic *hl_dyn_call_safe( vclosure *c, vdynamic **args, int nargs, bool struct _hl_thread; struct _hl_mutex; +struct _hl_semaphore; +struct _hl_condition; struct _hl_tls; typedef struct _hl_thread hl_thread; typedef struct _hl_mutex hl_mutex; +typedef struct _hl_semaphore hl_semaphore; +typedef struct _hl_condition hl_condition; typedef struct _hl_tls hl_tls; HL_API hl_thread *hl_thread_start( void *callback, void *param, bool withGC ); @@ -688,6 +692,22 @@ HL_API bool hl_mutex_try_acquire( hl_mutex *l ); HL_API void hl_mutex_release( hl_mutex *l ); HL_API void hl_mutex_free( hl_mutex *l ); +HL_API hl_semaphore *hl_semaphore_alloc(int value); +HL_API void hl_semaphore_acquire(hl_semaphore *sem); +HL_API bool hl_semaphore_try_acquire(hl_semaphore *sem, vdynamic *timeout); +HL_API void hl_semaphore_release(hl_semaphore *sem); +HL_API void hl_semaphore_free(hl_semaphore *sem); + +HL_API hl_condition *hl_condition_alloc(); +HL_API void hl_condition_acquire(hl_condition *cond); +HL_API bool hl_condition_try_acquire(hl_condition *cond); +HL_API void hl_condition_release(hl_condition *cond); +HL_API void hl_condition_wait(hl_condition *cond); +HL_API bool hl_condition_timed_wait(hl_condition *cond, double timeout); +HL_API void hl_condition_signal(hl_condition *cond); +HL_API void hl_condition_broadcast(hl_condition *cond); +HL_API void hl_condition_free(hl_condition *cond); + HL_API hl_tls *hl_tls_alloc( bool gc_value ); HL_API void hl_tls_set( hl_tls *l, void *value ); HL_API void *hl_tls_get( hl_tls *l ); diff --git a/src/std/thread.c b/src/std/thread.c index 115cf6f31..e49edb90a 100644 --- a/src/std/thread.c +++ b/src/std/thread.c @@ -28,6 +28,14 @@ struct _hl_mutex { void *_unused; }; +struct _hl_semaphore { + void (*free)(hl_semaphore *); +}; + +struct _hl_condition { + void (*free)(hl_condition *); +}; + struct _hl_tls { void (*free)( hl_tls * ); void *value; @@ -41,6 +49,17 @@ struct _hl_mutex { bool is_gc; }; +struct _hl_semaphore { + void (*free)(hl_semaphore *); + HANDLE sem; +}; + +struct _hl_condition { + void (*free)(hl_condition *); + CRITICAL_SECTION cs; + CONDITION_VARIABLE cond; +}; + struct _hl_tls { void (*free)( hl_tls * ); DWORD tid; @@ -53,6 +72,11 @@ struct _hl_tls { # include # include +#ifdef __APPLE__ +#include +#else +#include +#endif struct _hl_mutex { void (*free)( hl_mutex * ); @@ -60,6 +84,21 @@ struct _hl_mutex { bool is_gc; }; +struct _hl_semaphore { + void (*free)(hl_semaphore *); +# ifdef __APPLE__ + dispatch_semaphore_t sem; +# else + sem_t sem; +#endif +}; + +struct _hl_condition { + void (*free)(hl_condition *); + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + struct _hl_tls { void (*free)( hl_tls * ); pthread_key_t key; @@ -146,6 +185,241 @@ DEFINE_PRIM(_BOOL, mutex_try_acquire, _MUTEX); DEFINE_PRIM(_VOID, mutex_release, _MUTEX); DEFINE_PRIM(_VOID, mutex_free, _MUTEX); +// ------------------ SEMAPHORE + +HL_API hl_semaphore *hl_semaphore_alloc(int value) { +# if !defined(HL_THREADS) + static struct _hl_semaphore null_semaphore = {0}; + return (hl_condition *)&null_semaphore; +# elif defined(HL_WIN) + hl_semaphore *sem = + (hl_semaphore *)hl_gc_alloc_finalizer(sizeof(hl_semaphore)); + sem->free = hl_semaphore_free; + sem->sem = CreateSemaphoreW(NULL, value, 0x7FFFFFFF, NULL); + return sem; +# else + hl_semaphore *sem = + (hl_semaphore *)hl_gc_alloc_finalizer(sizeof(hl_semaphore)); + sem->free = hl_semaphore_free; +# ifdef __APPLE__ + sem->sem = dispatch_semaphore_create(value); +# else + sem_init(&sem->sem, false, value); +# endif + return sem; +# endif +} + +HL_API void hl_semaphore_acquire(hl_semaphore *sem) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + WaitForSingleObject(sem->sem, INFINITE); +# else +# ifdef __APPLE__ + dispatch_semaphore_wait(sem->sem, DISPATCH_TIME_FOREVER); +# else + sem_wait(&sem->sem); +# endif +# endif +} + +HL_API bool hl_semaphore_try_acquire(hl_semaphore *sem, vdynamic *timeout) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + return WaitForSingleObject(sem->sem, + timeout ? (DWORD)((FLOAT)timeout->v.d * 1000.0) + : 0) == WAIT_OBJECT_0; +# else +# ifdef __APPLE__ + return dispatch_semaphore_wait( + sem, dispatch_time(DISPATCH_TIME_NOW, + (int64_t)((timeout ? timeout->v.d : 0) * + 1000 * 1000 * 1000))) == 0; +# else + if (timeout) { + struct timeval tv; + struct timespec t; + double delta = timeout->v.d; + int idelta = (int)delta, idelta2; + delta -= idelta; + delta *= 1.0e9; + gettimeofday(&tv, NULL); + delta += tv.tv_usec * 1000.0; + idelta2 = (int)(delta / 1e9); + delta -= idelta2 * 1e9; + t.tv_sec = tv.tv_sec + idelta + idelta2; + t.tv_nsec = (long)delta; + return sem_timedwait(&sem->sem, &t) == 0; + } else { + + return sem_trywait(&sem->sem) == 0; + } +# endif +# endif +} + +HL_API void hl_semaphore_release(hl_semaphore *sem) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + ReleaseSemaphore(sem->sem, 1, NULL); +# else +# ifdef __APPLE__ + dispatch_semaphore_signal(&sem->sem); +# else + sem_post(&sem->sem); +# endif +# endif +} + +HL_API void hl_semaphore_free(hl_semaphore *sem) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + if (sem->free) { + CloseHandle(sem->sem); + sem->free = NULL; + } +# else + if (sem->free) { + sem_destroy(&sem->sem); + sem->free = NULL; + } +# endif +} + +#define _SEMAPHORE _ABSTRACT(hl_semaphore) +DEFINE_PRIM(_SEMAPHORE, semaphore_alloc, _I32); +DEFINE_PRIM(_VOID, semaphore_acquire, _SEMAPHORE); +DEFINE_PRIM(_BOOL, semaphore_try_acquire, _SEMAPHORE _NULL(_F64)); +DEFINE_PRIM(_VOID, semaphore_release, _SEMAPHORE); +DEFINE_PRIM(_VOID, semaphore_free, _SEMAPHORE); +// ------------------ CONDITION + +HL_API hl_condition *hl_condition_alloc() { +# if !defined(HL_THREADS) + static struct _hl_condition null_condition = {0}; + return (hl_condition *)&null_condition; +# elif defined(HL_WIN) + hl_condition *cond = + (hl_condition *)hl_gc_alloc_finalizer(sizeof(hl_condition)); + cond->free = hl_condition_free; + InitializeCriticalSection(&cond->cs); + InitializeConditionVariable(&cond->cond); + return cond; +# else + hl_condition *cond = + (hl_condition *)hl_gc_alloc_finalizer(sizeof(hl_condition)); + cond->free = hl_condition_free; + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_cond_init(&cond->cond, &attr); + pthread_condattr_destroy(&attr); + pthread_mutexattr_t mutexattr; + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&cond->mutex, &mutexattr); + pthread_mutexattr_destroy(&mutexattr); + return cond; +# endif +} +HL_API void hl_condition_acquire(hl_condition *cond) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + EnterCriticalSection(&cond->cs); +# else + pthread_mutex_lock(&cond->mutex); +# endif +} + +HL_API bool hl_condition_try_acquire(hl_condition *cond) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + return (bool)TryEnterCriticalSection(&cond->cs); +# else + return pthread_mutex_trylock(&cond->mutex) == 0; +# endif +} + +HL_API void hl_condition_release(hl_condition *cond) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + LeaveCriticalSection(&cond->cs); +# else + pthread_mutex_unlock(&cond->mutex); +# endif +} +HL_API void hl_condition_wait(hl_condition *cond) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + SleepConditionVariableCS(&cond->cond, &cond->cs, INFINITE); +# else + pthread_cond_wait(&cond->cond, &cond->mutex); +# endif +} + +HL_API bool hl_condition_timed_wait(hl_condition *cond, double timeout) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + SleepConditionVariableCS(&cond->cond, &cond->cs, + (DWORD)((FLOAT)timeout * 1000.0)); +# else + struct timeval tv; + struct timespec t; + double delta = timeout; + int idelta = (int)delta, idelta2; + delta -= idelta; + delta *= 1.0e9; + gettimeofday(&tv, NULL); + delta += tv.tv_usec * 1000.0; + idelta2 = (int)(delta / 1e9); + delta -= idelta2 * 1e9; + t.tv_sec = tv.tv_sec + idelta + idelta2; + t.tv_nsec = (long)delta; + return pthread_cond_timedwait(&cond->cond, &cond->mutex, &t) == 0; +# endif +} + +HL_API void hl_condition_signal(hl_condition *cond) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + WakeConditionVariable(&cond->cond); +# else + pthread_cond_signal(&cond->cond); +# endif +} +HL_API void hl_condition_broadcast(hl_condition *cond) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + WakeAllConditionVariable(&cond->cond); +# else + pthread_cond_broadcast(&cond->cond); +# endif +} +HL_API void hl_condition_free(hl_condition *cond) { +# if !defined(HL_THREADS) +# elif defined(HL_WIN) + if (cond->free) { + DeleteCriticalSection(&cond->cs); + cond->free = NULL; + } +# else + if (cond->free) { + pthread_cond_destroy(&cond->cond); + pthread_mutex_destroy(&cond->mutex); + cond->free = NULL; + } +# endif +} + +#define _CONDITION _ABSTRACT(hl_condition) +DEFINE_PRIM(_CONDITION, condition_alloc, _NO_ARG) +DEFINE_PRIM(_VOID, condition_acquire, _CONDITION) +DEFINE_PRIM(_BOOL, condition_try_acquire, _CONDITION) +DEFINE_PRIM(_VOID, condition_release, _CONDITION) +DEFINE_PRIM(_VOID, condition_wait, _CONDITION) +DEFINE_PRIM(_BOOL, condition_timed_wait, _CONDITION _F64) +DEFINE_PRIM(_VOID, condition_signal, _CONDITION) +DEFINE_PRIM(_VOID, condition_broadcast, _CONDITION) + // ----------------- THREAD LOCAL HL_PRIM hl_tls *hl_tls_alloc( bool gc_value ) {