352 lines
6.9 KiB
C
352 lines
6.9 KiB
C
/*
|
|
This is free and unencumbered software released into the public domain.
|
|
|
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
distribute this software, either in source code form or as a compiled
|
|
binary, for any purpose, commercial or non-commercial, and by any
|
|
means.
|
|
|
|
In jurisdictions that recognize copyright laws, the author or authors
|
|
of this software dedicate any and all copyright interest in the
|
|
software to the public domain. We make this dedication for the benefit
|
|
of the public at large and to the detriment of our heirs and
|
|
successors. We intend this dedication to be an overt act of
|
|
relinquishment in perpetuity of all present and future rights to this
|
|
software under copyright law.
|
|
|
|
THE SOFTWARE IS PROVIDED "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.
|
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
For more information, please refer to <http://unlicense.org/>
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#if defined(__linux__)
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#include "sync.h"
|
|
#include "clock.h"
|
|
|
|
//#define SYNC_PRINT_ERRORS
|
|
#ifdef SYNC_PRINT_ERRORS
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#endif
|
|
|
|
PROFILE_GLOBAL_START;
|
|
|
|
void os_mutex_create(os_mutex_t *mutex)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(_WIN32)
|
|
InitializeCriticalSection(mutex);
|
|
#elif defined(__linux__)
|
|
if (pthread_mutex_init(mutex, NULL))
|
|
abort();
|
|
#else
|
|
(void) mutex;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
void os_mutex_delete(os_mutex_t *mutex)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(_WIN32)
|
|
DeleteCriticalSection(mutex);
|
|
#elif defined(__linux__)
|
|
if (pthread_mutex_destroy(mutex))
|
|
abort();
|
|
#else
|
|
(void) mutex;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
void os_mutex_lock(os_mutex_t *mutex)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(_WIN32)
|
|
EnterCriticalSection(mutex);
|
|
#elif defined(__linux__)
|
|
if (pthread_mutex_lock(mutex))
|
|
abort();
|
|
#else
|
|
(void) mutex;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
void os_mutex_unlock(os_mutex_t *mutex)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(_WIN32)
|
|
LeaveCriticalSection(mutex);
|
|
#elif defined(__linux__)
|
|
if (pthread_mutex_unlock(mutex))
|
|
abort();
|
|
#else
|
|
(void) mutex;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
void os_condvar_create(os_condvar_t *condvar)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(_WIN32)
|
|
InitializeConditionVariable(condvar);
|
|
#elif defined(__linux__)
|
|
if (pthread_cond_init(condvar, NULL))
|
|
abort();
|
|
#else
|
|
(void) condvar;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
void os_condvar_delete(os_condvar_t *condvar)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(__linux__)
|
|
if (pthread_cond_destroy(condvar))
|
|
abort();
|
|
#else
|
|
(void) condvar;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
bool os_condvar_wait(os_condvar_t *condvar, os_mutex_t *mutex, int timeout_ms)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(_WIN32)
|
|
|
|
DWORD timeout = INFINITE;
|
|
if (timeout_ms >= 0) timeout = timeout_ms;
|
|
if (!SleepConditionVariableCS(condvar, mutex, timeout)) {
|
|
if (GetLastError() == ERROR_TIMEOUT) {
|
|
PROFILE_END;
|
|
return false;
|
|
}
|
|
abort();
|
|
}
|
|
|
|
PROFILE_END;
|
|
return true;
|
|
|
|
#elif defined(__linux__)
|
|
|
|
int err;
|
|
if (timeout_ms < 0)
|
|
err = pthread_cond_wait(condvar, mutex);
|
|
else {
|
|
uint64_t wakeup_ms = (uint64_t) timeout_ms + get_absolute_time_us() / 1000;
|
|
struct timespec abstime = {
|
|
.tv_sec = wakeup_ms / 1000,
|
|
.tv_nsec = (wakeup_ms % 1000) * 1000000,
|
|
};
|
|
err = pthread_cond_timedwait(condvar, mutex, &abstime);
|
|
}
|
|
if (err) {
|
|
if (err == ETIMEDOUT) {
|
|
PROFILE_END;
|
|
return false;
|
|
}
|
|
#ifdef SYNC_PRINT_ERRORS
|
|
fprintf(stderr, "ERROR!! pthread_cond_wait/timedwait: %s\n", strerror(err));
|
|
#endif
|
|
abort();
|
|
}
|
|
|
|
PROFILE_END;
|
|
return true;
|
|
|
|
#else
|
|
PROFILE_END;
|
|
(void) condvar;
|
|
#endif
|
|
}
|
|
|
|
void os_condvar_signal(os_condvar_t *condvar)
|
|
{
|
|
PROFILE_START;
|
|
|
|
#if defined(_WIN32)
|
|
WakeConditionVariable(condvar);
|
|
#elif defined(__linux__)
|
|
if (pthread_cond_signal(condvar))
|
|
abort();
|
|
#else
|
|
(void) condvar;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
void semaphore_create(semaphore_t *sem, int count)
|
|
{
|
|
PROFILE_START;
|
|
|
|
sem->count = count;
|
|
os_mutex_create(&sem->mutex);
|
|
os_condvar_create(&sem->cond);
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
void semaphore_delete(semaphore_t *sem)
|
|
{
|
|
PROFILE_START;
|
|
|
|
os_mutex_delete(&sem->mutex);
|
|
os_condvar_delete(&sem->cond);
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
bool semaphore_wait(semaphore_t *sem, int count, int timeout_ms)
|
|
{
|
|
PROFILE_START;
|
|
|
|
assert(count > 0);
|
|
|
|
uint64_t start_time_ms = get_relative_time_ns() / 1000000;
|
|
|
|
os_mutex_lock(&sem->mutex);
|
|
while (sem->count < count) {
|
|
|
|
uint64_t current_time_ms = get_relative_time_ns() / 1000000;
|
|
|
|
int remaining_ms = -1;
|
|
if (timeout_ms >= 0)
|
|
remaining_ms = timeout_ms - (int) (current_time_ms - start_time_ms);
|
|
|
|
if (!os_condvar_wait(&sem->cond, &sem->mutex, remaining_ms)) {
|
|
os_mutex_unlock(&sem->mutex);
|
|
PROFILE_END;
|
|
return false;
|
|
}
|
|
}
|
|
sem->count -= count;
|
|
os_mutex_unlock(&sem->mutex);
|
|
|
|
PROFILE_END;
|
|
return true;
|
|
}
|
|
|
|
void semaphore_signal(semaphore_t *sem, int count)
|
|
{
|
|
PROFILE_START;
|
|
|
|
assert(count > 0);
|
|
|
|
os_mutex_lock(&sem->mutex);
|
|
sem->count += count;
|
|
if (sem->count > 0)
|
|
os_condvar_signal(&sem->cond);
|
|
os_mutex_unlock(&sem->mutex);
|
|
|
|
PROFILE_END;
|
|
}
|
|
|
|
bool os_semaphore_create(os_semaphore_t *sem, int count, int max)
|
|
{
|
|
PROFILE_START;
|
|
|
|
int ok;
|
|
|
|
#ifdef _WIN32
|
|
SECURITY_ATTRIBUTES *attr = NULL; // Default
|
|
const char *name = NULL; // No name
|
|
void *handle = CreateSemaphoreA(attr, count, max, name);
|
|
if (handle == NULL) {
|
|
PROFILE_END;
|
|
return false;
|
|
}
|
|
sem->data = handle;
|
|
ok = 1;
|
|
#else
|
|
(void) max; // POSIX doesn't use this
|
|
ok = sem_init(&sem->data, 0, count) == 0;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
return ok;
|
|
}
|
|
|
|
bool os_semaphore_delete(os_semaphore_t *sem)
|
|
{
|
|
PROFILE_START;
|
|
|
|
int ok;
|
|
|
|
#ifdef _WIN32
|
|
CloseHandle(sem->data);
|
|
ok = 1;
|
|
#else
|
|
ok = sem_destroy(&sem->data) == 0;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
return ok;
|
|
}
|
|
|
|
bool os_semaphore_wait(os_semaphore_t *sem)
|
|
{
|
|
PROFILE_START;
|
|
|
|
int ok;
|
|
|
|
#ifdef _WIN32
|
|
ok = WaitForSingleObject(sem->data, INFINITE) == WAIT_OBJECT_0;
|
|
#else
|
|
ok = sem_wait(&sem->data) == 0;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
return ok;
|
|
}
|
|
|
|
bool os_semaphore_signal(os_semaphore_t *sem)
|
|
{
|
|
PROFILE_START;
|
|
|
|
int ok;
|
|
|
|
#ifdef _WIN32
|
|
ok = ReleaseSemaphore(sem->data, 1, NULL);
|
|
#else
|
|
ok = sem_post(&sem->data) == 0;
|
|
#endif
|
|
|
|
PROFILE_END;
|
|
return ok;
|
|
}
|
|
|
|
PROFILE_GLOBAL_END;
|
|
|
|
profile_results_t sync_profile_results(void)
|
|
{
|
|
return PROFILE_RESULTS;
|
|
} |