ish/util/sync.h
2020-06-06 23:52:12 -07:00

158 lines
4.7 KiB
C

#ifndef UTIL_SYNC_H
#define UTIL_SYNC_H
#include <errno.h>
#include <stdatomic.h>
#include <pthread.h>
#include <stdbool.h>
#include <setjmp.h>
#include "misc.h"
#include "debug.h"
// locks, implemented using pthread
#define LOCK_DEBUG 0
typedef struct {
pthread_mutex_t m;
pthread_t owner;
#if LOCK_DEBUG
struct lock_debug {
const char *file; // doubles as locked
int line;
int pid;
bool initialized;
} debug;
#endif
} lock_t;
static inline void lock_init(lock_t *lock) {
pthread_mutex_init(&lock->m, NULL);
#if LOCK_DEBUG
lock->debug = (struct lock_debug) {
.initialized = true,
};
#endif
}
#if LOCK_DEBUG
#define LOCK_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, 0, { .initialized = true }}
#else
#define LOCK_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, 0}
#endif
static inline void __lock(lock_t *lock, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {
pthread_mutex_lock(&lock->m);
lock->owner = pthread_self();
#if LOCK_DEBUG
assert(lock->debug.initialized);
assert(!lock->debug.file && "Attempting to recursively lock");
lock->debug.file = file;
lock->debug.line = line;
extern int current_pid(void);
lock->debug.pid = current_pid();
#endif
}
#define lock(lock) __lock(lock, __FILE__, __LINE__)
static inline void unlock(lock_t *lock) {
#if LOCK_DEBUG
assert(lock->debug.initialized);
assert(lock->debug.file && "Attempting to unlock an unlocked lock");
lock->debug = (struct lock_debug) { .initialized = true };
#endif
lock->owner = zero_init(pthread_t);
pthread_mutex_unlock(&lock->m);
}
static inline int trylock(lock_t *lock, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {
int status = pthread_mutex_trylock(&lock->m);
#if LOCK_DEBUG
if (!status) {
lock->debug.file = file;
lock->debug.line = line;
extern int current_pid(void);
lock->debug.pid = current_pid();
}
#endif
return status;
}
#define trylock(lock) trylock(lock, __FILE__, __LINE__)
// conditions, implemented using pthread conditions but hacked so you can also
// be woken by a signal
typedef struct {
pthread_cond_t cond;
} cond_t;
#define COND_INITIALIZER {PTHREAD_COND_INITIALIZER}
// Must call before using the condition
void cond_init(cond_t *cond);
// Must call when finished with the condition (currently doesn't do much but might do something important eventually I guess)
void cond_destroy(cond_t *cond);
// Releases the lock, waits for the condition, and reacquires the lock.
// Returns _EINTR if waiting stopped because the thread received a signal,
// _ETIMEDOUT if waiting stopped because the timout expired, 0 otherwise.
// Will never return _ETIMEDOUT if timeout is NULL.
int must_check wait_for(cond_t *cond, lock_t *lock, struct timespec *timeout);
// Same as wait_for, except it will never return _EINTR
int wait_for_ignore_signals(cond_t *cond, lock_t *lock, struct timespec *timeout);
// Wake up all waiters.
void notify(cond_t *cond);
// Wake up one waiter.
void notify_once(cond_t *cond);
// this is a read-write lock that prefers writers, i.e. if there are any
// writers waiting a read lock will block.
// on darwin pthread_rwlock_t is already like this, on linux you can configure
// it to prefer writers. not worrying about anything else right now.
typedef pthread_rwlock_t wrlock_t;
static inline void wrlock_init(wrlock_t *lock) {
pthread_rwlockattr_t *pattr = NULL;
#if defined(__GLIBC__)
pthread_rwlockattr_t attr;
pattr = &attr;
pthread_rwlockattr_init(pattr);
pthread_rwlockattr_setkind_np(pattr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
#endif
if (pthread_rwlock_init(lock, pattr)) __builtin_trap();
}
static inline void wrlock_destroy(wrlock_t *lock) {
if (pthread_rwlock_destroy(lock) != 0) __builtin_trap();
}
static inline void read_wrlock(wrlock_t *lock) {
if (pthread_rwlock_rdlock(lock) != 0) __builtin_trap();
}
static inline void read_wrunlock(wrlock_t *lock) {
if (pthread_rwlock_unlock(lock) != 0) __builtin_trap();
}
static inline void write_wrlock(wrlock_t *lock) {
if (pthread_rwlock_wrlock(lock) != 0) __builtin_trap();
}
static inline void write_wrunlock(wrlock_t *lock) {
if (pthread_rwlock_unlock(lock) != 0) __builtin_trap();
}
extern __thread sigjmp_buf unwind_buf;
extern __thread bool should_unwind;
static inline int sigunwind_start() {
if (sigsetjmp(unwind_buf, 1)) {
should_unwind = false;
return 1;
} else {
should_unwind = true;
return 0;
}
}
static inline void sigunwind_end() {
should_unwind = false;
}
#if __APPLE__
#define set_thread_name(threadname) pthread_setname_np(threadname)
#else
#define set_thread_name(threadname) pthread_setname_np(pthread_self(), threadname)
#endif
#endif