feature: add linux condition variable, mutex, and read-write lock implementations

This commit is contained in:
MechSlayer 2025-09-05 22:00:00 +02:00
parent 3211bb45b3
commit 9bd6be2c2c
4 changed files with 290 additions and 0 deletions

View file

@ -0,0 +1,111 @@
#include "drang/alloc.h"
#include "errno_convert.h"
#include "internal.h"
#include <drang/error.h>
#include <drang/sync.h>
#include <errno.h>
size_t drang_cond_size(void)
{
return sizeof(struct drang_cond);
}
size_t drang_cond_alignment(void)
{
return alignof(struct drang_cond);
}
int drang_cond_init(struct drang_cond *cond)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(cond != NULL, DRANG_EINVAL);
DRANG_CHECK(!cond->initialized, DRANG_EBUSY);
const int res = pthread_cond_init(&cond->inner, NULL);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
cond->initialized = true;
DRANG_END_TRY_IGNORE();
}
void drang_cond_fini(struct drang_cond *cond)
{
if (cond == NULL) {
return;
}
pthread_cond_destroy(&cond->inner);
cond->initialized = false;
}
int drang_cond_signal(struct drang_cond *cond)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(cond != NULL, DRANG_EINVAL);
DRANG_CHECK(cond->initialized, DRANG_EINVAL);
const int res = pthread_cond_signal(&cond->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}
int drang_cond_broadcast(struct drang_cond *cond)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(cond != NULL, DRANG_EINVAL);
DRANG_CHECK(cond->initialized, DRANG_EINVAL);
const int res = pthread_cond_broadcast(&cond->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}
int drang_cond_wait(struct drang_cond *cond, struct drang_mutex *mutex)
{
return drang_cond_timedwait(cond, mutex, 0);
}
int drang_cond_timedwait(struct drang_cond *cond, struct drang_mutex *mutex, uint64_t timeout_ms)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(cond != NULL, DRANG_EINVAL);
DRANG_CHECK(cond->initialized, DRANG_EINVAL);
DRANG_CHECK(mutex != NULL, DRANG_EINVAL);
DRANG_CHECK(mutex->initialized, DRANG_EINVAL);
int res;
if (timeout_ms == 0) {
// Infinite wait
res = pthread_cond_wait(&cond->inner, &mutex->inner);
} else {
// Timed wait - calculate absolute timeout
struct timespec abs_timeout;
if (clock_gettime(CLOCK_REALTIME, &abs_timeout) != 0) {
DRANG_FAIL(drang_errno_to_error(errno));
}
// Add timeout milliseconds to current time
abs_timeout.tv_sec += (time_t)(timeout_ms / 1000);
abs_timeout.tv_nsec += (time_t)((timeout_ms % 1000) * 1000000);
// Handle nanosecond overflow
if (abs_timeout.tv_nsec >= 1000000000) {
abs_timeout.tv_sec += 1;
abs_timeout.tv_nsec -= 1000000000;
}
res = pthread_cond_timedwait(&cond->inner, &mutex->inner, &abs_timeout);
}
if (res != 0) {
if (res == ETIMEDOUT) {
DRANG_FAIL(DRANG_ETIMEDOUT);
}
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}

View file

@ -0,0 +1,22 @@
#pragma once
#include <drang/sync.h>
#include <pthread.h>
#include <stdbool.h>
struct drang_mutex
{
pthread_mutex_t inner;
bool initialized;
};
struct drang_cond
{
pthread_cond_t inner;
bool initialized;
};
struct drang_rwlock
{
pthread_rwlock_t inner;
bool initialized;
};

View file

@ -0,0 +1,68 @@
#include "drang/alloc.h"
#include "errno_convert.h"
#include "internal.h"
#include <drang/error.h>
#include <drang/sync.h>
size_t drang_mutex_size(void)
{
return sizeof(struct drang_mutex);
}
size_t drang_mutex_alignment(void)
{
return alignof(struct drang_mutex);
}
int drang_mutex_init(struct drang_mutex *mutex)
{
DRANG_BEGIN_TRY();
DRANG_CHECK(mutex != NULL, DRANG_EINVAL);
DRANG_CHECK(!mutex->initialized, DRANG_EBUSY);
const int res = pthread_mutex_init(&mutex->inner, NULL);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
mutex->initialized = true;
DRANG_END_TRY_IGNORE();
}
void drang_mutex_fini(struct drang_mutex *mutex)
{
if (mutex == NULL) {
return;
}
if (mutex->initialized) {
pthread_mutex_destroy(&mutex->inner);
mutex->initialized = false;
}
}
int drang_mutex_lock(struct drang_mutex *mutex)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(mutex != NULL, DRANG_EINVAL);
DRANG_CHECK(mutex->initialized, DRANG_EINVAL);
const int res = pthread_mutex_lock(&mutex->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}
int drang_mutex_unlock(struct drang_mutex *mutex)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(mutex != NULL, DRANG_EINVAL);
DRANG_CHECK(mutex->initialized, DRANG_EINVAL);
const int res = pthread_mutex_unlock(&mutex->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}

View file

@ -0,0 +1,89 @@
#include "errno_convert.h"
#include "internal.h"
#include <drang/error.h>
#include <drang/sync.h>
#include <stdalign.h>
size_t drang_rwlock_size(void)
{
return sizeof(struct drang_rwlock);
}
size_t drang_rwlock_alignment(void)
{
return alignof(struct drang_rwlock);
}
int drang_rwlock_init(struct drang_rwlock *rwlock)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(rwlock != NULL, DRANG_EINVAL);
DRANG_CHECK(!rwlock->initialized, DRANG_EBUSY);
const int res = pthread_rwlock_init(&rwlock->inner, NULL);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
rwlock->initialized = true;
DRANG_END_TRY_IGNORE();
}
void drang_rwlock_fini(struct drang_rwlock *rwlock)
{
if (rwlock == NULL) {
return;
}
if (rwlock->initialized) {
pthread_rwlock_destroy(&rwlock->inner);
rwlock->initialized = false;
}
}
int drang_rwlock_rdlock(struct drang_rwlock *rwlock)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(rwlock != NULL, DRANG_EINVAL);
DRANG_CHECK(rwlock->initialized, DRANG_EINVAL);
const int res = pthread_rwlock_rdlock(&rwlock->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}
int drang_rwlock_wrlock(struct drang_rwlock *rwlock)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(rwlock != NULL, DRANG_EINVAL);
DRANG_CHECK(rwlock->initialized, DRANG_EINVAL);
const int res = pthread_rwlock_wrlock(&rwlock->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}
int drang_rwlock_rdunlock(struct drang_rwlock *rwlock)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(rwlock != NULL, DRANG_EINVAL);
DRANG_CHECK(rwlock->initialized, DRANG_EINVAL);
const int res = pthread_rwlock_unlock(&rwlock->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}
int drang_rwlock_wrunlock(struct drang_rwlock *rwlock)
{
DRANG_BEGIN_TRY()
DRANG_CHECK(rwlock != NULL, DRANG_EINVAL);
DRANG_CHECK(rwlock->initialized, DRANG_EINVAL);
const int res = pthread_rwlock_unlock(&rwlock->inner);
if (res != 0) {
DRANG_FAIL(drang_errno_to_error(res));
}
DRANG_END_TRY_IGNORE();
}