Skip to content

Instantly share code, notes, and snippets.

@kayws426
Created June 11, 2021 02:38
Show Gist options
  • Save kayws426/3fafdc7e216692c8db45633e58f5b486 to your computer and use it in GitHub Desktop.
Save kayws426/3fafdc7e216692c8db45633e58f5b486 to your computer and use it in GitHub Desktop.
a single-header library to provide some pthread functions
//
// Cross Platform Thread Wrapper
// Refer: https://nachtimwald.com/2019/04/05/cross-platform-thread-wrapper/
//
// This file is a single-header library to provide some pthread functions
//
#ifndef __PTHREAD_WRAPPER_H__
#define __PTHREAD_WRAPPER_H__
#ifdef _WIN32
#include <stdbool.h>
#include <windows.h>
#include <synchapi.h>
#else
#include <pthread.h>
#endif
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#define PTHREAD_WRAPPER_API static inline
typedef CRITICAL_SECTION pthread_mutex_t;
typedef void pthread_attr_t;
typedef void pthread_mutexattr_t;
typedef void pthread_condattr_t;
typedef void pthread_rwlockattr_t;
typedef HANDLE pthread_t;
typedef CONDITION_VARIABLE pthread_cond_t;
#if defined(WINVER) && (WINVER >= _WIN32_WINNT_WIN7)
typedef struct {
SRWLOCK lock;
bool exclusive;
} pthread_rwlock_t;
#define PTHREAD_RWLOCK_DEFINED 1
#endif
#if defined(_MSC_VER) && !defined(TIME_UTC)
struct timespec {
long tv_sec;
long tv_nsec;
};
#endif
#endif
#ifdef _WIN32
PTHREAD_WRAPPER_API int pthread_create(pthread_t* thread, pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
PTHREAD_WRAPPER_API int pthread_join(pthread_t thread, void** value_ptr);
PTHREAD_WRAPPER_API int pthread_detach(pthread_t);
PTHREAD_WRAPPER_API int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t* attr);
PTHREAD_WRAPPER_API int pthread_mutex_destroy(pthread_mutex_t* mutex);
PTHREAD_WRAPPER_API int pthread_mutex_lock(pthread_mutex_t* mutex);
PTHREAD_WRAPPER_API int pthread_mutex_unlock(pthread_mutex_t* mutex);
PTHREAD_WRAPPER_API int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* attr);
PTHREAD_WRAPPER_API int pthread_cond_destroy(pthread_cond_t* cond);
PTHREAD_WRAPPER_API int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
PTHREAD_WRAPPER_API int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime);
PTHREAD_WRAPPER_API int pthread_cond_signal(pthread_cond_t* cond);
PTHREAD_WRAPPER_API int pthread_cond_broadcast(pthread_cond_t* cond);
#if PTHREAD_RWLOCK_DEFINED
PTHREAD_WRAPPER_API int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr);
PTHREAD_WRAPPER_API int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
PTHREAD_WRAPPER_API int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
PTHREAD_WRAPPER_API int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
PTHREAD_WRAPPER_API int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
PTHREAD_WRAPPER_API int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);
PTHREAD_WRAPPER_API int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
#endif
#endif
PTHREAD_WRAPPER_API unsigned int pcthread_get_num_procs();
PTHREAD_WRAPPER_API void msec_to_timespec(struct timespec* ts, unsigned int ms);
PTHREAD_WRAPPER_API DWORD timespec_to_msec(const struct timespec* ts);
#ifdef _WIN32 // Implementations
// Thread: Create
PTHREAD_WRAPPER_API int pthread_create(pthread_t* thread, pthread_attr_t* attr, void* (*start_routine)(void*), void* arg) {
(void)(attr);
if (thread == NULL || start_routine == NULL)
return 1;
*thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
if (*thread == NULL) {
return 1;
}
return 0;
}
// Thread: Join
PTHREAD_WRAPPER_API int pthread_join(pthread_t thread, void** value_ptr) {
(void)value_ptr;
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return 0;
}
// Thread: Detach
PTHREAD_WRAPPER_API int pthread_detach(pthread_t thread) {
CloseHandle(thread);
return 0;
}
// Mutex: Init
PTHREAD_WRAPPER_API int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t* attr) {
(void)attr;
if (mutex == NULL) {
return 1;
}
InitializeCriticalSection(mutex);
return 0;
}
// Mutex: Destroy
PTHREAD_WRAPPER_API int pthread_mutex_destroy(pthread_mutex_t* mutex) {
if (mutex == NULL) {
return 1;
}
DeleteCriticalSection(mutex);
return 0;
}
// Mutex: Lock
PTHREAD_WRAPPER_API int pthread_mutex_lock(pthread_mutex_t* mutex) {
if (mutex == NULL) {
return 1;
}
EnterCriticalSection(mutex);
return 0;
}
// Mutex: Unlock
PTHREAD_WRAPPER_API int pthread_mutex_unlock(pthread_mutex_t* mutex) {
if (mutex == NULL) {
return 1;
}
LeaveCriticalSection(mutex);
return 0;
}
// Conditional Variable: Init
PTHREAD_WRAPPER_API int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* attr) {
(void)attr;
if (cond == NULL) {
return 1;
}
InitializeConditionVariable(cond);
return 0;
}
// Conditional Variable: Destroy
PTHREAD_WRAPPER_API int pthread_cond_destroy(pthread_cond_t* cond) {
/* Windows does not have a destroy for conditionals */
(void)cond;
return 0;
}
// Conditional Variable: Wait
PTHREAD_WRAPPER_API int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
if (cond == NULL || mutex == NULL) {
return 1;
}
return pthread_cond_timedwait(cond, mutex, NULL);
}
// Conditional Variable: TimedWait
PTHREAD_WRAPPER_API int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime) {
if (cond == NULL || mutex == NULL) {
return 1;
}
if (!SleepConditionVariableCS(cond, mutex, timespec_to_msec(abstime))) {
return 1;
}
return 0;
}
// Conditional Variable: Signal
PTHREAD_WRAPPER_API int pthread_cond_signal(pthread_cond_t* cond) {
if (cond == NULL) {
return 1;
}
WakeConditionVariable(cond);
return 0;
}
// Conditional Variable: Broadcast
PTHREAD_WRAPPER_API int pthread_cond_broadcast(pthread_cond_t* cond) {
if (cond == NULL) {
return 1;
}
WakeAllConditionVariable(cond);
return 0;
}
#if PTHREAD_RWLOCK_DEFINED
// Read Write Lock: Init
PTHREAD_WRAPPER_API int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr) {
(void)attr;
if (rwlock == NULL) {
return 1;
}
InitializeSRWLock(&(rwlock->lock));
rwlock->exclusive = false;
return 0;
}
// Read Write Lock: Destroy
PTHREAD_WRAPPER_API int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) {
(void)rwlock;
}
// Read Write Lock: ReadLock
PTHREAD_WRAPPER_API int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) {
if (rwlock == NULL) {
return 1;
}
AcquireSRWLockShared(&(rwlock->lock));
}
// Read Write Lock: TryReadLock
PTHREAD_WRAPPER_API int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) {
if (rwlock == NULL) {
return 1;
}
return !TryAcquireSRWLockShared(&(rwlock->lock));
}
// Read Write Lock: WriteLock
PTHREAD_WRAPPER_API int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) {
if (rwlock == NULL) {
return 1;
}
AcquireSRWLockExclusive(&(rwlock->lock));
rwlock->exclusive = true;
}
// Read Write Lock: TryWriteLock
PTHREAD_WRAPPER_API int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) {
BOOLEAN ret;
if (rwlock == NULL) {
return 1;
}
ret = TryAcquireSRWLockExclusive(&(rwlock->lock));
if (ret) {
rwlock->exclusive = true;
}
return ret;
}
// Read Write Lock: Unlock
PTHREAD_WRAPPER_API int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) {
if (rwlock == NULL) {
return 1;
}
if (rwlock->exclusive) {
rwlock->exclusive = false;
ReleaseSRWLockExclusive(&(rwlock->lock));
}
else {
ReleaseSRWLockShared(&(rwlock->lock));
}
}
#endif
// Time conversion: ms to timespec
PTHREAD_WRAPPER_API void msec_to_timespec(struct timespec* ts, unsigned int msec) {
if (ts == NULL) {
return;
}
#if defined(_MSC_VER) && defined(TIME_UTC)
if (TIME_UTC == timespec_get(ts, TIME_UTC)) {
long _nsec = ts->tv_nsec + ((msec % 1000) * 1000000);
if (_nsec >= 1000000000) {
_nsec -= 1000000000;
ts->tv_sec += ((msec / 1000) + 1);
ts->tv_nsec = _nsec;
}
else {
ts->tv_sec += (msec / 1000);
ts->tv_nsec = _nsec;
}
}
else {
// if 'timespec_get' returns fail, try with 'time' function
ts->tv_sec = (msec / 1000) + time(NULL);
ts->tv_nsec = (msec % 1000) * 1000000;
}
#else
ts->tv_sec = (msec / 1000) + time(NULL);
ts->tv_nsec = (msec % 1000) * 1000000;
#endif
}
// Time conversion: timespec to ms
PTHREAD_WRAPPER_API DWORD timespec_to_msec(const struct timespec* abstime) {
time_t t;
if (abstime == NULL) {
return INFINITE;
}
#if defined(_MSC_VER) && defined(TIME_UTC)
struct timespec _ts_now;
if (TIME_UTC == timespec_get(&_ts_now, TIME_UTC)) {
register time_t _sec = (abstime->tv_sec - _ts_now.tv_sec);
register long _msec = ((abstime->tv_nsec - _ts_now.tv_nsec) / 1000000);
if (_msec < 0) {
_msec += 1000;
_sec -= 1;
}
t = (_sec * 1000) + _msec;
}
else {
// if 'timespec_get' returns fail, try with 'time' function
t = ((abstime->tv_sec - time(NULL)) * 1000) + (abstime->tv_nsec / 1000000);
}
if (t < 0) {
t = 1;
}
#else
t = ((abstime->tv_sec - time(NULL)) * 1000) + (abstime->tv_nsec / 1000000);
if (t < 0) {
t = 1;
}
#endif
return (DWORD)t;
}
// Number of Cores / Processors
#ifdef _WIN32
PTHREAD_WRAPPER_API unsigned int pcthread_get_num_procs() {
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
}
#else
#include <unistd.h>
PTHREAD_WRAPPER_API unsigned int pcthread_get_num_procs() {
return (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
}
#endif
#endif // Implementations
#ifdef __cplusplus
} // end of extern "C"
#endif
#endif /* __PTHREAD_WRAPPER_H__ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment