From 3ffcdf00b7132b6d7514a2074b32759013e823c5 Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Mon, 6 Feb 2023 16:54:09 -0300 Subject: [PATCH 1/4] feat(thread): Replace pthread to CThreads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CThreads can also work in Windows using Windows threads, due to this, pthread was replaced by it. Co-authored-by: Lucas Müller Co-authored-by: HackerSmacker --- core/Makefile | 3 +- core/cthreads.c | 246 ++++++++++++++++++++++++++ core/cthreads.h | 328 +++++++++++++++++++++++++++++++++++ core/threadpool.c | 56 +++--- core/user-agent.c | 16 +- core/websockets.c | 46 ++--- include/discord-internal.h | 23 +-- src/Makefile | 3 +- src/discord-cache.c | 43 +++-- src/discord-client.c | 12 +- src/discord-gateway.c | 8 +- src/discord-refcount.c | 28 +-- src/discord-rest_ratelimit.c | 4 +- src/discord-rest_request.c | 53 +++--- src/discord-timer.c | 36 ++-- src/discord-voice.c | 23 +-- src/discord-worker.c | 17 +- test/cthreads.c | 95 ++++++++++ 18 files changed, 857 insertions(+), 183 deletions(-) create mode 100644 core/cthreads.c create mode 100644 core/cthreads.h create mode 100644 test/cthreads.c diff --git a/core/Makefile b/core/Makefile index 043545f99..bfdfccec2 100644 --- a/core/Makefile +++ b/core/Makefile @@ -12,7 +12,8 @@ OBJS = cog-utils.o \ priority_queue.o \ anomap.o \ sha1.o \ - threadpool.o + threadpool.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 000000000..ff7eea1e5 --- /dev/null +++ b/core/cthreads.c @@ -0,0 +1,246 @@ +#include +#include + +#include "cthreads.h" + +#if _WIN32 +#include +DWORD WINAPI __cthreads_win_thread_create_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 + args->func = func; + args->data = data; + + if (attr) thread->wThread = CreateThread(NULL, attr->stacksize ? attr->stacksize : 0, + __cthreads_win_thread_create_wrapper, args, + attr->dwCreationFlags ? (DWORD)attr->dwCreationFlags : 0, NULL); + else thread->wThread = CreateThread(NULL, 0, __cthreads_win_thread_create_wrapper, args, 0, NULL); + + return thread->wThread ? 0 : 1; + #else + pthread_attr_t pAttr; + + 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); + #if !defined __ANDROID__ + 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); + if (attr->stack) pthread_attr_setstack(&pAttr, attr->stackaddr, attr->stack); + 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 _WIN32 + return CloseHandle(thread->wThread); + #else + return pthread_detach(thread->pThread); + #endif +} + +void cthreads_thread_close(void *code) { + #ifdef _WIN32 + #ifdef __WATCOMC__ + ExitThread((DWORD)code); + #else + ExitThread((DWORD)(uintptr_t)code); + #endif + #else + pthread_exit(code); + #endif +} + +int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr *attr) { + #ifdef _WIN32 + return (attr ? mutex->wMutex = CreateMutex(NULL, attr->bInitialOwner ? TRUE : FALSE, + attr->lpName ? (LPCSTR)attr->lpName : NULL); + : CreateMutex(NULL, FALSE, NULL)) ? 0 : 1; + #else + pthread_mutexattr_t pAttr; + if (attr) { + if (pthread_mutexattr_init(&pAttr)) return 1; + if (attr->pshared) pthread_mutexattr_setpshared(&pAttr, attr->pshared); + if (attr->type) pthread_mutexattr_settype(&pAttr, attr->type); + #if (defined __linux__ || defined __FreeBSD__) && !defined __ANDROID__ + if (attr->robust) pthread_mutexattr_setrobust(&pAttr, attr->robust); + #endif + #if !defined __ANDROID__ + if (attr->protocol) pthread_mutexattr_setprotocol(&pAttr, attr->protocol); + 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 _WIN32 + return WaitForSingleObject(mutex->wMutex, INFINITE) == WAIT_OBJECT_0 ? 0 : 1; // Needs to re-see + #else + return pthread_mutex_lock(&mutex->pMutex); + #endif +} + +int cthreads_mutex_trylock(struct cthreads_mutex *mutex) { + #ifdef _WIN32 + return WaitForSingleObject(mutex->wMutex, 0) == WAIT_OBJECT_0 ? 0 : 1; // Needs to re-see + #else + return pthread_mutex_trylock(&mutex->pMutex); + #endif +} + +int cthreads_mutex_unlock(struct cthreads_mutex *mutex) { + #ifdef _WIN32 + return ReleaseMutex(mutex->wMutex) == 0 ? 1 : 0; // Needs to re-see + #else + return pthread_mutex_unlock(&mutex->pMutex); + #endif +} + +int cthreads_mutex_destroy(struct cthreads_mutex *mutex) { + #ifdef _WIN32 + return CloseHandle(mutex->wMutex) == 0 ? 1 : 0; // Needs to re-see + #else + return pthread_mutex_destroy(&mutex->pMutex); + #endif +} + +int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *attr) { + #ifdef _WIN32 + if (attr) cond->wCond = CreateEvent(NULL, attr->bManualReset ? TRUE : FALSE, + attr->bInitialState ? TRUE : FALSE, + attr->lpName ? (LPTSTR)attr->lpName : NULL); + else cond->wCond = CreateEvent(NULL, FALSE, FALSE, NULL); + + if (cond->wCond == NULL) return 1; + return 0; + #else + pthread_condattr_t pAttr; + if (attr) { + if (pthread_condattr_init(&pAttr) != 0) return 1; + if (attr->pshared) pthread_condattr_setpshared(&pAttr, attr->pshared); + if (attr->clock) pthread_condattr_setclock(&pAttr, attr->clock); + } + + return pthread_cond_init(&cond->pCond, attr ? &pAttr : NULL); + #endif +} + +int cthreads_cond_signal(struct cthreads_cond *cond) { + #ifdef _WIN32 + return SetEvent(cond->wCond) == 0 ? 1 : 0; // Needs to re-see + #else + return pthread_cond_signal(&cond->pCond); + #endif +} + +int cthreads_cond_broadcast(struct cthreads_cond *cond) { + #ifdef _WIN32 + return SetEvent(cond->wCond) == 0 ? 1 : 0; // Needs to re-see + #else + return pthread_cond_broadcast(&cond->pCond); + #endif +} + +int cthreads_cond_destroy(struct cthreads_cond *cond) { + #ifdef _WIN32 + return CloseHandle(cond->wCond) == 0 ? 1 : 0; // Needs to re-see + #else + return pthread_cond_destroy(&cond->pCond); + #endif +} + +int cthreads_cond_wait(struct cthreads_cond *cond, struct cthreads_mutex *mutex) { + #ifdef _WIN32 + return SleepConditionVariableCS(&cond->wCond, &mutex->wMutex, INFINITE) == 0 ? 1 : 0; + #else + return pthread_cond_wait(&cond->pCond, &mutex->pMutex); + #endif +} + +int cthreads_join(struct cthreads_thread *thread, void *code) { + #ifdef _WIN32 + if (WaitForSingleObject(thread->wThread, INFINITE) == WAIT_FAILED) return 1; + + return GetExitCodeThread(thread->wThread, (LPDWORD)&code) == 0 ? 1 : 0; + #else + return pthread_join(thread->pThread, code ? &code : NULL); + #endif +} + +int cthreads_rwlock_init(struct cthreads_rwlock *rwlock) { + #ifdef _WIN32 + return (rwlock->wRWLock = CreateMutex(NULL, FALSE, NULL)) == NULL ? 1 : 0; // Needs to re-see + #else + return pthread_rwlock_init(&rwlock->pRWLock, NULL); + #endif +} + +int cthreads_rwlock_rdlock(struct cthreads_rwlock *rwlock) { + #ifdef _WIN32 + return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED ? 1 : 0; // Needs to re-see + #else + return pthread_rwlock_rdlock(&rwlock->pRWLock); + #endif +} + +int cthreads_rwlock_unlock(struct cthreads_rwlock *rwlock) { + #ifdef _WIN32 + return ReleaseMutex(rwlock->wRWLock) == 0 ? 1 : 0; // Needs to re-see + #else + return pthread_rwlock_unlock(&rwlock->pRWLock); + #endif +} + +int cthreads_rwlock_wrlock(struct cthreads_rwlock *rwlock) { + #ifdef _WIN32 + return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED ? 1 : 0; // Needs to re-see + #else + return pthread_rwlock_wrlock(&rwlock->pRWLock); + #endif +} + +int cthreads_rwlock_destroy(struct cthreads_rwlock *rwlock) { + #ifdef _WIN32 + return CloseHandle(rwlock->wRWLock) == 0 ? 1 : 0; // Needs to re-see + #else + return pthread_rwlock_destroy(&rwlock->pRWLock); + #endif +} + +int cthreads_equal(struct cthreads_thread thread1, struct cthreads_thread thread2) { + #ifdef _WIN32 + return GetThreadId(thread1.wThread) == GetThreadId(thread2.wThread); + #else + return pthread_equal(thread1.pThread, thread2.pThread); + #endif +} + +struct cthreads_thread cthreads_self() { + struct cthreads_thread t; + + #ifdef _WIN32 + t.wThread = GetCurrentThread(); + #else + t.pThread = pthread_self(); + #endif + + return t; +} diff --git a/core/cthreads.h b/core/cthreads.h new file mode 100644 index 000000000..514e9db37 --- /dev/null +++ b/core/cthreads.h @@ -0,0 +1,328 @@ +#ifndef CTHREADS_H +#define CTHREADS_H + +struct cthreads_args { + void *(*func)(void *data); + void *data; +}; + +#if _WIN32 + #include +#else + #include +#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; + size_t stack; + #endif +}; + +struct cthreads_mutex { + #ifdef _WIN32 + HANDLE wMutex; + #else + pthread_mutex_t pMutex; + #endif +}; + +struct cthreads_mutex_attr { + #ifdef _WIN32 + int bInitialOwner; + char *lpName; + #else + int pshared; + int type; + #if (defined __linux__ || defined __FreeBSD__) && !defined __ANDROID__ + int robust; + #endif + #if !defined __ANDROID__ + int protocol; + int prioceiling; + #endif + #endif +}; + +struct cthreads_cond { + #ifdef _WIN32 + HANDLE wCond; + #else + pthread_cond_t pCond; + #endif +}; + +struct cthreads_cond_attr { + #ifdef _WIN32 + int bManualReset; + int bInitialState; + char *lpName; + #else + int pshared; + int clock; + #endif +}; + +struct cthreads_rwlock { + #ifdef _WIN32 + HANDLE wRWLock; + #else + pthread_rwlock_t pRWLock; + #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 Pointer to the thread structure to be detached. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_thread_detach(struct cthreads_thread *thread); + +/** + * Closes a thread. + * + * - pthread: pthread_exit + * - windows threads: ExitThread + * + * @param code Pointer to the thread exit code. + */ +void cthreads_thread_close(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. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr *attr); + +/** + * 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. + * @return 0 on success, non-zero error code on failure. + */ +int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *attr); + +/** + * 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); + +/** + * Joins a thread. + * + * - pthread: pthread_join + * - windows threads: WaitForSingleObject + * + * @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_join(struct cthreads_thread *thread, void *code); + +/** + * 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: ReleaseSRWLockExclusive for writer, ReleaseSRWLockShared for reader + * + * @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: DeleteSRWLock + * + * @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); + +/** + * 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 Non-zero if the threads are equal, zero otherwise. + */ +int cthreads_equal(struct cthreads_thread thread1, struct cthreads_thread thread2); + +/** + * Retrieves the thread identifier of the current thread. + * + * - pthread: pthread_self + * - windows threads: GetCurrentThreadId + * + * @return Thread identifier of the current thread. + */ +struct cthreads_thread cthreads_self(); + +#endif /* CTHREADS_H */ diff --git a/core/threadpool.c b/core/threadpool.c index 1fe3fb084..e8fe6e1f6 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,13 +114,13 @@ 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; @@ -128,8 +128,8 @@ threadpool_t *threadpool_create(int thread_count, int queue_size, int flags) /* 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 +157,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 +182,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 +204,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 +219,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_join(&pool->threads[i], NULL) != 0) { err = threadpool_thread_failure; } } @@ -254,9 +254,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 +270,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 +291,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 +299,7 @@ static void *threadpool_thread(void *threadpool) pool->started--; - pthread_mutex_unlock(&(pool->lock)); - pthread_exit(NULL); + cthreads_mutex_unlock(&(pool->lock)); + cthreads_thread_close(NULL); return(NULL); -} +} \ No newline at end of file diff --git a/core/user-agent.c b/core/user-agent.c index b207d5a90..72fb5bd75 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 { @@ -450,7 +450,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); @@ -465,7 +465,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; } @@ -526,10 +526,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 * @@ -543,7 +543,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(); } @@ -569,7 +569,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 982400fbd..5a31e2b09 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,9 @@ 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; + struct cthreads_rwlock rwlock; /** * user-triggered actions @@ -181,9 +181,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 @@ -345,7 +345,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, @@ -364,7 +364,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; } @@ -456,9 +456,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; } @@ -494,10 +494,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; } @@ -509,7 +509,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_debug(&ws->conf, "Websockets new URL: %s", base_url); @@ -529,15 +529,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); } @@ -815,9 +815,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; } @@ -827,9 +827,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; } @@ -844,14 +844,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/include/discord-internal.h b/include/discord-internal.h index 0c580c77a..c256ff8ed 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" /** @brief Return 1 if string isn't considered empty */ #define NOT_EMPTY_STR(str) ((str) && *(str)) @@ -89,12 +90,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; }; /** @@ -459,7 +460,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; }; @@ -494,11 +495,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; }; @@ -775,7 +776,7 @@ struct discord_gateway { */ int ping_ms; /** ping rwlock */ - pthread_rwlock_t rwlock; + struct cthreads_rwlock rwlock; } * timer; /** the identify structure for client authentication */ @@ -944,7 +945,7 @@ struct discord_refcounter { */ struct _discord_ref *refs; /** global lock */ - pthread_mutex_t *g_lock; + struct cthreads_mutex *g_lock; }; /** @@ -1228,9 +1229,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/src/Makefile b/src/Makefile index 5d3777933..0d2ef8a45 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,7 +19,8 @@ CORE_OBJS = $(CORE_DIR)/cog-utils.o \ $(CORE_DIR)/priority_queue.o \ $(CORE_DIR)/anomap.o \ $(CORE_DIR)/sha1.o \ - $(CORE_DIR)/threadpool.o + $(CORE_DIR)/threadpool.o \ + $(CORE_DIR)/cthreads.o GENCODECS_OBJ = $(GENCODECS_DIR)/discord_codecs.o VOICE_OBJS = discord-voice.o diff --git a/src/discord-cache.c b/src/discord-cache.c index 1a8d02b27..cc992227b 100644 --- a/src/discord-cache.c +++ b/src/discord-cache.c @@ -1,4 +1,3 @@ -#include #include #include "discord.h" @@ -21,7 +20,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; @@ -38,7 +37,7 @@ static void _discord_shard_cache_cleanup(struct discord *client, struct _discord_shard_cache *cache) { - pthread_mutex_lock(&cache->lock); + cthreads_mutex_lock(&cache->lock); for (size_t i = 0; i < anomap_length(cache->guild_map); i++) { struct discord_guild *guild; anomap_at_index(cache->guild_map, i, NULL, &guild); @@ -51,7 +50,7 @@ _discord_shard_cache_cleanup(struct discord *client, } 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) \ @@ -61,18 +60,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 @@ -81,9 +80,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 @@ -94,9 +93,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 @@ -108,9 +107,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) \ @@ -206,7 +205,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; @@ -223,7 +222,7 @@ _on_garbage_collection(struct discord *client, struct discord_timer *timer) (void *)vals[j]); } } // !DELETE MESSAGES - pthread_mutex_unlock(&cache->lock); + cthreads_mutex_unlock(&cache->lock); } timer->repeat = 1; timer->interval = 1000 * 60; @@ -238,7 +237,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, @@ -267,7 +266,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); cache->guild_map = anomap_create(sizeof(u64snowflake), sizeof(void *), cmp_sf); cache->msg_map = @@ -314,13 +313,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; @@ -334,11 +333,11 @@ discord_cache_get_guild(struct discord *client, u64snowflake guild_id) for (int i = 0; i < data->total_shards; i++) { struct _discord_shard_cache *cache = &data->caches[i]; 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) return valid ? guild : NULL; } return NULL; diff --git a/src/discord-client.c b/src/discord-client.c index 10a350223..6579dc1b2 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -59,9 +59,9 @@ _discord_init(struct discord *new_client) discord_timers_init(&new_client->timers.user, new_client->io_poller); 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); @@ -229,8 +229,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); @@ -310,9 +310,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 0a8754925..df811165f 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -317,9 +317,9 @@ 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); - pthread_rwlock_unlock(&gw->timer->rwlock); + cthreads_rwlock_unlock(&gw->timer->rwlock); logconf_trace(&gw->conf, "PING: %d ms", gw->timer->ping_ms); } @@ -548,7 +548,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"); /* client connection status */ @@ -589,7 +589,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 8e67cfdd9..67868c2c3 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); } @@ -172,14 +172,14 @@ discord_refcounter_claim(struct discord_refcounter *rc, const void *data) { CCORDcode code = CCORD_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); ++value->claims; code = _discord_refcounter_incr_no_lock(rc, (void *)data); } - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); return code; } @@ -188,7 +188,7 @@ discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) { CCORDcode code = CCORD_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); @@ -200,7 +200,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; } @@ -211,14 +211,14 @@ 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, }); - pthread_mutex_unlock(rc->g_lock); + cthreads_mutex_unlock(rc->g_lock); } void @@ -228,23 +228,23 @@ 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, }); - 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; } @@ -252,8 +252,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 69b3567f2..1ad3ee75e 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 5872a6b82..9ceda1fee 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 */ @@ -278,9 +278,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 @@ -313,10 +313,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 = @@ -432,14 +432,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); } } } @@ -491,9 +491,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)) { @@ -562,7 +562,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(); } @@ -573,7 +573,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); @@ -627,19 +627,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 3cda106a6..c9e8c7d3d 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; } @@ -128,16 +128,16 @@ _discord_timer_ctl_no_lock(struct discord *client, #define LOCK_TIMERS(timers) \ do { \ - pthread_mutex_lock(&timers.lock); \ + cthreads_mutex_lock(&timers.lock); \ if (timers.active.is_active \ - && !pthread_equal(pthread_self(), timers.active.thread)) \ - pthread_cond_wait(&timers.cond, &timers.lock); \ + && !cthreads_equal(cthreads_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_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 cceb2bd69..3d159118e 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"); } } diff --git a/src/discord-worker.c b/src/discord-worker.c index d4381a016..67ffb7860 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/cthreads.c b/test/cthreads.c new file mode 100644 index 000000000..de9a553e7 --- /dev/null +++ b/test/cthreads.c @@ -0,0 +1,95 @@ +#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_join(&thread, 0); + ASSERT_EQ(0, ret); + + PASS(); +} + +TEST mutexes() { + struct cthreads_mutex mutex; + int ret; + + ret = cthreads_mutex_init(&mutex, NULL); + 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_self(); + thread2 = cthreads_self(); + + ret = cthreads_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 From 61e9dc0d5a2d049af1393aed8c71762e8348a11e Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Fri, 26 Jan 2024 18:53:33 -0300 Subject: [PATCH 2/4] update(cthreads): update CThreads library This commit updates CThreads library with latest fixes. --- core/cthreads.c | 374 +++++++++++++++++++----------------- core/cthreads.h | 256 +++++++++++++++--------- core/threadpool.c | 2 +- licenses/LICENSE.performanc | 13 +- src/discord-timer.c | 20 +- src/discord-voice.c | 2 +- 6 files changed, 389 insertions(+), 278 deletions(-) diff --git a/core/cthreads.c b/core/cthreads.c index ff7eea1e5..843d2b98b 100644 --- a/core/cthreads.c +++ b/core/cthreads.c @@ -1,246 +1,274 @@ +/* + Licensed under PerformanC's Custom license. + Available at licenses/LICENSE.performanc + + https://github.com/PerformanC/CThreads +*/ + #include #include #include "cthreads.h" -#if _WIN32 +#ifdef _WIN32 #include -DWORD WINAPI __cthreads_win_thread_create_wrapper(void *data) { - struct cthreads_args *args = data; - args->func(args->data); +DWORD WINAPI __cthreads_winthreads_function_wrapper(void *data) { + struct cthreads_args *args = data; + args->func(args->data); - return TRUE; + 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 - args->func = func; - args->data = data; + #ifdef _WIN32 + 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; + + 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 _WIN32 + return CloseHandle(thread.wThread); + #else + return pthread_detach(thread.pThread); + #endif +} - if (attr) thread->wThread = CreateThread(NULL, attr->stacksize ? attr->stacksize : 0, - __cthreads_win_thread_create_wrapper, args, - attr->dwCreationFlags ? (DWORD)attr->dwCreationFlags : 0, NULL); - else thread->wThread = CreateThread(NULL, 0, __cthreads_win_thread_create_wrapper, args, 0, NULL); +int cthreads_thread_join(struct cthreads_thread thread, void *code) { + #ifdef _WIN32 + if (WaitForSingleObject(thread.wThread, INFINITE) == WAIT_FAILED) return 0; - return thread->wThread ? 0 : 1; - #else - pthread_attr_t pAttr; - - 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); - #if !defined __ANDROID__ - 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); - if (attr->stack) pthread_attr_setstack(&pAttr, attr->stackaddr, attr->stack); - if (attr->stacksize) pthread_attr_setstacksize(&pAttr, attr->stacksize); - } - - return pthread_create(&thread->pThread, attr ? &pAttr : NULL, func, data); - #endif + return GetExitCodeThread(thread.wThread, (LPDWORD)&code) == 0; + #else + return pthread_join(thread.pThread, code ? &code : NULL); + #endif } -int cthreads_thread_detach(struct cthreads_thread *thread) { - #ifdef _WIN32 - return CloseHandle(thread->wThread); - #else - return pthread_detach(thread->pThread); - #endif +int cthreads_thread_equal(struct cthreads_thread thread1, struct cthreads_thread thread2) { + #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 _WIN32 + t.wThread = GetCurrentThread(); + #else + t.pThread = pthread_self(); + #endif + + return t; +} + +unsigned long cthreads_thread_id(struct cthreads_thread thread) { + #ifdef _WIN32 + return GetThreadId(thread.wThread); + #else + return (unsigned long)thread.pThread; + #endif } void cthreads_thread_close(void *code) { - #ifdef _WIN32 - #ifdef __WATCOMC__ - ExitThread((DWORD)code); - #else - ExitThread((DWORD)(uintptr_t)code); - #endif + #ifdef _WIN32 + #ifdef __WATCOMC__ + ExitThread((DWORD)code); #else - pthread_exit(code); + ExitThread((DWORD)(uintptr_t)code); #endif + #else + pthread_exit(code); + #endif } int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr *attr) { - #ifdef _WIN32 - return (attr ? mutex->wMutex = CreateMutex(NULL, attr->bInitialOwner ? TRUE : FALSE, - attr->lpName ? (LPCSTR)attr->lpName : NULL); - : CreateMutex(NULL, FALSE, NULL)) ? 0 : 1; - #else - pthread_mutexattr_t pAttr; - if (attr) { - if (pthread_mutexattr_init(&pAttr)) return 1; - if (attr->pshared) pthread_mutexattr_setpshared(&pAttr, attr->pshared); - if (attr->type) pthread_mutexattr_settype(&pAttr, attr->type); - #if (defined __linux__ || defined __FreeBSD__) && !defined __ANDROID__ - if (attr->robust) pthread_mutexattr_setrobust(&pAttr, attr->robust); - #endif - #if !defined __ANDROID__ - if (attr->protocol) pthread_mutexattr_setprotocol(&pAttr, attr->protocol); - if (attr->prioceiling) pthread_mutexattr_setprioceiling(&pAttr, attr->prioceiling); - #endif - } - - return pthread_mutex_init(&mutex->pMutex, attr ? &pAttr : NULL); - #endif + #ifdef _WIN32 + if (attr) mutex->wMutex = CreateMutex(NULL, attr->bInitialOwner ? TRUE : FALSE, + attr->lpName ? (LPCSTR)attr->lpName : NULL); + else mutex->wMutex = CreateMutex(NULL, FALSE, NULL); + + return mutex->wMutex == NULL; + #else + pthread_mutexattr_t pAttr; + 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 _WIN32 - return WaitForSingleObject(mutex->wMutex, INFINITE) == WAIT_OBJECT_0 ? 0 : 1; // Needs to re-see - #else - return pthread_mutex_lock(&mutex->pMutex); - #endif + #ifdef _WIN32 + return WaitForSingleObject(mutex->wMutex, INFINITE) != WAIT_OBJECT_0; + #else + return pthread_mutex_lock(&mutex->pMutex); + #endif } int cthreads_mutex_trylock(struct cthreads_mutex *mutex) { - #ifdef _WIN32 - return WaitForSingleObject(mutex->wMutex, 0) == WAIT_OBJECT_0 ? 0 : 1; // Needs to re-see - #else - return pthread_mutex_trylock(&mutex->pMutex); - #endif + #ifdef _WIN32 + return WaitForSingleObject(mutex->wMutex, 0) != WAIT_OBJECT_0; + #else + return pthread_mutex_trylock(&mutex->pMutex); + #endif } int cthreads_mutex_unlock(struct cthreads_mutex *mutex) { - #ifdef _WIN32 - return ReleaseMutex(mutex->wMutex) == 0 ? 1 : 0; // Needs to re-see - #else - return pthread_mutex_unlock(&mutex->pMutex); - #endif + #ifdef _WIN32 + return ReleaseMutex(mutex->wMutex) == 0; + #else + return pthread_mutex_unlock(&mutex->pMutex); + #endif } int cthreads_mutex_destroy(struct cthreads_mutex *mutex) { - #ifdef _WIN32 - return CloseHandle(mutex->wMutex) == 0 ? 1 : 0; // Needs to re-see - #else - return pthread_mutex_destroy(&mutex->pMutex); - #endif + #ifdef _WIN32 + return CloseHandle(mutex->wMutex) == 0; + #else + return pthread_mutex_destroy(&mutex->pMutex); + #endif } int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *attr) { - #ifdef _WIN32 - if (attr) cond->wCond = CreateEvent(NULL, attr->bManualReset ? TRUE : FALSE, - attr->bInitialState ? TRUE : FALSE, - attr->lpName ? (LPTSTR)attr->lpName : NULL); - else cond->wCond = CreateEvent(NULL, FALSE, FALSE, NULL); - - if (cond->wCond == NULL) return 1; - return 0; - #else - pthread_condattr_t pAttr; - if (attr) { - if (pthread_condattr_init(&pAttr) != 0) return 1; - if (attr->pshared) pthread_condattr_setpshared(&pAttr, attr->pshared); - if (attr->clock) pthread_condattr_setclock(&pAttr, attr->clock); - } - - return pthread_cond_init(&cond->pCond, attr ? &pAttr : NULL); - #endif + #ifdef _WIN32 + if (attr) cond->wCond = CreateEvent(NULL, attr->bManualReset ? TRUE : FALSE, + attr->bInitialState ? TRUE : FALSE, + attr->lpName ? (LPTSTR)attr->lpName : NULL); + else cond->wCond = CreateEvent(NULL, FALSE, FALSE, NULL); + + return cond->wCond == NULL; + #else + pthread_condattr_t pAttr; + 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 _WIN32 - return SetEvent(cond->wCond) == 0 ? 1 : 0; // Needs to re-see - #else - return pthread_cond_signal(&cond->pCond); - #endif + #ifdef _WIN32 + return SetEvent(cond->wCond) == 0; + #else + return pthread_cond_signal(&cond->pCond); + #endif } int cthreads_cond_broadcast(struct cthreads_cond *cond) { - #ifdef _WIN32 - return SetEvent(cond->wCond) == 0 ? 1 : 0; // Needs to re-see - #else - return pthread_cond_broadcast(&cond->pCond); - #endif + #ifdef _WIN32 + return SetEvent(cond->wCond) == 0; + #else + return pthread_cond_broadcast(&cond->pCond); + #endif } int cthreads_cond_destroy(struct cthreads_cond *cond) { - #ifdef _WIN32 - return CloseHandle(cond->wCond) == 0 ? 1 : 0; // Needs to re-see - #else - return pthread_cond_destroy(&cond->pCond); - #endif + #ifdef _WIN32 + return CloseHandle(cond->wCond) == 0; + #else + return pthread_cond_destroy(&cond->pCond); + #endif } int cthreads_cond_wait(struct cthreads_cond *cond, struct cthreads_mutex *mutex) { - #ifdef _WIN32 - return SleepConditionVariableCS(&cond->wCond, &mutex->wMutex, INFINITE) == 0 ? 1 : 0; - #else - return pthread_cond_wait(&cond->pCond, &mutex->pMutex); - #endif -} - -int cthreads_join(struct cthreads_thread *thread, void *code) { - #ifdef _WIN32 - if (WaitForSingleObject(thread->wThread, INFINITE) == WAIT_FAILED) return 1; - - return GetExitCodeThread(thread->wThread, (LPDWORD)&code) == 0 ? 1 : 0; - #else - return pthread_join(thread->pThread, code ? &code : NULL); - #endif + #ifdef _WIN32 + return SleepConditionVariableCS(&cond->wCond, &mutex->wMutex, INFINITE) == 0; + #else + return pthread_cond_wait(&cond->pCond, &mutex->pMutex); + #endif } -int cthreads_rwlock_init(struct cthreads_rwlock *rwlock) { +#ifdef CTHREADS_RWLOCK + int cthreads_rwlock_init(struct cthreads_rwlock *rwlock) { #ifdef _WIN32 - return (rwlock->wRWLock = CreateMutex(NULL, FALSE, NULL)) == NULL ? 1 : 0; // Needs to re-see + return (rwlock->wRWLock = CreateMutex(NULL, FALSE, NULL)) == NULL; #else - return pthread_rwlock_init(&rwlock->pRWLock, NULL); + return pthread_rwlock_init(&rwlock->pRWLock, NULL); #endif -} - -int cthreads_rwlock_rdlock(struct cthreads_rwlock *rwlock) { - #ifdef _WIN32 - return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED ? 1 : 0; // Needs to re-see - #else - return pthread_rwlock_rdlock(&rwlock->pRWLock); - #endif -} - -int cthreads_rwlock_unlock(struct cthreads_rwlock *rwlock) { - #ifdef _WIN32 - return ReleaseMutex(rwlock->wRWLock) == 0 ? 1 : 0; // Needs to re-see - #else - return pthread_rwlock_unlock(&rwlock->pRWLock); - #endif -} + } -int cthreads_rwlock_wrlock(struct cthreads_rwlock *rwlock) { + int cthreads_rwlock_rdlock(struct cthreads_rwlock *rwlock) { #ifdef _WIN32 - return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED ? 1 : 0; // Needs to re-see + return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED; #else - return pthread_rwlock_wrlock(&rwlock->pRWLock); + return pthread_rwlock_rdlock(&rwlock->pRWLock); #endif -} + } -int cthreads_rwlock_destroy(struct cthreads_rwlock *rwlock) { + int cthreads_rwlock_unlock(struct cthreads_rwlock *rwlock) { #ifdef _WIN32 - return CloseHandle(rwlock->wRWLock) == 0 ? 1 : 0; // Needs to re-see + return ReleaseMutex(rwlock->wRWLock) == 0; #else - return pthread_rwlock_destroy(&rwlock->pRWLock); + return pthread_rwlock_unlock(&rwlock->pRWLock); #endif -} + } -int cthreads_equal(struct cthreads_thread thread1, struct cthreads_thread thread2) { + int cthreads_rwlock_wrlock(struct cthreads_rwlock *rwlock) { #ifdef _WIN32 - return GetThreadId(thread1.wThread) == GetThreadId(thread2.wThread); + return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED; #else - return pthread_equal(thread1.pThread, thread2.pThread); + return pthread_rwlock_wrlock(&rwlock->pRWLock); #endif -} - -struct cthreads_thread cthreads_self() { - struct cthreads_thread t; + } + int cthreads_rwlock_destroy(struct cthreads_rwlock *rwlock) { #ifdef _WIN32 - t.wThread = GetCurrentThread(); + return CloseHandle(rwlock->wRWLock) == 0; #else - t.pThread = pthread_self(); + return pthread_rwlock_destroy(&rwlock->pRWLock); #endif - - return t; -} + } +#endif diff --git a/core/cthreads.h b/core/cthreads.h index 514e9db37..8696292b1 100644 --- a/core/cthreads.h +++ b/core/cthreads.h @@ -1,3 +1,10 @@ +/* + Licensed under PerformanC's Custom license. + Available at licenses/LICENSE.performanc + + https://github.com/PerformanC/CThreads +*/ + #ifndef CTHREADS_H #define CTHREADS_H @@ -7,85 +14,143 @@ struct cthreads_args { }; #if _WIN32 - #include + #include #else - #include + #include #endif -struct cthreads_thread { - #ifdef _WIN32 - HANDLE wThread; - #else - pthread_t pThread; +#ifdef _WIN32 + #define CTHREADS_THREAD_DWCREATIONFLAGS 1 + + #define CTHREADS_MUTEX_BINITIALOWNER 1 + #define CTHREADS_MUTEX_LPNAME 1 + + #define CTHREADS_COND_BMANUALRESET 1 + #define CTHREADS_COND_BINITIALSTATE 1 + #define CTHREADS_COND_LPNAME 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_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_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; - size_t stack; + 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 - HANDLE wMutex; - #else - pthread_mutex_t pMutex; - #endif + #ifdef _WIN32 + HANDLE wMutex; + #else + pthread_mutex_t pMutex; + #endif }; struct cthreads_mutex_attr { - #ifdef _WIN32 - int bInitialOwner; - char *lpName; - #else - int pshared; - int type; - #if (defined __linux__ || defined __FreeBSD__) && !defined __ANDROID__ - int robust; - #endif - #if !defined __ANDROID__ - int protocol; - int prioceiling; - #endif + #ifdef _WIN32 + int bInitialOwner; + char *lpName; + #else + 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 }; struct cthreads_cond { - #ifdef _WIN32 - HANDLE wCond; - #else - pthread_cond_t pCond; - #endif + #ifdef _WIN32 + HANDLE wCond; + #else + pthread_cond_t pCond; + #endif }; struct cthreads_cond_attr { - #ifdef _WIN32 - int bManualReset; - int bInitialState; - char *lpName; - #else - int pshared; - int clock; + #ifdef _WIN32 + int bManualReset; + int bInitialState; + char *lpName; + #else + int pshared; + #ifdef CTHREADS_COND_CLOCK + int clock; #endif + #endif }; +#ifdef CTHREADS_RWLOCK struct cthreads_rwlock { - #ifdef _WIN32 - HANDLE wRWLock; - #else - pthread_rwlock_t pRWLock; - #endif + #ifdef _WIN32 + HANDLE wRWLock; + #else + pthread_rwlock_t pRWLock; + #endif }; +#endif /** * Creates a new thread. @@ -108,10 +173,55 @@ int cthreads_thread_create(struct cthreads_thread *thread, struct cthreads_threa * - pthread: pthread_detach * - windows threads: CloseHandle * - * @param thread Pointer to the thread structure to be detached. + * @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); +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); /** * Closes a thread. @@ -236,18 +346,7 @@ int cthreads_cond_destroy(struct cthreads_cond *cond); */ int cthreads_cond_wait(struct cthreads_cond *cond, struct cthreads_mutex *mutex); -/** - * Joins a thread. - * - * - pthread: pthread_join - * - windows threads: WaitForSingleObject - * - * @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_join(struct cthreads_thread *thread, void *code); - +#ifdef CTHREADS_RWLOCK /** * Initializes a read-write lock. * @@ -302,27 +401,6 @@ int cthreads_rwlock_wrlock(struct cthreads_rwlock *rwlock); * @return 0 on success, non-zero error code on failure. */ int cthreads_rwlock_destroy(struct cthreads_rwlock *rwlock); - -/** - * 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 Non-zero if the threads are equal, zero otherwise. - */ -int cthreads_equal(struct cthreads_thread thread1, struct cthreads_thread thread2); - -/** - * Retrieves the thread identifier of the current thread. - * - * - pthread: pthread_self - * - windows threads: GetCurrentThreadId - * - * @return Thread identifier of the current thread. - */ -struct cthreads_thread cthreads_self(); +#endif #endif /* CTHREADS_H */ diff --git a/core/threadpool.c b/core/threadpool.c index e8fe6e1f6..7710ed099 100644 --- a/core/threadpool.c +++ b/core/threadpool.c @@ -227,7 +227,7 @@ int threadpool_destroy(threadpool_t *pool, int flags) /* Join all worker thread */ for(i = 0; i < pool->thread_count; i++) { - if(cthreads_join(&pool->threads[i], NULL) != 0) { + if(cthreads_thread_join(pool->threads[i], NULL) != 0) { err = threadpool_thread_failure; } } diff --git a/licenses/LICENSE.performanc b/licenses/LICENSE.performanc index 9fc31120c..71d9a9417 100644 --- a/licenses/LICENSE.performanc +++ b/licenses/LICENSE.performanc @@ -1,13 +1,18 @@ -Custom PerformanC License +PerformanC's Custom License Copyright (c) 2023 PerformanC 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. +and it can even be used for commercial purposes. +However, its source code cannot be used to train a neural +network, nor can it (The Software) be copied in any way without the +permission of the PerformanC Organization. -The license can be included at the source code of the PerformanC software, although it is not required. +In case of external usage or copy, the license also must be +included at the source code of the PerformanC software +outside the official PerformanC repository, followed by +the URL of the PerformanC GitHub repository. 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 diff --git a/src/discord-timer.c b/src/discord-timer.c index c9e8c7d3d..6db21c128 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -126,18 +126,18 @@ _discord_timer_ctl_no_lock(struct discord *client, } } -#define LOCK_TIMERS(timers) \ - do { \ - cthreads_mutex_lock(&timers.lock); \ - if (timers.active.is_active \ - && !cthreads_equal(cthreads_self(), timers.active.thread)) \ - cthreads_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; \ - cthreads_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) { \ - cthreads_mutex_unlock(&timers->lock); \ + cthreads_mutex_unlock(&timers->lock); \ timer.on_status_changed(client, &timer); \ - cthreads_mutex_lock(&timers->lock); \ + cthreads_mutex_lock(&timers->lock); \ } \ timers->active.skip_update_phase = false; \ continue; \ @@ -173,7 +173,7 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) cthreads_mutex_lock(&timers->lock); timers->active.is_active = true; - timers->active.thread = cthreads_self(); + timers->active.thread = cthreads_thread_self(); struct discord_timer timer; timers->active.timer = &timer; diff --git a/src/discord-voice.c b/src/discord-voice.c index 3d159118e..d3b62db0f 100644 --- a/src/discord-voice.c +++ b/src/discord-voice.c @@ -862,7 +862,7 @@ _discord_on_voice_server_update(struct discord *client, /** TODO: replace with a threadpool */ if (cthreads_thread_create(&tid, NULL, &start_voice_ws_thread, vc, &targs)) ERR("Couldn't create thread"); - if (cthreads_thread_detach(&tid)) ERR("Couldn't detach thread"); + if (cthreads_thread_detach(tid)) ERR("Couldn't detach thread"); } } From cafb3ff8783bce33596cbaf7158bd1f117ad7ef8 Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Thu, 1 Feb 2024 22:39:26 -0300 Subject: [PATCH 3/4] chore(cthreads): improve usage This commit improves CThreads usage by destroying properly the mutexes, and expanding its usage to examples. --- core/cthreads.c | 4 ++-- core/cthreads.h | 4 ++-- core/threadpool.c | 2 +- core/websockets.c | 4 ++++ examples/slash-commands2.c | 11 ++++++----- include/discord-internal.h | 4 ++++ src/concord-once.c | 19 ++++++++++++------- src/discord-voice.c | 1 + test/Makefile | 2 +- test/cthreads.c | 8 ++++---- 10 files changed, 37 insertions(+), 22 deletions(-) diff --git a/core/cthreads.c b/core/cthreads.c index 843d2b98b..36361a2a0 100644 --- a/core/cthreads.c +++ b/core/cthreads.c @@ -103,9 +103,9 @@ unsigned long cthreads_thread_id(struct cthreads_thread thread) { #endif } -void cthreads_thread_close(void *code) { +void cthreads_thread_exit(void *code) { #ifdef _WIN32 - #ifdef __WATCOMC__ + #if defined __WATCOMC__ || _MSC_VER || __DMC__ ExitThread((DWORD)code); #else ExitThread((DWORD)(uintptr_t)code); diff --git a/core/cthreads.h b/core/cthreads.h index 8696292b1..c63df962b 100644 --- a/core/cthreads.h +++ b/core/cthreads.h @@ -224,14 +224,14 @@ struct cthreads_thread cthreads_thread_self(void); unsigned long cthreads_thread_id(struct cthreads_thread thread); /** - * Closes a thread. + * Exits a thread. * * - pthread: pthread_exit * - windows threads: ExitThread * * @param code Pointer to the thread exit code. */ -void cthreads_thread_close(void *code); +void cthreads_thread_exit(void *code); /** * Initializes a mutex. diff --git a/core/threadpool.c b/core/threadpool.c index 7710ed099..39f0a959d 100644 --- a/core/threadpool.c +++ b/core/threadpool.c @@ -300,6 +300,6 @@ static void *threadpool_thread(void *threadpool) pool->started--; cthreads_mutex_unlock(&(pool->lock)); - cthreads_thread_close(NULL); + cthreads_thread_exit(NULL); return(NULL); } \ No newline at end of file diff --git a/core/websockets.c b/core/websockets.c index e6fbf3105..3574e0152 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -50,7 +50,11 @@ struct websockets { /** lock for functions that may be called in other threads */ struct cthreads_mutex lock; /** lock for reading/writing the event-loop timestamp */ + #ifdef CTHREADS_RWLOCK struct cthreads_rwlock rwlock; + #else + #error "pthread_rwlock functions are not available on this system." + #endif /** * user-triggered actions diff --git a/examples/slash-commands2.c b/examples/slash-commands2.c index 74e9556d9..0539b93ec 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 e9eb083fc..701538ee7 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -780,7 +780,11 @@ struct discord_gateway { */ int ping_ms; /** ping 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 */ diff --git a/src/concord-once.c b/src/concord-once.c index f9621c114..d50e1bf0d 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-voice.c b/src/discord-voice.c index d3b62db0f..4a6c8d78e 100644 --- a/src/discord-voice.c +++ b/src/discord-voice.c @@ -881,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/test/Makefile b/test/Makefile index 3a5943863..899922d40 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 index de9a553e7..4d0d77e0a 100644 --- a/test/cthreads.c +++ b/test/cthreads.c @@ -18,7 +18,7 @@ TEST create_thread() { ret = cthreads_thread_create(&thread, NULL, thread_func, NULL, &args); ASSERT_EQ(0, ret); - ret = cthreads_join(&thread, 0); + ret = cthreads_thread_join(thread, 0); ASSERT_EQ(0, ret); PASS(); @@ -70,10 +70,10 @@ TEST equal() { struct cthreads_thread thread2; int ret; - thread1 = cthreads_self(); - thread2 = cthreads_self(); + thread1 = cthreads_thread_self(); + thread2 = cthreads_thread_self(); - ret = cthreads_equal(thread1, thread2); + ret = cthreads_thread_equal(thread1, thread2); ASSERT_EQ(1, ret); PASS(); From b72dd510bbe2518e791d877f0a90db6b7af0a717 Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Sat, 16 Mar 2024 19:29:24 -0300 Subject: [PATCH 4/4] chore(cthreads): update library This commit updates the library, allowing it to work properly on Windows. --- core/cthreads.c | 196 ++++++++++++++++++++++++++++++----- core/cthreads.h | 201 ++++++++++++++++++------------------ core/threadpool.c | 2 + licenses/LICENSE.performanc | 34 +++--- test/cthreads.c | 4 + 5 files changed, 301 insertions(+), 136 deletions(-) diff --git a/core/cthreads.c b/core/cthreads.c index 36361a2a0..1c0c85b6b 100644 --- a/core/cthreads.c +++ b/core/cthreads.c @@ -7,6 +7,7 @@ #include #include +#include #include "cthreads.h" @@ -24,6 +25,10 @@ DWORD WINAPI __cthreads_winthreads_function_wrapper(void *data) { 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; @@ -38,6 +43,10 @@ int cthreads_thread_create(struct cthreads_thread *thread, struct cthreads_threa (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); @@ -58,6 +67,10 @@ int cthreads_thread_create(struct cthreads_thread *thread, struct cthreads_threa } int cthreads_thread_detach(struct cthreads_thread thread) { + #ifdef CTHREADS_DEBUG + printf("cthreads_thread_detach\n"); + #endif + #ifdef _WIN32 return CloseHandle(thread.wThread); #else @@ -66,6 +79,10 @@ int cthreads_thread_detach(struct cthreads_thread thread) { } 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; @@ -76,6 +93,10 @@ int cthreads_thread_join(struct cthreads_thread thread, void *code) { } 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 @@ -86,6 +107,10 @@ int cthreads_thread_equal(struct cthreads_thread thread1, struct cthreads_thread 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 @@ -96,6 +121,10 @@ struct cthreads_thread cthreads_thread_self(void) { } 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 @@ -104,6 +133,10 @@ unsigned long cthreads_thread_id(struct cthreads_thread thread) { } 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); @@ -115,15 +148,29 @@ void cthreads_thread_exit(void *code) { #endif } -int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr *attr) { +#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 - if (attr) mutex->wMutex = CreateMutex(NULL, attr->bInitialOwner ? TRUE : FALSE, - attr->lpName ? (LPCSTR)attr->lpName : NULL); - else mutex->wMutex = CreateMutex(NULL, FALSE, NULL); + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_init\n"); + #endif + + (void) attr; + + InitializeCriticalSection(&mutex->wMutex); - return mutex->wMutex == NULL; + 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); @@ -146,47 +193,80 @@ int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr } int cthreads_mutex_lock(struct cthreads_mutex *mutex) { + #ifdef CTHREADS_DEBUG + printf("cthreads_mutex_lock\n"); + #endif + #ifdef _WIN32 - return WaitForSingleObject(mutex->wMutex, INFINITE) != WAIT_OBJECT_0; + 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 - return WaitForSingleObject(mutex->wMutex, 0) != WAIT_OBJECT_0; + 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 - return ReleaseMutex(mutex->wMutex) == 0; + 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 - return CloseHandle(mutex->wMutex) == 0; + DeleteCriticalSection(&mutex->wMutex); + + return 0; #else return pthread_mutex_destroy(&mutex->pMutex); #endif } -int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *attr) { +#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 - if (attr) cond->wCond = CreateEvent(NULL, attr->bManualReset ? TRUE : FALSE, - attr->bInitialState ? TRUE : FALSE, - attr->lpName ? (LPTSTR)attr->lpName : NULL); - else cond->wCond = CreateEvent(NULL, FALSE, FALSE, NULL); + (void) attr; + + InitializeConditionVariable(&cond->wCond); - return cond->wCond == NULL; + 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); @@ -200,30 +280,50 @@ int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *at } int cthreads_cond_signal(struct cthreads_cond *cond) { + #ifdef CTHREADS_DEBUG + printf("cthreads_cond_signal\n"); + #endif + #ifdef _WIN32 - return SetEvent(cond->wCond) == 0; + 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 - return SetEvent(cond->wCond) == 0; + 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 CloseHandle(cond->wCond) == 0; + 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 @@ -233,40 +333,90 @@ int cthreads_cond_wait(struct cthreads_cond *cond, struct cthreads_mutex *mutex) #ifdef CTHREADS_RWLOCK int cthreads_rwlock_init(struct cthreads_rwlock *rwlock) { + #ifdef CTHREADS_DEBUG + printf("cthreads_rwlock_init\n"); + #endif + #ifdef _WIN32 - return (rwlock->wRWLock = CreateMutex(NULL, FALSE, NULL)) == NULL; + 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 - return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED; + 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 - return ReleaseMutex(rwlock->wRWLock) == 0; + 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 - return WaitForSingleObject(rwlock->wRWLock, INFINITE) == WAIT_FAILED; + 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 - return CloseHandle(rwlock->wRWLock) == 0; + free(rwlock->wRWLock); + rwlock->wRWLock = NULL; + rwlock->type = 0; + + return 0; #else return pthread_rwlock_destroy(&rwlock->pRWLock); #endif diff --git a/core/cthreads.h b/core/cthreads.h index c63df962b..595ae898a 100644 --- a/core/cthreads.h +++ b/core/cthreads.h @@ -13,7 +13,7 @@ struct cthreads_args { void *data; }; -#if _WIN32 +#ifdef _WIN32 #include #else #include @@ -22,13 +22,6 @@ struct cthreads_args { #ifdef _WIN32 #define CTHREADS_THREAD_DWCREATIONFLAGS 1 - #define CTHREADS_MUTEX_BINITIALOWNER 1 - #define CTHREADS_MUTEX_LPNAME 1 - - #define CTHREADS_COND_BMANUALRESET 1 - #define CTHREADS_COND_BINITIALSTATE 1 - #define CTHREADS_COND_LPNAME 1 - #define CTHREADS_RWLOCK 1 #else #define CTHREADS_THREAD_STACKADDR 1 @@ -43,6 +36,8 @@ struct cthreads_args { #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 @@ -57,6 +52,8 @@ struct cthreads_args { #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 @@ -94,58 +91,56 @@ struct cthreads_thread_attr { struct cthreads_mutex { #ifdef _WIN32 - HANDLE wMutex; + CRITICAL_SECTION wMutex; #else pthread_mutex_t pMutex; #endif }; -struct cthreads_mutex_attr { - #ifdef _WIN32 - int bInitialOwner; - char *lpName; - #else - int pshared; - #ifdef CTHREADS_MUTEX_TYPE - int type; +#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 - #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 - HANDLE wCond; + CONDITION_VARIABLE wCond; #else pthread_cond_t pCond; #endif }; -struct cthreads_cond_attr { - #ifdef _WIN32 - int bManualReset; - int bInitialState; - char *lpName; - #else - int pshared; - #ifdef CTHREADS_COND_CLOCK - int clock; +#ifdef CTHREADS_COND_ATTR + struct cthreads_cond_attr { + #ifndef _WIN32 + int pshared; + #ifdef CTHREADS_COND_CLOCK + int clock; + #endif #endif - #endif -}; + }; +#endif #ifdef CTHREADS_RWLOCK struct cthreads_rwlock { #ifdef _WIN32 - HANDLE wRWLock; + int type; + PSRWLOCK wRWLock; #else pthread_rwlock_t pRWLock; #endif @@ -240,10 +235,14 @@ void cthreads_thread_exit(void *code); * - 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. + * @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. */ -int cthreads_mutex_init(struct cthreads_mutex *mutex, struct cthreads_mutex_attr *attr); +#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. @@ -296,10 +295,14 @@ int cthreads_mutex_destroy(struct cthreads_mutex *mutex); * - 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. + * @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. */ -int cthreads_cond_init(struct cthreads_cond *cond, struct cthreads_cond_attr *attr); +#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. @@ -347,60 +350,60 @@ int cthreads_cond_destroy(struct cthreads_cond *cond); 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: ReleaseSRWLockExclusive for writer, ReleaseSRWLockShared for reader - * - * @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: DeleteSRWLock - * - * @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); + /** + * 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 39f0a959d..e3d835d06 100644 --- a/core/threadpool.c +++ b/core/threadpool.c @@ -118,6 +118,7 @@ threadpool_t *threadpool_create(int thread_count, int queue_size, int flags) pool->queue = (threadpool_task_t *)malloc (sizeof(threadpool_task_t) * queue_size); + /* Initialize mutex and conditional variable first */ if((cthreads_mutex_init(&(pool->lock), NULL) != 0) || (cthreads_cond_init(&(pool->notify), NULL) != 0) || @@ -126,6 +127,7 @@ threadpool_t *threadpool_create(int thread_count, int queue_size, int flags) goto err; } + /* Start worker threads */ for(i = 0; i < thread_count; i++) { if(cthreads_thread_create(&(pool->threads[i]), NULL, diff --git a/licenses/LICENSE.performanc b/licenses/LICENSE.performanc index 71d9a9417..bf86410c7 100644 --- a/licenses/LICENSE.performanc +++ b/licenses/LICENSE.performanc @@ -1,18 +1,24 @@ -PerformanC's Custom 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, its source code cannot be used to train a neural -network, nor can it (The Software) be copied in any way without the -permission of the PerformanC Organization. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -In case of external usage or copy, the license also must be -included at the source code of the PerformanC software -outside the official PerformanC repository, followed by -the URL of the PerformanC GitHub repository. +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/test/cthreads.c b/test/cthreads.c index 4d0d77e0a..90faf4dc2 100644 --- a/test/cthreads.c +++ b/test/cthreads.c @@ -28,7 +28,11 @@ 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);