diff --git a/Source/DrangPlatform/Include/drang/error.h b/Source/DrangPlatform/Include/drang/error.h index 8a616ae..6100d59 100644 --- a/Source/DrangPlatform/Include/drang/error.h +++ b/Source/DrangPlatform/Include/drang/error.h @@ -10,17 +10,18 @@ DRANG_BEGIN_DECLS */ DRANG_PLATFORM_API const char *drang_error_str(int err); -#define DRANG_EOK (0) // Success -#define DRANG_EINVAL (-1) // Invalid argument -#define DRANG_ENOMEM (-2) // Out of memory -#define DRANG_EIO (-3) // I/O error -#define DRANG_NOTSUP (-4) // Operation not supported -#define DRANG_EAGAIN (-5) // Resource temporarily unavailable -#define DRANG_ENOENT (-6) // No such file or directory -#define DRANG_EDEADLK (-7) // Resource deadlock would occur -#define DRANG_EPERM (-8) // Operation not permitted -#define DRANG_ETIMEDOUT (-9) // Connection timed out -#define DRANG_EBUSY (-10) // Device or resource busy +#define DRANG_EOK (0) // Success +#define DRANG_EINVAL (-1) // Invalid argument +#define DRANG_ENOMEM (-2) // Out of memory +#define DRANG_EIO (-3) // I/O error +#define DRANG_NOTSUP (-4) // Operation not supported +#define DRANG_EAGAIN (-5) // Resource temporarily unavailable +#define DRANG_ENOENT (-6) // No such file or directory +#define DRANG_EDEADLK (-7) // Resource deadlock would occur +#define DRANG_EPERM (-8) // Operation not permitted +#define DRANG_ETIMEDOUT (-9) // Connection timed out +#define DRANG_EBUSY (-10) // Device or resource busy +#define DRANG_EPLATFORM (-1000) // An unknown platform-specific error occurred /** * @brief Begins a try-catch block for error handling. diff --git a/Source/DrangPlatform/Include/drang/sync.h b/Source/DrangPlatform/Include/drang/sync.h index aa0460e..5401c94 100644 --- a/Source/DrangPlatform/Include/drang/sync.h +++ b/Source/DrangPlatform/Include/drang/sync.h @@ -28,6 +28,17 @@ struct drang_mutex; */ DRANG_PLATFORM_API size_t drang_mutex_size(void); +/** + * @brief Returns the alignment in bytes required for a mutex structure. + * + * This function returns the platform-specific alignment needed to allocate + * a mutex structure. Useful for stack allocation or custom memory management. + * + * @return Alignment in bytes required for a drang_mutex structure. + * @remarks The returned alignment is platform-specific and may vary between builds. + */ +DRANG_PLATFORM_API size_t drang_mutex_alignment(void); + /** * @brief Creates and initializes a new mutex on the heap. * @@ -144,6 +155,17 @@ struct drang_cond; */ DRANG_PLATFORM_API size_t drang_cond_size(void); +/** + * @brief Returns the alignment in bytes required for a condition variable structure. + * + * This function returns the platform-specific alignment needed to allocate + * a condition variable structure. Useful for stack allocation or custom memory management. + * + * @return Alignment in bytes required for a drang_cond structure. + * @remarks The returned alignment is platform-specific and may vary between builds. + */ +DRANG_PLATFORM_API size_t drang_cond_alignment(void); + /** * @brief Creates and initializes a new condition variable on the heap. * @@ -300,6 +322,17 @@ struct drang_rwlock; */ DRANG_PLATFORM_API size_t drang_rwlock_size(void); +/** + * @brief Returns the alignment in bytes required for a read-write lock structure. + * + * This function returns the platform-specific alignment needed to allocate + * a read-write lock structure. Useful for stack allocation or custom memory management. + * + * @return Alignment in bytes required for a drang_rwlock structure. + * @remarks The returned alignment is platform-specific and may vary between builds. + */ +DRANG_PLATFORM_API size_t drang_rwlock_alignment(void); + /** * @brief Creates and initializes a new read-write lock on the heap. * diff --git a/Source/DrangPlatform/Source/errno_convert.c b/Source/DrangPlatform/Source/errno_convert.c new file mode 100644 index 0000000..0ec599f --- /dev/null +++ b/Source/DrangPlatform/Source/errno_convert.c @@ -0,0 +1,56 @@ +#include "errno_convert.h" + +#include "drang/error.h" +#include + +int drang_error_to_errno(int error) +{ + switch (error) { + case DRANG_EOK: + return 0; + case DRANG_EAGAIN: + return EAGAIN; + case DRANG_ENOMEM: + return ENOMEM; + case DRANG_EINVAL: + return EINVAL; + case DRANG_ENOENT: + return ENOENT; + case DRANG_EDEADLK: + return EDEADLK; + case DRANG_EPERM: + return EPERM; + case DRANG_ETIMEDOUT: + return ETIMEDOUT; + case DRANG_EBUSY: + return EBUSY; + case DRANG_EPLATFORM: + default: + return EIO; // Generic I/O error for unknown platform errors + } +} +int drang_errno_to_error(const int errnum) +{ + switch (errnum) { + case 0: + return DRANG_EOK; + case EAGAIN: + return DRANG_EAGAIN; + case ENOMEM: + return DRANG_ENOMEM; + case EINVAL: + return DRANG_EINVAL; + case ENOENT: + return DRANG_ENOENT; + case EDEADLK: + return DRANG_EDEADLK; + case EPERM: + return DRANG_EPERM; + case ETIMEDOUT: + return DRANG_ETIMEDOUT; + case EBUSY: + return DRANG_EBUSY; + default: + return DRANG_EPLATFORM; + } +} diff --git a/Source/DrangPlatform/Source/errno_convert.h b/Source/DrangPlatform/Source/errno_convert.h new file mode 100644 index 0000000..aba18d8 --- /dev/null +++ b/Source/DrangPlatform/Source/errno_convert.h @@ -0,0 +1,9 @@ +#pragma once +#include + +DRANG_BEGIN_DECLS + +int drang_error_to_errno(int error); +int drang_errno_to_error(int errnum); + +DRANG_END_DECLS diff --git a/Source/DrangPlatform/Source/linux/sync/cond_var.c b/Source/DrangPlatform/Source/linux/sync/cond_var.c new file mode 100644 index 0000000..aafd680 --- /dev/null +++ b/Source/DrangPlatform/Source/linux/sync/cond_var.c @@ -0,0 +1,111 @@ +#include "drang/alloc.h" +#include "errno_convert.h" +#include "internal.h" +#include +#include +#include + +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(); +} diff --git a/Source/DrangPlatform/Source/linux/sync/internal.h b/Source/DrangPlatform/Source/linux/sync/internal.h new file mode 100644 index 0000000..e41e1d5 --- /dev/null +++ b/Source/DrangPlatform/Source/linux/sync/internal.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + +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; +}; diff --git a/Source/DrangPlatform/Source/linux/sync/mutex.c b/Source/DrangPlatform/Source/linux/sync/mutex.c new file mode 100644 index 0000000..2510a96 --- /dev/null +++ b/Source/DrangPlatform/Source/linux/sync/mutex.c @@ -0,0 +1,66 @@ +#include "drang/alloc.h" +#include "errno_convert.h" +#include "internal.h" +#include +#include + +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(); +} diff --git a/Source/DrangPlatform/Source/linux/sync/rwlock.c b/Source/DrangPlatform/Source/linux/sync/rwlock.c new file mode 100644 index 0000000..cd0093b --- /dev/null +++ b/Source/DrangPlatform/Source/linux/sync/rwlock.c @@ -0,0 +1,87 @@ +#include "errno_convert.h" +#include "internal.h" +#include +#include +#include + +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(); +} diff --git a/Source/DrangPlatform/Source/sync/cond_var.c b/Source/DrangPlatform/Source/sync/cond_var.c new file mode 100644 index 0000000..cc56ad5 --- /dev/null +++ b/Source/DrangPlatform/Source/sync/cond_var.c @@ -0,0 +1,35 @@ +#include "drang/alloc.h" +#include "drang/error.h" +#include + +int drang_cond_new(struct drang_cond **cond) +{ + struct drang_cond *c = NULL; + DRANG_BEGIN_TRY(); + + DRANG_CHECK(cond != NULL, DRANG_EINVAL); + + c = DRANG_TRY_ALLOC_ALIGNED(drang_cond_size(), drang_cond_alignment()); + + DRANG_TRY(drang_cond_init(c)); + + DRANG_RETURN_IN(cond, c); + + DRANG_CATCH(_) + { + if (c != NULL) { + drang_free(c); + } + } + DRANG_END_TRY(); +} + +void drang_cond_free(struct drang_cond *cond) +{ + if (cond == NULL) { + return; + } + + drang_cond_fini(cond); + drang_free(cond); +} diff --git a/Source/DrangPlatform/Source/sync/mutex.c b/Source/DrangPlatform/Source/sync/mutex.c new file mode 100644 index 0000000..6761be8 --- /dev/null +++ b/Source/DrangPlatform/Source/sync/mutex.c @@ -0,0 +1,35 @@ +#include "drang/alloc.h" +#include "drang/error.h" +#include + +int drang_mutex_new(struct drang_mutex **mutex) +{ + struct drang_mutex *m = NULL; + DRANG_BEGIN_TRY(); + + DRANG_CHECK(mutex != NULL, DRANG_EINVAL); + + m = DRANG_TRY_ALLOC_ALIGNED(drang_mutex_size(), drang_mutex_alignment()); + + DRANG_TRY(drang_mutex_init(m)); + + DRANG_RETURN_IN(mutex, m); + + DRANG_CATCH(_) + { + if (m != NULL) { + drang_free(m); + } + } + + DRANG_END_TRY() +} + +void drang_mutex_free(struct drang_mutex *mutex) +{ + if (mutex == NULL) { + return; + } + drang_mutex_fini(mutex); + drang_free(mutex); +} diff --git a/Source/DrangPlatform/Source/sync/rwlock.c b/Source/DrangPlatform/Source/sync/rwlock.c new file mode 100644 index 0000000..b35c2e9 --- /dev/null +++ b/Source/DrangPlatform/Source/sync/rwlock.c @@ -0,0 +1,35 @@ +#include +#include +#include + +int drang_rwlock_new(struct drang_rwlock **rwlock) +{ + struct drang_rwlock *rw = NULL; + DRANG_BEGIN_TRY(); + + DRANG_CHECK(rwlock != NULL, DRANG_EINVAL); + + rw = DRANG_TRY_ALLOC_ALIGNED(drang_rwlock_size(), drang_rwlock_alignment()); + + DRANG_TRY(drang_rwlock_init(rw)); + + DRANG_RETURN_IN(rwlock, rw); + + DRANG_CATCH(_) + { + if (rw != NULL) { + drang_free(rw); + } + } + DRANG_END_TRY(); +} + +void drang_rwlock_free(struct drang_rwlock *rwlock) +{ + if (rwlock == NULL) { + return; + } + + drang_rwlock_fini(rwlock); + drang_free(rwlock); +} diff --git a/Source/DrangPlatform/Source/win32/sync/cond_var.c b/Source/DrangPlatform/Source/win32/sync/cond_var.c index 59845b7..62dde1a 100644 --- a/Source/DrangPlatform/Source/win32/sync/cond_var.c +++ b/Source/DrangPlatform/Source/win32/sync/cond_var.c @@ -8,36 +8,9 @@ size_t drang_cond_size(void) return sizeof(struct drang_cond); } -int drang_cond_new(struct drang_cond **cond) +size_t drang_cond_alignment(void) { - struct drang_cond *c = NULL; - DRANG_BEGIN_TRY(); - - DRANG_CHECK(cond != NULL, DRANG_EINVAL); - - c = DRANG_TRY_ALLOC_T(struct drang_cond); - - DRANG_TRY(drang_cond_init(c)); - - DRANG_RETURN_IN(cond, c); - - DRANG_CATCH(_) - { - if (c != NULL) { - drang_free(c); - } - } - DRANG_END_TRY(); -} - -void drang_cond_free(struct drang_cond *cond) -{ - if (cond == NULL) { - return; - } - - drang_cond_fini(cond); - drang_free(cond); + return alignof(struct drang_cond); } int drang_cond_init(struct drang_cond *cond) diff --git a/Source/DrangPlatform/Source/win32/sync/mutex.c b/Source/DrangPlatform/Source/win32/sync/mutex.c index b74d0be..9d0bafd 100644 --- a/Source/DrangPlatform/Source/win32/sync/mutex.c +++ b/Source/DrangPlatform/Source/win32/sync/mutex.c @@ -7,36 +7,9 @@ size_t drang_mutex_size(void) return sizeof(struct drang_mutex); } -int drang_mutex_new(struct drang_mutex **mutex) +size_t drang_mutex_alignment(void) { - struct drang_mutex *m = NULL; - DRANG_BEGIN_TRY(); - - DRANG_CHECK(mutex != NULL, DRANG_EINVAL); - - m = DRANG_TRY_ALLOC_T(struct drang_mutex); - - DRANG_TRY(drang_mutex_init(m)); - - DRANG_RETURN_IN(mutex, m); - - DRANG_CATCH(_) - { - if (m != NULL) { - drang_free(m); - } - } - DRANG_END_TRY(); -} - -void drang_mutex_free(struct drang_mutex *mutex) -{ - if (mutex == NULL) { - return; - } - - drang_mutex_fini(mutex); - drang_free(mutex); + return alignof(struct drang_mutex); } int drang_mutex_init(struct drang_mutex *mutex) diff --git a/Source/DrangPlatform/Source/win32/sync/rwlock.c b/Source/DrangPlatform/Source/win32/sync/rwlock.c index 5e860e8..161de6d 100644 --- a/Source/DrangPlatform/Source/win32/sync/rwlock.c +++ b/Source/DrangPlatform/Source/win32/sync/rwlock.c @@ -7,36 +7,9 @@ size_t drang_rwlock_size(void) return sizeof(struct drang_rwlock); } -int drang_rwlock_new(struct drang_rwlock **rwlock) +size_t drang_rwlock_alignment(void) { - struct drang_rwlock *rw = NULL; - DRANG_BEGIN_TRY(); - - DRANG_CHECK(rwlock != NULL, DRANG_EINVAL); - - rw = DRANG_TRY_ALLOC_T(struct drang_rwlock); - - DRANG_TRY(drang_rwlock_init(rw)); - - DRANG_RETURN_IN(rwlock, rw); - - DRANG_CATCH(_) - { - if (rw != NULL) { - drang_free(rw); - } - } - DRANG_END_TRY(); -} - -void drang_rwlock_free(struct drang_rwlock *rwlock) -{ - if (rwlock == NULL) { - return; - } - - drang_rwlock_fini(rwlock); - drang_free(rwlock); + return alignof(struct drang_rwlock); } int drang_rwlock_init(struct drang_rwlock *rwlock)