diff --git a/core/Makefile b/core/Makefile index e3269d8f..8aa82a56 100644 --- a/core/Makefile +++ b/core/Makefile @@ -13,7 +13,8 @@ OBJS = cog-utils.o \ anomap.o \ sha1.o \ threadpool.o \ - queriec.o + queriec.o \ + cthreads.o WFLAGS = -Wall -Wextra -Wpedantic CFLAGS += -std=c99 -pthread -D_XOPEN_SOURCE=600 -DLOG_USE_COLOR \ diff --git a/core/cthreads.c b/core/cthreads.c new file mode 100644 index 00000000..1c0c85b6 --- /dev/null +++ b/core/cthreads.c @@ -0,0 +1,424 @@ +/* + Licensed under PerformanC's Custom license. + Available at licenses/LICENSE.performanc + + https://github.com/PerformanC/CThreads +*/ + +#include +#include +#include + +#include "cthreads.h" + +#ifdef _WIN32 +#include +DWORD WINAPI __cthreads_winthreads_function_wrapper(void *data) { + struct cthreads_args *args = data; + args->func(args->data); + + return TRUE; +} +#else +#include +#endif + +int cthreads_thread_create(struct cthreads_thread *thread, struct cthreads_thread_attr *attr, void *(*func)(void *data), void *data, struct cthreads_args *args) { + #ifdef _WIN32 + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_create\n"); + #endif + + args->func = func; + args->data = data; + + if (attr) thread->wThread = CreateThread(NULL, attr->stacksize ? attr->stacksize : 0, + __cthreads_winthreads_function_wrapper, args, + attr->dwCreationFlags ? (DWORD)attr->dwCreationFlags : 0, NULL); + else thread->wThread = CreateThread(NULL, 0, __cthreads_winthreads_function_wrapper, args, 0, NULL); + + return thread->wThread == NULL; + #else + pthread_attr_t pAttr; + + (void) args; + + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_create\n"); + #endif + + if (attr) { + if (pthread_attr_init(&pAttr)) return 1; + if (attr->detachstate) pthread_attr_setdetachstate(&pAttr, attr->detachstate); + if (attr->guardsize) pthread_attr_setguardsize(&pAttr, attr->guardsize); + #ifdef CTHREADS_THREAD_INHERITSCHED + if (attr->inheritsched) pthread_attr_setinheritsched(&pAttr, attr->inheritsched); + #endif + if (attr->schedpolicy) pthread_attr_setschedpolicy(&pAttr, attr->schedpolicy); + if (attr->scope) pthread_attr_setscope(&pAttr, attr->scope); + #ifdef CTHREADS_THREAD_STACK + if (attr->stack) pthread_attr_setstack(&pAttr, attr->stackaddr, attr->stack); + #endif + if (attr->stacksize) pthread_attr_setstacksize(&pAttr, attr->stacksize); + } + + return pthread_create(&thread->pThread, attr ? &pAttr : NULL, func, data); + #endif +} + +int cthreads_thread_detach(struct cthreads_thread thread) { + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_detach\n"); + #endif + + #ifdef _WIN32 + return CloseHandle(thread.wThread); + #else + return pthread_detach(thread.pThread); + #endif +} + +int cthreads_thread_join(struct cthreads_thread thread, void *code) { + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_join\n"); + #endif + + #ifdef _WIN32 + if (WaitForSingleObject(thread.wThread, INFINITE) == WAIT_FAILED) return 0; + + return GetExitCodeThread(thread.wThread, (LPDWORD)&code) == 0; + #else + return pthread_join(thread.pThread, code ? &code : NULL); + #endif +} + +int cthreads_thread_equal(struct cthreads_thread thread1, struct cthreads_thread thread2) { + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_equal\n"); + #endif + + #ifdef _WIN32 + return thread1.wThread == thread2.wThread; + #else + return pthread_equal(thread1.pThread, thread2.pThread); + #endif +} + +struct cthreads_thread cthreads_thread_self(void) { + struct cthreads_thread t; + + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_self\n"); + #endif + + #ifdef _WIN32 + t.wThread = GetCurrentThread(); + #else + t.pThread = pthread_self(); + #endif + + return t; +} + +unsigned long cthreads_thread_id(struct cthreads_thread thread) { + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_id\n"); + #endif + + #ifdef _WIN32 + return GetThreadId(thread.wThread); + #else + return (unsigned long)thread.pThread; + #endif +} + +void cthreads_thread_exit(void *code) { + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_exit\n"); + #endif + + #ifdef _WIN32 + #if defined __WATCOMC__ || _MSC_VER || __DMC__ + ExitThread((DWORD)code); + #else + ExitThread((DWORD)(uintptr_t)code); + #endif + #else + pthread_exit(code); + #endif +} + +#ifdef CTHREADS_MUTEX_ATTR + int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr *attr) { +#else + int cthreads_mutex_init(struct cthreads_mutex *mutex, void *attr) { +#endif + #ifdef _WIN32 + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_init\n"); + #endif + + (void) attr; + + InitializeCriticalSection(&mutex->wMutex); + + return 0; + #else + pthread_mutexattr_t pAttr; + + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_init\n"); + #endif + + /* CTHREADS_MUTEX_ATTR is always available on non-Windows platforms */ + if (attr) { + if (pthread_mutexattr_init(&pAttr)) return 1; + if (attr->pshared) pthread_mutexattr_setpshared(&pAttr, attr->pshared); + #ifdef CTHREADS_MUTEX_TYPE + if (attr->type) pthread_mutexattr_settype(&pAttr, attr->type); + #endif + #ifdef CTHREADS_MUTEX_ROBUST + if (attr->robust) pthread_mutexattr_setrobust(&pAttr, attr->robust); + #endif + #ifdef CTHREADS_MUTEX_PROTOCOL + if (attr->protocol) pthread_mutexattr_setprotocol(&pAttr, attr->protocol); + #endif + #ifdef CTHREADS_MUTEX_PRIOCEILING + if (attr->prioceiling) pthread_mutexattr_setprioceiling(&pAttr, attr->prioceiling); + #endif + } + + return pthread_mutex_init(&mutex->pMutex, attr ? &pAttr : NULL); + #endif +} + +int cthreads_mutex_lock(struct cthreads_mutex *mutex) { + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_lock\n"); + #endif + + #ifdef _WIN32 + EnterCriticalSection(&mutex->wMutex); + + return 0; + #else + return pthread_mutex_lock(&mutex->pMutex); + #endif +} + +int cthreads_mutex_trylock(struct cthreads_mutex *mutex) { + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_trylock\n"); + #endif + + #ifdef _WIN32 + TryEnterCriticalSection(&mutex->wMutex); + + return 0; + #else + return pthread_mutex_trylock(&mutex->pMutex); + #endif +} + +int cthreads_mutex_unlock(struct cthreads_mutex *mutex) { + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_unlock\n"); + #endif + + #ifdef _WIN32 + LeaveCriticalSection(&mutex->wMutex); + + return 0; + #else + return pthread_mutex_unlock(&mutex->pMutex); + #endif +} + +int cthreads_mutex_destroy(struct cthreads_mutex *mutex) { + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_destroy\n"); + #endif + + #ifdef _WIN32 + DeleteCriticalSection(&mutex->wMutex); + + return 0; + #else + return pthread_mutex_destroy(&mutex->pMutex); + #endif +} + +#ifdef CTHREADS_COND_ATTR + int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *attr) { +#else + int cthreads_cond_init(struct cthreads_cond *cond, void *attr) { +#endif + #ifdef CTHREADS_DEBUG + printf("cthreads_cond_init\n"); + #endif + + #ifdef _WIN32 + (void) attr; + + InitializeConditionVariable(&cond->wCond); + + return 0; + #else + pthread_condattr_t pAttr; + + /* CTHREADS_COND_ATTR is always available on non-Windows platforms */ + if (attr) { + if (pthread_condattr_init(&pAttr) != 0) return 1; + if (attr->pshared) pthread_condattr_setpshared(&pAttr, attr->pshared); + #ifdef CTHREADS_COND_CLOCK + if (attr->clock) pthread_condattr_setclock(&pAttr, attr->clock); + #endif + } + + return pthread_cond_init(&cond->pCond, attr ? &pAttr : NULL); + #endif +} + +int cthreads_cond_signal(struct cthreads_cond *cond) { + #ifdef CTHREADS_DEBUG + printf("cthreads_cond_signal\n"); + #endif + + #ifdef _WIN32 + WakeConditionVariable(&cond->wCond); + + return 0; + #else + return pthread_cond_signal(&cond->pCond); + #endif +} + +int cthreads_cond_broadcast(struct cthreads_cond *cond) { + #ifdef CTHREADS_DEBUG + printf("cthreads_cond_broadcast\n"); + #endif + + #ifdef _WIN32 + WakeAllConditionVariable(&cond->wCond); + + return 0; + #else + return pthread_cond_broadcast(&cond->pCond); + #endif +} + +int cthreads_cond_destroy(struct cthreads_cond *cond) { + #ifdef CTHREADS_DEBUG + printf("cthreads_cond_destroy\n"); + #endif + + #ifdef _WIN32 + return 0; + #else + return pthread_cond_destroy(&cond->pCond); + #endif +} + +int cthreads_cond_wait(struct cthreads_cond *cond, struct cthreads_mutex *mutex) { + #ifdef CTHREADS_DEBUG + printf("cthreads_cond_wait\n"); + #endif + + #ifdef _WIN32 + return SleepConditionVariableCS(&cond->wCond, &mutex->wMutex, INFINITE) == 0; + #else + return pthread_cond_wait(&cond->pCond, &mutex->pMutex); + #endif +} + +#ifdef CTHREADS_RWLOCK + int cthreads_rwlock_init(struct cthreads_rwlock *rwlock) { + #ifdef CTHREADS_DEBUG + printf("cthreads_rwlock_init\n"); + #endif + + #ifdef _WIN32 + rwlock->wRWLock = malloc(sizeof(SRWLOCK)); + if (!rwlock->wRWLock) return 1; + + InitializeSRWLock(rwlock->wRWLock); + + return 0; + #else + return pthread_rwlock_init(&rwlock->pRWLock, NULL); + #endif + } + + int cthreads_rwlock_rdlock(struct cthreads_rwlock *rwlock) { + #ifdef CTHREADS_DEBUG + printf("cthreads_rwlock_rdlock\n"); + #endif + + #ifdef _WIN32 + AcquireSRWLockShared(rwlock->wRWLock); + rwlock->type = 1; + + return 0; + #else + return pthread_rwlock_rdlock(&rwlock->pRWLock); + #endif + } + + int cthreads_rwlock_unlock(struct cthreads_rwlock *rwlock) { + #ifdef CTHREADS_DEBUG + printf("cthreads_rwlock_unlock\n"); + #endif + + #ifdef _WIN32 + switch (rwlock->type) { + case 1: { + ReleaseSRWLockShared(rwlock->wRWLock); + + break; + } + case 2: { + ReleaseSRWLockExclusive(rwlock->wRWLock); + + break; + } + } + + rwlock->type = 0; + + return 0; + #else + return pthread_rwlock_unlock(&rwlock->pRWLock); + #endif + } + + int cthreads_rwlock_wrlock(struct cthreads_rwlock *rwlock) { + #ifdef CTHREADS_DEBUG + printf("cthreads_rwlock_wrlock\n"); + #endif + + #ifdef _WIN32 + AcquireSRWLockExclusive(rwlock->wRWLock); + rwlock->type = 2; + + return 0; + #else + return pthread_rwlock_wrlock(&rwlock->pRWLock); + #endif + } + + int cthreads_rwlock_destroy(struct cthreads_rwlock *rwlock) { + #ifdef CTHREADS_DEBUG + printf("cthreads_rwlock_destroy\n"); + #endif + + #ifdef _WIN32 + free(rwlock->wRWLock); + rwlock->wRWLock = NULL; + rwlock->type = 0; + + return 0; + #else + return pthread_rwlock_destroy(&rwlock->pRWLock); + #endif + } +#endif diff --git a/core/cthreads.h b/core/cthreads.h new file mode 100644 index 00000000..595ae898 --- /dev/null +++ b/core/cthreads.h @@ -0,0 +1,409 @@ +/* + Licensed under PerformanC's Custom license. + Available at licenses/LICENSE.performanc + + https://github.com/PerformanC/CThreads +*/ + +#ifndef CTHREADS_H +#define CTHREADS_H + +struct cthreads_args { + void *(*func)(void *data); + void *data; +}; + +#ifdef _WIN32 + #include +#else + #include +#endif + +#ifdef _WIN32 + #define CTHREADS_THREAD_DWCREATIONFLAGS 1 + + #define CTHREADS_RWLOCK 1 +#else + #define CTHREADS_THREAD_STACKADDR 1 + #define CTHREADS_THREAD_DETACHSTATE 1 + #define CTHREADS_THREAD_GUARDSIZE 1 + #ifndef __ANDROID__ + #define CTHREADS_THREAD_INHERITSCHED 1 + #endif + #define CTHREADS_THREAD_SCHEDPOLICY 1 + #define CTHREADS_THREAD_SCOPE 1 + #if _POSIX_C_SOURCE >= 200112L + #define CTHREADS_THREAD_STACK 1 + #endif + + #define CTHREADS_MUTEX_ATTR 1 + + #define CTHREADS_MUTEX_PSHARED 1 + #if _POSIX_C_SOURCE >= 200809L + #define CTHREADS_MUTEX_TYPE 1 + #endif + #if _POSIX_C_SOURCE >= 200112L + #if (defined __linux__ || defined __FreeBSD__) && !defined __ANDROID__ + #define CTHREADS_MUTEX_ROBUST 1 + #endif + #endif + #ifndef __ANDROID__ + #define CTHREADS_MUTEX_PROTOCOL 1 + #define CTHREADS_MUTEX_PRIOCEILING 1 + #endif + + #define CTHREADS_COND_ATTR 1 + + #define CTHREADS_COND_PSHARED 1 + #if _POSIX_C_SOURCE >= 200112L + #define CTHREADS_COND_CLOCK 1 + #endif + + #if _POSIX_C_SOURCE >= 200112L + #define CTHREADS_RWLOCK 1 + #endif +#endif + +struct cthreads_thread { + #ifdef _WIN32 + HANDLE wThread; + #else + pthread_t pThread; + #endif +}; + +struct cthreads_thread_attr { + size_t stacksize; + #ifdef _WIN32 + int dwCreationFlags; + #else + void *stackaddr; + int detachstate; + size_t guardsize; + int inheritsched; + int schedpolicy; + int scope; + #ifdef CTHREADS_THREAD_STACK + size_t stack; + #endif + #endif +}; + +struct cthreads_mutex { + #ifdef _WIN32 + CRITICAL_SECTION wMutex; + #else + pthread_mutex_t pMutex; + #endif +}; + +#ifdef CTHREADS_MUTEX_ATTR + struct cthreads_mutex_attr { + #ifndef _WIN32 + int pshared; + #ifdef CTHREADS_MUTEX_TYPE + int type; + #endif + #ifdef CTHREADS_MUTEX_ROBUST + int robust; + #endif + #ifdef CTHREADS_MUTEX_PROTOCOL + int protocol; + #endif + #ifdef CTHREADS_MUTEX_PRIOCEILING + int prioceiling; + #endif + #endif + }; +#endif + +struct cthreads_cond { + #ifdef _WIN32 + CONDITION_VARIABLE wCond; + #else + pthread_cond_t pCond; + #endif +}; + +#ifdef CTHREADS_COND_ATTR + struct cthreads_cond_attr { + #ifndef _WIN32 + int pshared; + #ifdef CTHREADS_COND_CLOCK + int clock; + #endif + #endif + }; +#endif + +#ifdef CTHREADS_RWLOCK +struct cthreads_rwlock { + #ifdef _WIN32 + int type; + PSRWLOCK wRWLock; + #else + pthread_rwlock_t pRWLock; + #endif +}; +#endif + +/** + * Creates a new thread. + * + * - pthread: pthread_create + * - windows threads: CreateThread + * + * @param thread Pointer to the thread structure to be filled with the new thread information. + * @param attr Pointer to the thread attributes. Set it to NULL for default attributes. + * @param func Pointer to the function that will be executed in the new thread. + * @param data Pointer to the data that will be passed to the thread function. + * @param args Pointer to the thread arguments. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_thread_create(struct cthreads_thread *thread, struct cthreads_thread_attr *attr, void *(*func)(void *data), void *data, struct cthreads_args *args); + +/** + * Detaches a thread. + * + * - pthread: pthread_detach + * - windows threads: CloseHandle + * + * @param thread Thread structure to be detached. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_thread_detach(struct cthreads_thread thread); + +/** + * Joins a thread. + * + * - pthread: pthread_join + * - windows threads: WaitForSingleObject & GetExitCodeThread + * + * @param thread Pointer to the thread structure to be joined. + * @param code Pointer to store the exit code of the joined thread. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_thread_join(struct cthreads_thread thread, void *code); + +/** + * Compares two thread structures for equality. + * + * - pthread: pthread_equal + * - windows threads: GetCurrentThreadId + * + * @param thread1 First thread structure to compare. + * @param thread2 Second thread structure to compare. + * @return 1 if the threads are equal, zero otherwise. + */ +int cthreads_thread_equal(struct cthreads_thread thread1, struct cthreads_thread thread2); + +/** + * Retrieves the thread struct of the current thread. + * + * - pthread: pthread_self + * - windows threads: GetCurrentThreadId + * + * @return Thread struct of the current thread. + */ +struct cthreads_thread cthreads_thread_self(void); + +/** + * Retrieves the thread identifier of the specified thread. + * + * - pthread: N/A + * - windows threads: GetThreadId + * + * @param thread Thread structure to retrieve the identifier from. + * @return Thread identifier of the specified thread. +*/ +unsigned long cthreads_thread_id(struct cthreads_thread thread); + +/** + * Exits a thread. + * + * - pthread: pthread_exit + * - windows threads: ExitThread + * + * @param code Pointer to the thread exit code. + */ +void cthreads_thread_exit(void *code); + +/** + * Initializes a mutex. + * + * - pthread: pthread_mutex_init + * - windows threads: InitializeCriticalSection + * + * @param mutex Pointer to the mutex structure to be initialized. + * @param attr Pointer to the mutex attributes. Set it to NULL for default attributes. Only available if CTHREADS_MUTEX_ATTR is defined. + * @return 0 on success, non-zero error code on failure. + */ +#ifdef CTHREADS_MUTEX_ATTR + int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr *attr); +#else + int cthreads_mutex_init(struct cthreads_mutex *mutex, void *attr); +#endif + +/** + * Locks a mutex. + * + * - pthread: pthread_mutex_lock + * - windows threads: EnterCriticalSection + * + * @param mutex Pointer to the mutex structure to be locked. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_mutex_lock(struct cthreads_mutex *mutex); + +/** + * Tries to lock a mutex without blocking. + * + * - pthread: pthread_mutex_trylock + * - windows threads: TryEnterCriticalSection + * + * @param mutex Pointer to the mutex structure to be locked. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_mutex_trylock(struct cthreads_mutex *mutex); + +/** + * Unlocks a mutex. + * + * - pthread: pthread_mutex_unlock + * - windows threads: LeaveCriticalSection + * + * @param mutex Pointer to the mutex structure to be unlocked. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_mutex_unlock(struct cthreads_mutex *mutex); + +/** + * Destroys a mutex. + * + * - pthread: pthread_mutex_destroy + * - windows threads: DeleteCriticalSection + * + * @param mutex Pointer to the mutex structure to be destroyed. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_mutex_destroy(struct cthreads_mutex *mutex); + +/** + * Initializes a condition variable. + * + * - pthread: pthread_cond_init + * - windows threads: InitializeConditionVariable + * + * @param cond Pointer to the condition variable structure to be initialized. + * @param attr Pointer to the condition variable attributes. Set it to NULL for default attributes. Only available if CTHREADS_COND_ATTR is defined. + * @return 0 on success, non-zero error code on failure. + */ +#ifdef CTHREADS_COND_ATTR + int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *attr); +#else + int cthreads_cond_init(struct cthreads_cond *cond, void *attr); +#endif + +/** + * Signals a condition variable. + * + * - pthread: pthread_cond_signal + * - windows threads: WakeConditionVariable + * + * @param cond Pointer to the condition variable structure. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_cond_signal(struct cthreads_cond *cond); + +/** + * Broadcasts a condition variable. + * + * - pthread: pthread_cond_broadcast + * - windows threads: WakeAllConditionVariable + * + * @param cond Pointer to the condition variable structure. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_cond_broadcast(struct cthreads_cond *cond); + +/** + * Destroys a condition variable. + * + * - pthread: pthread_cond_destroy + * - windows threads: DeleteConditionVariable + * + * @param cond Pointer to the condition variable structure to be destroyed. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_cond_destroy(struct cthreads_cond *cond); + +/** + * Waits on a condition variable. + * + * - pthread: pthread_cond_wait + * - windows threads: SleepConditionVariableCS + * + * @param cond Pointer to the condition variable structure. + * @param mutex Pointer to the associated mutex structure. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_cond_wait(struct cthreads_cond *cond, struct cthreads_mutex *mutex); + +#ifdef CTHREADS_RWLOCK + /** + * Initializes a read-write lock. + * + * - pthread: pthread_rwlock_init + * - windows threads: InitializeSRWLock + * + * @param rwlock Pointer to the read-write lock structure to be initialized. + * @return 0 on success, non-zero error code on failure. + */ + int cthreads_rwlock_init(struct cthreads_rwlock *rwlock); + + /** + * Acquires a read lock on a read-write lock. + * + * - pthread: pthread_rwlock_rdlock + * - windows threads: AcquireSRWLockShared + * + * @param rwlock Pointer to the read-write lock structure to be locked. + * @return 0 on success, non-zero error code on failure. + */ + int cthreads_rwlock_rdlock(struct cthreads_rwlock *rwlock); + + /** + * Unlocks a read-write lock. + * + * - pthread: pthread_rwlock_unlock + * - windows threads: ReleaseSRWLockShared or ReleaseSRWLockExclusive + * + * @param rwlock Pointer to the read-write lock structure to be unlocked. + * @return 0 on success, non-zero error code on failure. + */ + int cthreads_rwlock_unlock(struct cthreads_rwlock *rwlock); + + /** + * Acquires a write lock on a read-write lock. + * + * - pthread: pthread_rwlock_wrlock + * - windows threads: AcquireSRWLockExclusive + * + * @param rwlock Pointer to the read-write lock structure to be locked. + * @return 0 on success, non-zero error code on failure. + */ + int cthreads_rwlock_wrlock(struct cthreads_rwlock *rwlock); + + /** + * Destroys a read-write lock. + * + * - pthread: pthread_rwlock_destroy + * - windows threads: N/A + * + * @param rwlock Pointer to the read-write lock structure to be destroyed. + * @return 0 on success, non-zero error code on failure. + */ + int cthreads_rwlock_destroy(struct cthreads_rwlock *rwlock); +#endif + +#endif /* CTHREADS_H */ diff --git a/core/threadpool.c b/core/threadpool.c index 1fe3fb08..e3d835d0 100644 --- a/core/threadpool.c +++ b/core/threadpool.c @@ -32,10 +32,9 @@ */ #include -#include -#include #include "threadpool.h" +#include "cthreads.h" typedef enum { immediate_shutdown = 1, @@ -71,9 +70,9 @@ typedef struct { * @var started Number of started threads */ struct threadpool_t { - pthread_mutex_t lock; - pthread_cond_t notify; - pthread_t *threads; + struct cthreads_mutex lock; + struct cthreads_cond notify; + struct cthreads_thread *threads; threadpool_task_t *queue; int thread_count; int queue_size; @@ -96,6 +95,7 @@ int threadpool_free(threadpool_t *pool); threadpool_t *threadpool_create(int thread_count, int queue_size, int flags) { threadpool_t *pool; + struct cthreads_args targs; int i; (void) flags; @@ -114,22 +114,24 @@ threadpool_t *threadpool_create(int thread_count, int queue_size, int flags) pool->shutdown = pool->started = 0; /* Allocate thread and task queue */ - pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * thread_count); + pool->threads = (struct cthreads_thread *)malloc(sizeof(struct cthreads_thread) * thread_count); pool->queue = (threadpool_task_t *)malloc (sizeof(threadpool_task_t) * queue_size); + /* Initialize mutex and conditional variable first */ - if((pthread_mutex_init(&(pool->lock), NULL) != 0) || - (pthread_cond_init(&(pool->notify), NULL) != 0) || + if((cthreads_mutex_init(&(pool->lock), NULL) != 0) || + (cthreads_cond_init(&(pool->notify), NULL) != 0) || (pool->threads == NULL) || (pool->queue == NULL)) { goto err; } + /* Start worker threads */ for(i = 0; i < thread_count; i++) { - if(pthread_create(&(pool->threads[i]), NULL, - threadpool_thread, (void*)pool) != 0) { + if(cthreads_thread_create(&(pool->threads[i]), NULL, + threadpool_thread, (void*)pool, &targs) != 0) { threadpool_destroy(pool, 0); return NULL; } @@ -157,7 +159,7 @@ int threadpool_add(threadpool_t *pool, void (*function)(void *), return threadpool_invalid; } - if(pthread_mutex_lock(&(pool->lock)) != 0) { + if(cthreads_mutex_lock(&(pool->lock)) != 0) { return threadpool_lock_failure; } @@ -182,14 +184,14 @@ int threadpool_add(threadpool_t *pool, void (*function)(void *), pool->tail = next; pool->count += 1; - /* pthread_cond_broadcast */ - if(pthread_cond_signal(&(pool->notify)) != 0) { + /* cthreads_cond_broadcast */ + if(cthreads_cond_signal(&(pool->notify)) != 0) { err = threadpool_lock_failure; break; } } while(0); - if(pthread_mutex_unlock(&pool->lock) != 0) { + if(cthreads_mutex_unlock(&pool->lock) != 0) { err = threadpool_lock_failure; } @@ -204,7 +206,7 @@ int threadpool_destroy(threadpool_t *pool, int flags) return threadpool_invalid; } - if(pthread_mutex_lock(&(pool->lock)) != 0) { + if(cthreads_mutex_lock(&(pool->lock)) != 0) { return threadpool_lock_failure; } @@ -219,15 +221,15 @@ int threadpool_destroy(threadpool_t *pool, int flags) graceful_shutdown : immediate_shutdown; /* Wake up all worker threads */ - if((pthread_cond_broadcast(&(pool->notify)) != 0) || - (pthread_mutex_unlock(&(pool->lock)) != 0)) { + if((cthreads_cond_broadcast(&(pool->notify)) != 0) || + (cthreads_mutex_unlock(&(pool->lock)) != 0)) { err = threadpool_lock_failure; break; } /* Join all worker thread */ for(i = 0; i < pool->thread_count; i++) { - if(pthread_join(pool->threads[i], NULL) != 0) { + if(cthreads_thread_join(pool->threads[i], NULL) != 0) { err = threadpool_thread_failure; } } @@ -254,9 +256,9 @@ int threadpool_free(threadpool_t *pool) /* Because we allocate pool->threads after initializing the mutex and condition variable, we're sure they're initialized. Let's lock the mutex just in case. */ - pthread_mutex_lock(&(pool->lock)); - pthread_mutex_destroy(&(pool->lock)); - pthread_cond_destroy(&(pool->notify)); + cthreads_mutex_lock(&(pool->lock)); + cthreads_mutex_destroy(&(pool->lock)); + cthreads_cond_destroy(&(pool->notify)); } free(pool); return 0; @@ -270,12 +272,12 @@ static void *threadpool_thread(void *threadpool) for(;;) { /* Lock must be taken to wait on conditional variable */ - pthread_mutex_lock(&(pool->lock)); + cthreads_mutex_lock(&(pool->lock)); /* Wait on condition variable, check for spurious wakeups. - When returning from pthread_cond_wait(), we own the lock. */ + When returning from cthreads_cond_wait(), we own the lock. */ while((pool->count == 0) && (!pool->shutdown)) { - pthread_cond_wait(&(pool->notify), &(pool->lock)); + cthreads_cond_wait(&(pool->notify), &(pool->lock)); } if((pool->shutdown == immediate_shutdown) || @@ -291,7 +293,7 @@ static void *threadpool_thread(void *threadpool) pool->count -= 1; /* Unlock */ - pthread_mutex_unlock(&(pool->lock)); + cthreads_mutex_unlock(&(pool->lock)); /* Get to work */ (*(task.function))(task.argument); @@ -299,7 +301,7 @@ static void *threadpool_thread(void *threadpool) pool->started--; - pthread_mutex_unlock(&(pool->lock)); - pthread_exit(NULL); + cthreads_mutex_unlock(&(pool->lock)); + cthreads_thread_exit(NULL); return(NULL); -} +} \ No newline at end of file diff --git a/core/user-agent.c b/core/user-agent.c index 0c271a87..9691feda 100644 --- a/core/user-agent.c +++ b/core/user-agent.c @@ -5,11 +5,11 @@ #include /* isspace() */ #include #include -#include #include "user-agent.h" #include "cog-utils.h" #include "queue.h" +#include "cthreads.h" #define CURLE_LOG(conn, ecode) \ logconf_fatal(&conn->ua->conf, "(CURLE code: %d) %s", ecode, \ @@ -51,7 +51,7 @@ struct ua_conn_queue { /** total amount of created connection handles */ int total; /** lock for blocking queue operations */ - pthread_mutex_t lock; + struct cthreads_mutex lock; }; struct ua_conn { @@ -474,7 +474,7 @@ ua_conn_start(struct user_agent *ua) QUEUE(struct ua_conn) *qelem = NULL; struct ua_conn *conn = NULL; - pthread_mutex_lock(&ua->connq->lock); + cthreads_mutex_lock(&ua->connq->lock); if (QUEUE_EMPTY(&ua->connq->idle)) { conn = _ua_conn_init(ua); @@ -489,7 +489,7 @@ ua_conn_start(struct user_agent *ua) } QUEUE_INSERT_TAIL(&ua->connq->busy, &conn->entry); - pthread_mutex_unlock(&ua->connq->lock); + cthreads_mutex_unlock(&ua->connq->lock); return conn; } @@ -550,10 +550,10 @@ ua_conn_stop(struct ua_conn *conn) } /* move conn from 'busy' to 'idle' queue */ - pthread_mutex_lock(&ua->connq->lock); + cthreads_mutex_lock(&ua->connq->lock); QUEUE_REMOVE(&conn->entry); QUEUE_INSERT_TAIL(&ua->connq->idle, &conn->entry); - pthread_mutex_unlock(&ua->connq->lock); + cthreads_mutex_unlock(&ua->connq->lock); } struct user_agent * @@ -567,7 +567,7 @@ ua_init(struct ua_attr *attr) QUEUE_INIT(&new_ua->connq->idle); QUEUE_INIT(&new_ua->connq->busy); - if (pthread_mutex_init(&new_ua->connq->lock, NULL)) { + if (cthreads_mutex_init(&new_ua->connq->lock, NULL)) { logconf_fatal(&new_ua->conf, "Couldn't initialize mutex"); abort(); } @@ -593,7 +593,7 @@ ua_cleanup(struct user_agent *ua) _ua_conn_cleanup(conn); } } - pthread_mutex_destroy(&ua->connq->lock); + cthreads_mutex_destroy(&ua->connq->lock); free(ua->connq); /* cleanup logging module */ diff --git a/core/websockets.c b/core/websockets.c index 502d61f4..3574e015 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -1,12 +1,12 @@ #include #include #include -#include #include "curl-websocket.h" #include "websockets.h" #include "cog-utils.h" +#include "cthreads.h" #define CURLM_LOG(ws, mcode) \ logconf_fatal(&ws->conf, "(CURLM code: %d) %s", mcode, \ @@ -48,9 +48,13 @@ struct websockets { */ char errbuf[CURL_ERROR_SIZE]; /** lock for functions that may be called in other threads */ - pthread_mutex_t lock; + struct cthreads_mutex lock; /** lock for reading/writing the event-loop timestamp */ - pthread_rwlock_t rwlock; + #ifdef CTHREADS_RWLOCK + struct cthreads_rwlock rwlock; + #else + #error "pthread_rwlock functions are not available on this system." + #endif /** * user-triggered actions @@ -207,9 +211,9 @@ _ws_set_status_nolock(struct websockets *ws, enum ws_status status) static void _ws_set_status(struct websockets *ws, enum ws_status status) { - pthread_mutex_lock(&ws->lock); + cthreads_mutex_lock(&ws->lock); _ws_set_status_nolock(ws, status); - pthread_mutex_unlock(&ws->lock); + cthreads_mutex_unlock(&ws->lock); } static void @@ -371,7 +375,7 @@ _ws_check_action_cb(void *p_userdata, (void)ultotal; (void)ulnow; - pthread_mutex_lock(&ws->lock); + cthreads_mutex_lock(&ws->lock); switch (ws->action) { case WS_ACTION_BEGIN_CLOSE: logconf_warn(&ws->conf, @@ -390,7 +394,7 @@ _ws_check_action_cb(void *p_userdata, break; } ws->action = WS_ACTION_NONE; - pthread_mutex_unlock(&ws->lock); + cthreads_mutex_unlock(&ws->lock); return ret; } @@ -484,9 +488,9 @@ ws_get_status(struct websockets *ws) { enum ws_status status; - pthread_mutex_lock(&ws->lock); + cthreads_mutex_lock(&ws->lock); status = ws->status; - pthread_mutex_unlock(&ws->lock); + cthreads_mutex_unlock(&ws->lock); return status; } @@ -522,10 +526,10 @@ ws_init(struct ws_callbacks *cbs, CURLM *mhandle, struct ws_attr *attr) /** respond ping with a pong by default */ if (!new_ws->cbs.on_ping) new_ws->cbs.on_ping = &default_on_ping; - if (pthread_mutex_init(&new_ws->lock, NULL)) - ERR("[%s] Couldn't initialize pthread mutex", new_ws->conf.id); - if (pthread_rwlock_init(&new_ws->rwlock, NULL)) - ERR("[%s] Couldn't initialize pthread rwlock", new_ws->conf.id); + if (cthreads_mutex_init(&new_ws->lock, NULL)) + ERR("[%s] Couldn't initialize cthreads mutex", new_ws->conf.id); + if (cthreads_rwlock_init(&new_ws->rwlock)) + ERR("[%s] Couldn't initialize cthreads rwlock", new_ws->conf.id); return new_ws; } @@ -537,7 +541,7 @@ ws_set_url(struct websockets *ws, { size_t len; - pthread_mutex_lock(&ws->lock); + cthreads_mutex_lock(&ws->lock); if (!*ws->base_url) { logconf_info(&ws->conf, "Websockets new URL: %s", base_url); @@ -559,15 +563,15 @@ ws_set_url(struct websockets *ws, "[%s] Out of bounds write attempt", ws->conf.id); } - pthread_mutex_unlock(&ws->lock); + cthreads_mutex_unlock(&ws->lock); } void ws_cleanup(struct websockets *ws) { if (ws->ehandle) cws_free(ws->ehandle); - pthread_mutex_destroy(&ws->lock); - pthread_rwlock_destroy(&ws->rwlock); + cthreads_mutex_destroy(&ws->lock); + cthreads_rwlock_destroy(&ws->rwlock); free(ws); } @@ -846,9 +850,9 @@ ws_timestamp(struct websockets *ws) { uint64_t now_tstamp; - pthread_rwlock_rdlock(&ws->rwlock); + cthreads_rwlock_rdlock(&ws->rwlock); now_tstamp = ws->now_tstamp; - pthread_rwlock_unlock(&ws->rwlock); + cthreads_rwlock_unlock(&ws->rwlock); return now_tstamp; } @@ -858,9 +862,9 @@ ws_timestamp_update(struct websockets *ws) { uint64_t now_tstamp; - pthread_rwlock_wrlock(&ws->rwlock); + cthreads_rwlock_wrlock(&ws->rwlock); now_tstamp = ws->now_tstamp = cog_timestamp_ms(); - pthread_rwlock_unlock(&ws->rwlock); + cthreads_rwlock_unlock(&ws->rwlock); return now_tstamp; } @@ -882,14 +886,14 @@ ws_close(struct websockets *ws, "Attempting to close WebSockets connection with %s : %.*s", ws_close_opcode_print(code), (int)len, reason); - pthread_mutex_lock(&ws->lock); + cthreads_mutex_lock(&ws->lock); ws->action = WS_ACTION_BEGIN_CLOSE; ws->pending_close.code = code; snprintf(ws->pending_close.reason, sizeof(ws->pending_close.reason), "%.*s", (int)len, reason); - pthread_mutex_unlock(&ws->lock); + cthreads_mutex_unlock(&ws->lock); } void diff --git a/examples/slash-commands2.c b/examples/slash-commands2.c index 74e9556d..0539b93e 100644 --- a/examples/slash-commands2.c +++ b/examples/slash-commands2.c @@ -2,12 +2,12 @@ #include #include #include -#include #include #include #include /* SCNu64 */ #include "discord.h" +#include "cthreads.h" #include "log.h" u64snowflake g_app_id; @@ -80,7 +80,7 @@ read_input(void *p_client) char cmd_action[9 + 1]; CCORDcode code; - pthread_detach(pthread_self()); + cthreads_thread_detach(cthreads_thread_self()); while (1) { memset(buf, 0, sizeof(buf)); @@ -254,7 +254,7 @@ read_input(void *p_client) print_help(); } - pthread_exit(NULL); + cthreads_thread_exit(NULL); } int @@ -284,8 +284,9 @@ main(int argc, char *argv[]) fscanf(stdin, "%" SCNu64, &g_app_id); } while (!g_app_id || errno == ERANGE); - pthread_t tid; - pthread_create(&tid, NULL, &read_input, client); + struct cthreads_thread tid; + struct cthreads_args args; + cthreads_thread_create(&tid, NULL, read_input, client, &args); discord_run(client); diff --git a/include/discord-internal.h b/include/discord-internal.h index 7f37fb8b..6a986382 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -28,6 +28,7 @@ extern "C" { #include "io_poller.h" #include "queue.h" #include "priority_queue.h" +#include "cthreads.h" #include "attributes.h" /** @brief Return 1 if string isn't considered empty */ @@ -93,12 +94,12 @@ struct discord_timers { struct io_poller *io; struct { bool is_active; - pthread_t thread; + struct cthreads_thread thread; struct discord_timer *timer; bool skip_update_phase; } active; - pthread_mutex_t lock; - pthread_cond_t cond; + struct cthreads_mutex lock; + struct cthreads_cond cond; }; /** @@ -463,7 +464,7 @@ struct discord_request { /** current retry attempt (stop at rest->retry_limit) */ int retry_attempt; /** synchronize synchronous requests */ - pthread_cond_t *cond; + struct cthreads_cond *cond; /** entry for @ref discord_ratelimiter and @ref discord_bucket queues */ QUEUE entry; }; @@ -498,11 +499,11 @@ struct discord_requestor { /** queue locks */ struct { /** recycling queue lock */ - pthread_mutex_t recycling; + struct cthreads_mutex recycling; /** pending queue lock */ - pthread_mutex_t pending; + struct cthreads_mutex pending; /** finished queue lock */ - pthread_mutex_t finished; + struct cthreads_mutex finished; } * qlocks; }; @@ -784,7 +785,11 @@ struct discord_gateway { */ int ping_ms; /** ping rwlock */ - pthread_rwlock_t rwlock; + #ifdef CTHREADS_RWLOCK + struct cthreads_rwlock rwlock; + #else + #error "pthread_rwlock functions are not available on this system." + #endif } * timer; /** the identify structure for client authentication */ @@ -953,7 +958,7 @@ struct discord_refcounter { */ struct _discord_ref *refs; /** global lock */ - pthread_mutex_t *g_lock; + struct cthreads_mutex *g_lock; }; /** @@ -1241,9 +1246,9 @@ struct discord { /** amount of worker-threads currently being used by client */ int count; /** synchronize `count` between workers */ - pthread_mutex_t lock; + struct cthreads_mutex lock; /** notify of `count` decrement */ - pthread_cond_t cond; + struct cthreads_cond cond; } * workers; #ifdef CCORD_VOICE diff --git a/licenses/LICENSE.performanc b/licenses/LICENSE.performanc index 9fc31120..bf86410c 100644 --- a/licenses/LICENSE.performanc +++ b/licenses/LICENSE.performanc @@ -1,13 +1,24 @@ -Custom PerformanC License +BSD 2-Clause License -Copyright (c) 2023 PerformanC +Copyright (c) 2024, The PerformanC Organization -This Software may be shared, altered, and used without charge; -it may also be sold (though not as a stand-alone product); -and it can even be used for commercial purposes. -However, the software code may not be used to train a neural network. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -The license can be included at the source code of the PerformanC software, although it is not required. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -The Software is given "as is" and without any warranties, -and its developers disclaim all liability for any harm it (The Software) may cause. \ No newline at end of file +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index c22ebf14..6192844e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,7 +20,9 @@ CORE_OBJS = $(CORE_DIR)/cog-utils.o \ $(CORE_DIR)/anomap.o \ $(CORE_DIR)/sha1.o \ $(CORE_DIR)/threadpool.o \ - $(CORE_DIR)/queriec.o + $(CORE_DIR)/queriec.o \ + $(CORE_DIR)/cthreads.o + GENCODECS_OBJ = $(GENCODECS_DIR)/discord_codecs.o VOICE_OBJS = discord-voice.o diff --git a/src/concord-once.c b/src/concord-once.c index f9621c11..d50e1bf0 100644 --- a/src/concord-once.c +++ b/src/concord-once.c @@ -1,15 +1,15 @@ #include #include #include -#include #include #include #include #include "error.h" #include "discord-worker.h" +#include "cthreads.h" -static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static struct cthreads_mutex lock; static int shutdown_fds[2] = { -1, @@ -53,7 +53,9 @@ _ccord_sigint_handler(int signum) CCORDcode ccord_global_init() { - pthread_mutex_lock(&lock); + cthreads_mutex_init(&lock, NULL); + + cthreads_mutex_lock(&lock); if (0 == init_counter++) { #ifdef CCORD_SIGINTCATCH signal(SIGINT, &_ccord_sigint_handler); @@ -83,7 +85,7 @@ ccord_global_init() } } } - pthread_mutex_unlock(&lock); + cthreads_mutex_unlock(&lock); return CCORD_OK; fail_pipe_init: @@ -99,14 +101,14 @@ ccord_global_init() curl_global_cleanup(); init_counter = 0; - pthread_mutex_unlock(&lock); + cthreads_mutex_unlock(&lock); return CCORD_GLOBAL_INIT; } void ccord_global_cleanup() { - pthread_mutex_lock(&lock); + cthreads_mutex_lock(&lock); if (init_counter && 0 == --init_counter) { curl_global_cleanup(); discord_worker_global_cleanup(); @@ -115,7 +117,7 @@ ccord_global_cleanup() shutdown_fds[i] = -1; } } - pthread_mutex_unlock(&lock); + cthreads_mutex_unlock(&lock); } int @@ -130,5 +132,8 @@ discord_dup_shutdown_fd(void) fd = -1; } } + + cthreads_mutex_destroy(&lock); + return fd; } diff --git a/src/discord-cache.c b/src/discord-cache.c index 5661af4d..fa3a914b 100644 --- a/src/discord-cache.c +++ b/src/discord-cache.c @@ -1,4 +1,3 @@ -#include #include #include "discord.h" @@ -16,7 +15,7 @@ _calculate_shard(u64snowflake guild_id, int total_shards) } struct _discord_shard_cache { - pthread_mutex_t lock; + struct cthreads_mutex lock; bool valid; struct anomap *guild_map; struct anomap *msg_map; @@ -34,10 +33,10 @@ _discord_shard_cache_cleanup(struct discord *client, struct _discord_shard_cache *cache) { (void)client; - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); anomap_clear(cache->guild_map); anomap_clear(cache->msg_map); - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); } #define EV_CB(name, data) \ @@ -47,18 +46,18 @@ _discord_shard_cache_cleanup(struct discord *client, struct _discord_cache_data *const DATA = client->cache.data; \ const int SHARD = _calculate_shard(GUILD_ID, DATA->total_shards); \ struct _discord_shard_cache *const cache = &data->caches[SHARD]; \ - pthread_mutex_lock(&CACHE->lock) + cthreads_mutex_lock(&CACHE->lock) -#define CACHE_END(CACHE) pthread_mutex_unlock(&CACHE->lock) +#define CACHE_END(CACHE) cthreads_mutex_unlock(&CACHE->lock) EV_CB(ready, discord_ready) { int shard = ev->shard ? ev->shard->array[0] : 0; struct _discord_cache_data *data = client->cache.data; struct _discord_shard_cache *cache = &data->caches[shard]; - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); cache->valid = true; - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); } static void @@ -67,9 +66,9 @@ _on_shard_resumed(struct discord *client, const struct discord_identify *ev) int shard = ev->shard ? ev->shard->array[0] : 0; struct _discord_cache_data *data = client->cache.data; struct _discord_shard_cache *cache = &data->caches[shard]; - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); cache->valid = true; - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); } static void @@ -80,9 +79,9 @@ _on_shard_reconnected(struct discord *client, struct _discord_cache_data *data = client->cache.data; struct _discord_shard_cache *cache = &data->caches[shard]; _discord_shard_cache_cleanup(client, cache); - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); cache->valid = true; - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); } static void @@ -94,9 +93,9 @@ _on_shard_disconnected(struct discord *client, struct _discord_cache_data *data = client->cache.data; struct _discord_shard_cache *cache = &data->caches[shard]; if (!resumable) _discord_shard_cache_cleanup(client, cache); - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); cache->valid = false; - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); } #define GUILD_BEGIN(guild) \ @@ -180,7 +179,7 @@ _on_garbage_collection(struct discord *client, struct discord_timer *timer) struct _discord_cache_data *data = timer->data; for (int i = 0; i < data->total_shards; i++) { struct _discord_shard_cache *const cache = &data->caches[i]; - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); { // DELETE MESSAGES u64snowflake delete_before = ((cog_timestamp_ms() - DISCORD_EPOCH) - 10 * 60 * 1000) << 22; @@ -188,7 +187,7 @@ _on_garbage_collection(struct discord *client, struct discord_timer *timer) anomap_index_of(cache->msg_map, &delete_before, &idx); if (idx--) anomap_delete_range(cache->msg_map, 0, idx, NULL, NULL); } // !DELETE MESSAGES - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); } timer->repeat = 1; timer->interval = 1000 * 60; @@ -203,7 +202,7 @@ _discord_cache_cleanup(struct discord *client) _discord_shard_cache_cleanup(client, cache); anomap_destroy(cache->guild_map); anomap_destroy(cache->msg_map); - pthread_mutex_destroy(&cache->lock); + cthreads_mutex_destroy(&cache->lock); } free(data->caches); discord_internal_timer_ctl(client, @@ -249,7 +248,7 @@ discord_cache_enable(struct discord *client, data->caches = calloc(nshards, sizeof *data->caches); for (int i = 0; i < data->total_shards; i++) { struct _discord_shard_cache *cache = &data->caches[i]; - pthread_mutex_init(&cache->lock, NULL); + cthreads_mutex_init(&cache->lock, NULL); const size_t sf_sz = sizeof(u64snowflake); cache->guild_map = anomap_create(sf_sz, sizeof(void *), _cmp_sf); anomap_set_on_item_changed(cache->guild_map, _on_guild_map_changed, @@ -299,13 +298,13 @@ discord_cache_get_channel_message(struct discord *client, for (int i = 0; i < data->total_shards; i++) { struct _discord_shard_cache *cache = &data->caches[i]; struct discord_message *message = NULL; - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); anomap_do(cache->msg_map, anomap_getval, &message_id, &message); const bool found = message; const bool valid = cache->valid; if (found && message->channel_id != channel_id) message = NULL; if (message && valid) (void)discord_claim(client, message); - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); if (found) return valid ? message : NULL; } return NULL; @@ -319,11 +318,11 @@ discord_cache_get_guild(struct discord *client, u64snowflake guild_id) struct _discord_shard_cache *cache = &data->caches[_calculate_shard(guild_id, data->total_shards)]; struct discord_guild *guild = NULL; - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); anomap_do(cache->guild_map, anomap_getval, &guild_id, &guild); const bool valid = cache->valid; if (guild && valid) (void)discord_claim(client, guild); - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); if (guild && valid) return guild; return NULL; } diff --git a/src/discord-client.c b/src/discord-client.c index 0df74241..c581f5e8 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -74,9 +74,9 @@ _discord_init(struct discord *new_client) IO_POLLER_IN, _on_shutdown_triggered, new_client); new_client->workers = calloc(1, sizeof *new_client->workers); - ASSERT_S(!pthread_mutex_init(&new_client->workers->lock, NULL), + ASSERT_S(!cthreads_mutex_init(&new_client->workers->lock, NULL), "Couldn't initialize Client's mutex"); - ASSERT_S(!pthread_cond_init(&new_client->workers->cond, NULL), + ASSERT_S(!cthreads_cond_init(&new_client->workers->cond, NULL), "Couldn't initialize Client's cond"); discord_refcounter_init(&new_client->refcounter, &new_client->conf); @@ -248,8 +248,8 @@ discord_cleanup(struct discord *client) io_poller_destroy(client->io_poller); logconf_cleanup(&client->conf); if (client->token) free(client->token); - pthread_mutex_destroy(&client->workers->lock); - pthread_cond_destroy(&client->workers->cond); + cthreads_mutex_destroy(&client->workers->lock); + cthreads_cond_destroy(&client->workers->cond); free(client->workers); free(client); @@ -346,9 +346,9 @@ discord_get_ping(struct discord *client) { int ping_ms; - pthread_rwlock_rdlock(&client->gw.timer->rwlock); + cthreads_rwlock_rdlock(&client->gw.timer->rwlock); ping_ms = client->gw.timer->ping_ms; - pthread_rwlock_unlock(&client->gw.timer->rwlock); + cthreads_rwlock_unlock(&client->gw.timer->rwlock); return ping_ms; } diff --git a/src/discord-gateway.c b/src/discord-gateway.c index c2b47174..30fa50ce 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -317,10 +317,10 @@ static void _discord_on_heartbeat_ack(struct discord_gateway *gw) { /* get request / response interval in milliseconds */ - pthread_rwlock_wrlock(&gw->timer->rwlock); + cthreads_rwlock_wrlock(&gw->timer->rwlock); gw->timer->ping_ms = (int)(gw->timer->now - gw->timer->hbeat_last); gw->timer->hbeat_acknowledged = true; - pthread_rwlock_unlock(&gw->timer->rwlock); + cthreads_rwlock_unlock(&gw->timer->rwlock); logconf_trace(&gw->conf, "PING: %d ms", gw->timer->ping_ms); } @@ -549,7 +549,7 @@ discord_gateway_init(struct discord_gateway *gw, logconf_branch(&gw->conf, conf, "DISCORD_GATEWAY"); gw->timer = calloc(1, sizeof *gw->timer); - ASSERT_S(!pthread_rwlock_init(&gw->timer->rwlock, NULL), + ASSERT_S(!cthreads_rwlock_init(&gw->timer->rwlock), "Couldn't initialize Gateway's rwlock"); /* mark true to not get reconnected each reconnect */ @@ -593,7 +593,7 @@ discord_gateway_cleanup(struct discord_gateway *gw) curl_multi_cleanup(gw->mhandle); ws_cleanup(gw->ws); /* cleanup timers */ - pthread_rwlock_destroy(&gw->timer->rwlock); + cthreads_rwlock_destroy(&gw->timer->rwlock); free(gw->timer); /* cleanup bot identification */ free(gw->id.properties); diff --git a/src/discord-refcount.c b/src/discord-refcount.c index 4e3dd66b..24038279 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -100,7 +100,7 @@ discord_refcounter_init(struct discord_refcounter *rc, struct logconf *conf) __chash_init(rc, REFCOUNTER_TABLE); rc->g_lock = malloc(sizeof *rc->g_lock); - ASSERT_S(!pthread_mutex_init(rc->g_lock, NULL), + ASSERT_S(!cthreads_mutex_init(rc->g_lock, NULL), "Couldn't initialize refcounter mutex"); } @@ -108,7 +108,7 @@ void discord_refcounter_cleanup(struct discord_refcounter *rc) { __chash_free(rc, REFCOUNTER_TABLE); - pthread_mutex_destroy(rc->g_lock); + cthreads_mutex_destroy(rc->g_lock); free(rc->g_lock); } @@ -180,7 +180,7 @@ discord_refcounter_claim(struct discord_refcounter *rc, const void *data) { CCORDcode code = CCORD_RESOURCE_UNAVAILABLE; - pthread_mutex_lock(rc->g_lock); + cthreads_mutex_lock(rc->g_lock); if (_discord_refcounter_contains(rc, data)) { struct _discord_refvalue *value = _discord_refvalue_find(rc, data); @@ -189,7 +189,7 @@ discord_refcounter_claim(struct discord_refcounter *rc, const void *data) logconf_trace(&rc->conf, "Claiming %p (claims: %d)", data, value->claims); } - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); return code; } @@ -198,7 +198,7 @@ discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) { CCORDcode code = CCORD_RESOURCE_UNAVAILABLE; - pthread_mutex_lock(rc->g_lock); + cthreads_mutex_lock(rc->g_lock); if (_discord_refcounter_contains(rc, data)) { struct _discord_refvalue *value = _discord_refvalue_find(rc, data); @@ -213,7 +213,7 @@ discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) code = _discord_refcounter_decr_no_lock(rc, data); } } - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); return code; } @@ -224,15 +224,16 @@ discord_refcounter_add_internal(struct discord_refcounter *rc, void (*cleanup)(void *data), bool should_free) { - pthread_mutex_lock(rc->g_lock); + cthreads_mutex_lock(rc->g_lock); _discord_refvalue_init(rc, data, &(struct _discord_refvalue){ .expects_client = false, .cleanup.internal = cleanup, .should_free = should_free, }); + logconf_info(&rc->conf, "Adding concord's internal resource %p", data); - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); } void @@ -242,24 +243,25 @@ discord_refcounter_add_client(struct discord_refcounter *rc, void *data), bool should_free) { - pthread_mutex_lock(rc->g_lock); + cthreads_mutex_lock(rc->g_lock); _discord_refvalue_init(rc, data, &(struct _discord_refvalue){ .expects_client = true, .cleanup.client = cleanup, .should_free = should_free, }); + logconf_info(&rc->conf, "Adding user's custom resource %p", data); - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); } CCORDcode discord_refcounter_incr(struct discord_refcounter *rc, void *data) { CCORDcode code; - pthread_mutex_lock(rc->g_lock); + cthreads_mutex_lock(rc->g_lock); code = _discord_refcounter_incr_no_lock(rc, data); - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); return code; } @@ -267,8 +269,8 @@ CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data) { CCORDcode code; - pthread_mutex_lock(rc->g_lock); + cthreads_mutex_lock(rc->g_lock); code = _discord_refcounter_decr_no_lock(rc, data); - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); return code; } diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 69b3567f..1ad3ee75 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -171,9 +171,9 @@ _discord_bucket_cancel_all(struct discord_ratelimiter *rl, if (b->busy_req) discord_request_cancel(rqtor, b->busy_req); /* move pending tranfers to recycling */ - pthread_mutex_lock(&rqtor->qlocks->recycling); + cthreads_mutex_lock(&rqtor->qlocks->recycling); QUEUE_ADD(&rqtor->queues->recycling, &b->queues.next); - pthread_mutex_unlock(&rqtor->qlocks->recycling); + cthreads_mutex_unlock(&rqtor->qlocks->recycling); QUEUE_INIT(&b->queues.next); } diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 8105be93..643a6c76 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -52,11 +52,11 @@ discord_requestor_init(struct discord_requestor *rqtor, QUEUE_INIT(&rqtor->queues->finished); rqtor->qlocks = malloc(sizeof *rqtor->qlocks); - ASSERT_S(!pthread_mutex_init(&rqtor->qlocks->recycling, NULL), + ASSERT_S(!cthreads_mutex_init(&rqtor->qlocks->recycling, NULL), "Couldn't initialize requestor's recycling queue mutex"); - ASSERT_S(!pthread_mutex_init(&rqtor->qlocks->pending, NULL), + ASSERT_S(!cthreads_mutex_init(&rqtor->qlocks->pending, NULL), "Couldn't initialize requestor's pending queue mutex"); - ASSERT_S(!pthread_mutex_init(&rqtor->qlocks->finished, NULL), + ASSERT_S(!cthreads_mutex_init(&rqtor->qlocks->finished, NULL), "Couldn't initialize requestor's finished queue mutex"); rqtor->mhandle = curl_multi_init(); @@ -94,9 +94,9 @@ discord_requestor_cleanup(struct discord_requestor *rqtor) free(rqtor->queues); /* cleanup queue locks */ - pthread_mutex_destroy(&rqtor->qlocks->recycling); - pthread_mutex_destroy(&rqtor->qlocks->pending); - pthread_mutex_destroy(&rqtor->qlocks->finished); + cthreads_mutex_destroy(&rqtor->qlocks->recycling); + cthreads_mutex_destroy(&rqtor->qlocks->pending); + cthreads_mutex_destroy(&rqtor->qlocks->finished); free(rqtor->qlocks); /* cleanup curl's multi handle */ @@ -279,9 +279,9 @@ discord_request_cancel(struct discord_requestor *rqtor, memset(req, 0, sizeof(struct discord_attributes)); QUEUE_REMOVE(&req->entry); - pthread_mutex_lock(&rqtor->qlocks->recycling); + cthreads_mutex_lock(&rqtor->qlocks->recycling); QUEUE_INSERT_TAIL(&rqtor->queues->recycling, &req->entry); - pthread_mutex_unlock(&rqtor->qlocks->recycling); + cthreads_mutex_unlock(&rqtor->qlocks->recycling); } static CCORDcode @@ -314,10 +314,10 @@ _discord_request_dispatch_response(struct discord_requestor *rqtor, void discord_requestor_dispatch_responses(struct discord_requestor *rqtor) { - if (0 == pthread_mutex_trylock(&rqtor->qlocks->finished)) { + if (0 == cthreads_mutex_trylock(&rqtor->qlocks->finished)) { QUEUE(struct discord_request) queue; QUEUE_MOVE(&rqtor->queues->finished, &queue); - pthread_mutex_unlock(&rqtor->qlocks->finished); + cthreads_mutex_unlock(&rqtor->qlocks->finished); if (!QUEUE_EMPTY(&queue)) { struct discord_rest *rest = @@ -433,14 +433,14 @@ discord_requestor_info_read(struct discord_requestor *rqtor) req); if (req->dispatch.sync) { - pthread_mutex_lock(&rqtor->qlocks->pending); - pthread_cond_signal(req->cond); - pthread_mutex_unlock(&rqtor->qlocks->pending); + cthreads_mutex_lock(&rqtor->qlocks->pending); + cthreads_cond_signal(req->cond); + cthreads_mutex_unlock(&rqtor->qlocks->pending); } else { - pthread_mutex_lock(&rqtor->qlocks->finished); + cthreads_mutex_lock(&rqtor->qlocks->finished); QUEUE_INSERT_TAIL(&rqtor->queues->finished, &req->entry); - pthread_mutex_unlock(&rqtor->qlocks->finished); + cthreads_mutex_unlock(&rqtor->qlocks->finished); } } } @@ -500,9 +500,9 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) struct discord_request *req; struct discord_bucket *b; - pthread_mutex_lock(&rqtor->qlocks->pending); + cthreads_mutex_lock(&rqtor->qlocks->pending); QUEUE_MOVE(&rqtor->queues->pending, &queue); - pthread_mutex_unlock(&rqtor->qlocks->pending); + cthreads_mutex_unlock(&rqtor->qlocks->pending); /* match pending requests to their buckets */ while (!QUEUE_EMPTY(&queue)) { @@ -571,7 +571,7 @@ _discord_request_get(struct discord_requestor *rqtor) { struct discord_request *req; - pthread_mutex_lock(&rqtor->qlocks->recycling); + cthreads_mutex_lock(&rqtor->qlocks->recycling); if (QUEUE_EMPTY(&rqtor->queues->recycling)) { /* new request struct */ req = _discord_request_init(); } @@ -582,7 +582,7 @@ _discord_request_get(struct discord_requestor *rqtor) QUEUE_REMOVE(qelem); req = QUEUE_DATA(qelem, struct discord_request, entry); } - pthread_mutex_unlock(&rqtor->qlocks->recycling); + cthreads_mutex_unlock(&rqtor->qlocks->recycling); QUEUE_INIT(&req->entry); @@ -636,19 +636,20 @@ discord_request_begin(struct discord_requestor *rqtor, req->dispatch.cleanup, false); } - pthread_mutex_lock(&rqtor->qlocks->pending); + cthreads_mutex_lock(&rqtor->qlocks->pending); QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); io_poller_wakeup(rest->io_poller); if (!req->dispatch.sync) { - pthread_mutex_unlock(&rqtor->qlocks->pending); + cthreads_mutex_unlock(&rqtor->qlocks->pending); code = CCORD_PENDING; } else { /* wait for request's completion if sync mode is active */ - pthread_cond_t temp_cond = PTHREAD_COND_INITIALIZER; - req->cond = &temp_cond; - pthread_cond_wait(req->cond, &rqtor->qlocks->pending); - req->cond = NULL; - pthread_mutex_unlock(&rqtor->qlocks->pending); + struct cthreads_cond cond; + cthreads_cond_init(&cond, NULL); + req->cond = &cond; + cthreads_cond_wait(req->cond, &rqtor->qlocks->pending); + cthreads_cond_destroy(&cond); + cthreads_mutex_unlock(&rqtor->qlocks->pending); code = _discord_request_dispatch_response(rqtor, req); } return code; diff --git a/src/discord-timer.c b/src/discord-timer.c index 3cda106a..6db21c12 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -32,8 +32,8 @@ discord_timers_init(struct discord_timers *timers, struct io_poller *io) timers->q = priority_queue_create( sizeof(int64_t), sizeof(struct discord_timer), cmp_timers, 0); timers->io = io; - pthread_mutex_init(&timers->lock, NULL); - pthread_cond_init(&timers->cond, NULL); + cthreads_mutex_init(&timers->lock, NULL); + cthreads_cond_init(&timers->cond, NULL); } static void @@ -52,8 +52,8 @@ discord_timers_cleanup(struct discord *client, struct discord_timers *timers) { priority_queue_set_max_capacity(timers->q, 0); discord_timers_cancel_all(client, timers); - pthread_cond_destroy(&timers->cond); - pthread_mutex_destroy(&timers->lock); + cthreads_cond_destroy(&timers->cond); + cthreads_mutex_destroy(&timers->lock); priority_queue_destroy(timers->q); memset(timers, 0, sizeof *timers); } @@ -68,7 +68,7 @@ discord_timers_get_next_trigger(struct discord_timers *const timers[], for (unsigned i = 0; i < n; i++) { int64_t trigger; - if (0 != pthread_mutex_trylock(&timers[i]->lock)) return 0; + if (0 != cthreads_mutex_trylock(&timers[i]->lock)) return 0; if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { if (trigger < 0) goto unlock; @@ -79,7 +79,7 @@ discord_timers_get_next_trigger(struct discord_timers *const timers[], max_time = trigger - now; } unlock: - pthread_mutex_unlock(&timers[i]->lock); + cthreads_mutex_unlock(&timers[i]->lock); } return max_time; } @@ -126,18 +126,18 @@ _discord_timer_ctl_no_lock(struct discord *client, } } -#define LOCK_TIMERS(timers) \ - do { \ - pthread_mutex_lock(&timers.lock); \ - if (timers.active.is_active \ - && !pthread_equal(pthread_self(), timers.active.thread)) \ - pthread_cond_wait(&timers.cond, &timers.lock); \ +#define LOCK_TIMERS(timers) \ + do { \ + cthreads_mutex_lock(&timers.lock); \ + if (timers.active.is_active \ + && !cthreads_thread_equal(cthreads_thread_self(), timers.active.thread)) \ + cthreads_cond_wait(&timers.cond, &timers.lock); \ } while (0); #define UNLOCK_TIMERS(timers) \ do { \ bool should_wakeup = !timers.active.is_active; \ - pthread_mutex_unlock(&timers.lock); \ + cthreads_mutex_unlock(&timers.lock); \ if (should_wakeup) io_poller_wakeup(timers.io); \ } while (0) @@ -157,9 +157,9 @@ _discord_timer_ctl(struct discord *client, if (timer.flags & DISCORD_TIMER_DELETE) { \ priority_queue_del(timers->q, timer.id); \ if (timer.on_status_changed) { \ - pthread_mutex_unlock(&timers->lock); \ + cthreads_mutex_unlock(&timers->lock); \ timer.on_status_changed(client, &timer); \ - pthread_mutex_lock(&timers->lock); \ + cthreads_mutex_lock(&timers->lock); \ } \ timers->active.skip_update_phase = false; \ continue; \ @@ -171,9 +171,9 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) int64_t now = (int64_t)discord_timestamp_us(client); const int64_t start_time = now; - pthread_mutex_lock(&timers->lock); + cthreads_mutex_lock(&timers->lock); timers->active.is_active = true; - timers->active.thread = pthread_self(); + timers->active.thread = cthreads_thread_self(); struct discord_timer timer; timers->active.timer = &timer; @@ -206,9 +206,9 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) enum discord_timer_flags prev_flags = timer.flags; if (cb) { - pthread_mutex_unlock(&timers->lock); + cthreads_mutex_unlock(&timers->lock); cb(client, &timer); - pthread_mutex_lock(&timers->lock); + cthreads_mutex_lock(&timers->lock); } timer.flags &= ~(enum discord_timer_flags)DISCORD_TIMER_TICK; @@ -252,8 +252,8 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) timers->active.is_active = false; timers->active.timer = NULL; - pthread_cond_broadcast(&timers->cond); - pthread_mutex_unlock(&timers->lock); + cthreads_cond_broadcast(&timers->cond); + cthreads_mutex_unlock(&timers->lock); } unsigned diff --git a/src/discord-voice.c b/src/discord-voice.c index cceb2bd6..4a6c8d78 100644 --- a/src/discord-voice.c +++ b/src/discord-voice.c @@ -126,7 +126,7 @@ struct discord_voice { return #code /* TODO: use a per-client lock instead */ -static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER; +struct cthreads_mutex client_lock; static const char * opcode_print(enum discord_voice_opcodes opcode) @@ -643,6 +643,8 @@ discord_voice_join(struct discord *client, bool self_mute, bool self_deaf) { + cthreads_mutex_init(&client_lock, NULL); + struct discord_update_voice_state state = { .guild_id = guild_id, .channel_id = vchannel_id, .self_mute = self_mute, @@ -652,7 +654,7 @@ discord_voice_join(struct discord *client, if (!ws_is_functional(client->gw.ws)) return DISCORD_VOICE_ERROR; - pthread_mutex_lock(&client_lock); + cthreads_mutex_lock(&client_lock); for (int i = 0; i < DISCORD_MAX_VCS; ++i) { if (0 == client->vcs[i].guild_id) { vc = client->vcs + i; @@ -667,7 +669,7 @@ discord_voice_join(struct discord *client, break; } } - pthread_mutex_unlock(&client_lock); + cthreads_mutex_unlock(&client_lock); if (!vc) { logconf_error(&client->conf, @@ -697,7 +699,7 @@ _discord_on_voice_state_update(struct discord *client, { struct discord_voice *vc = NULL; - pthread_mutex_lock(&client_lock); + cthreads_mutex_lock(&client_lock); for (int i = 0; i < DISCORD_MAX_VCS; ++i) { if (event->guild_id == client->vcs[i].guild_id) { vc = client->vcs + i; @@ -714,7 +716,7 @@ _discord_on_voice_state_update(struct discord *client, break; } } - pthread_mutex_unlock(&client_lock); + cthreads_mutex_unlock(&client_lock); if (!vc) { if (event->channel_id) { @@ -821,14 +823,14 @@ _discord_on_voice_server_update(struct discord *client, struct discord_voice *vc = NULL; int len; - pthread_mutex_lock(&client_lock); + cthreads_mutex_lock(&client_lock); for (int i = 0; i < DISCORD_MAX_VCS; ++i) { if (event->guild_id == client->vcs[i].guild_id) { vc = client->vcs + i; break; } } - pthread_mutex_unlock(&client_lock); + cthreads_mutex_unlock(&client_lock); if (!vc) { logconf_fatal(&client->conf, "Couldn't match voice server to client"); @@ -851,15 +853,16 @@ _discord_on_voice_server_update(struct discord *client, ws_close(vc->ws, WS_CLOSE_REASON_NORMAL, reason, sizeof(reason)); } else { - pthread_t tid; + struct cthreads_thread tid; + struct cthreads_args targs; memcpy(vc->token, vc->new_token, sizeof(vc->new_token)); ws_set_url(vc->ws, vc->new_url, NULL); /** TODO: replace with a threadpool */ - if (pthread_create(&tid, NULL, &start_voice_ws_thread, vc)) + if (cthreads_thread_create(&tid, NULL, &start_voice_ws_thread, vc, &targs)) ERR("Couldn't create thread"); - if (pthread_detach(tid)) ERR("Couldn't detach thread"); + if (cthreads_thread_detach(tid)) ERR("Couldn't detach thread"); } } @@ -878,6 +881,7 @@ _discord_voice_cleanup(struct discord_voice *vc) if (vc->ws) ws_cleanup(vc->ws); if (vc->parse.pairs) free(vc->parse.pairs); if (vc->parse.tokens) free(vc->parse.tokens); + cthreads_mutex_destroy(&client_lock); } void diff --git a/src/discord-worker.c b/src/discord-worker.c index d4381a01..67ffb786 100644 --- a/src/discord-worker.c +++ b/src/discord-worker.c @@ -1,6 +1,5 @@ #include #include -#include #include #include "threadpool.h" @@ -57,16 +56,16 @@ _discord_worker_cb(void *p_cxt) { struct discord_worker_context *cxt = p_cxt; - pthread_mutex_lock(&cxt->client->workers->lock); + cthreads_mutex_lock(&cxt->client->workers->lock); ++cxt->client->workers->count; - pthread_mutex_unlock(&cxt->client->workers->lock); + cthreads_mutex_unlock(&cxt->client->workers->lock); cxt->callback(cxt->data); - pthread_mutex_lock(&cxt->client->workers->lock); + cthreads_mutex_lock(&cxt->client->workers->lock); --cxt->client->workers->count; - pthread_cond_signal(&cxt->client->workers->cond); - pthread_mutex_unlock(&cxt->client->workers->lock); + cthreads_cond_signal(&cxt->client->workers->cond); + cthreads_mutex_unlock(&cxt->client->workers->lock); free(cxt); } @@ -87,11 +86,11 @@ discord_worker_add(struct discord *client, CCORDcode discord_worker_join(struct discord *client) { - pthread_mutex_lock(&client->workers->lock); + cthreads_mutex_lock(&client->workers->lock); while (client->workers->count != 0) { - pthread_cond_wait(&client->workers->cond, &client->workers->lock); + cthreads_cond_wait(&client->workers->cond, &client->workers->lock); } - pthread_mutex_unlock(&client->workers->lock); + cthreads_mutex_unlock(&client->workers->lock); return CCORD_OK; } diff --git a/test/Makefile b/test/Makefile index 3a594386..899922d4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -6,7 +6,7 @@ INCLUDE_DIR = $(TOP)/include GENCODECS_DIR = $(TOP)/gencodecs TEST_DISCORD = racecond rest timeout -TEST_CORE = user-agent websockets queriec +TEST_CORE = user-agent websockets queriec cthreads TESTS = $(TEST_DISCORD) $(TEST_GITHUB) $(TEST_CORE) diff --git a/test/cthreads.c b/test/cthreads.c new file mode 100644 index 00000000..90faf4dc --- /dev/null +++ b/test/cthreads.c @@ -0,0 +1,99 @@ +#include +#include + +#include "cthreads.h" +#include "greatest.h" + +void *thread_func(void *data) { + PASS(); + + return NULL; +} + +TEST create_thread() { + struct cthreads_thread thread; + struct cthreads_args args; + int ret; + + ret = cthreads_thread_create(&thread, NULL, thread_func, NULL, &args); + ASSERT_EQ(0, ret); + + ret = cthreads_thread_join(thread, 0); + ASSERT_EQ(0, ret); + + PASS(); +} + +TEST mutexes() { + struct cthreads_mutex mutex; + int ret; + + #ifdef CTHREADS_MUTEX_ATTR + ret = cthreads_mutex_init(&mutex, NULL); + #else + ret = cthreads_mutex_init(&mutex); + #endif + ASSERT_EQ(0, ret); + + ret = cthreads_mutex_lock(&mutex); + ASSERT_EQ(0, ret); + + ret = cthreads_mutex_unlock(&mutex); + ASSERT_EQ(0, ret); + + ret = cthreads_mutex_destroy(&mutex); + ASSERT_EQ(0, ret); + + PASS(); +} + +TEST locks() { + struct cthreads_rwlock rwlock; + int ret; + + ret = cthreads_rwlock_init(&rwlock); + ASSERT_EQ(0, ret); + + ret = cthreads_rwlock_rdlock(&rwlock); + ASSERT_EQ(0, ret); + + ret = cthreads_rwlock_unlock(&rwlock); + ASSERT_EQ(0, ret); + + ret = cthreads_rwlock_wrlock(&rwlock); + ASSERT_EQ(0, ret); + + ret = cthreads_rwlock_destroy(&rwlock); + ASSERT_EQ(0, ret); + + PASS(); +} + +TEST equal() { + struct cthreads_thread thread1; + struct cthreads_thread thread2; + int ret; + + thread1 = cthreads_thread_self(); + thread2 = cthreads_thread_self(); + + ret = cthreads_thread_equal(thread1, thread2); + ASSERT_EQ(1, ret); + + PASS(); +} + +GREATEST_MAIN_DEFS(); + +int main(int argc, char **argv) { + GREATEST_MAIN_BEGIN(); + + RUN_TEST(create_thread); + RUN_TEST(mutexes); + RUN_TEST(locks); + RUN_TEST(equal); + + GREATEST_MAIN_END(); + + return 0; +} \ No newline at end of file