-
-
Notifications
You must be signed in to change notification settings - Fork 851
/
sync.h
175 lines (161 loc) · 5.26 KB
/
sync.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#ifndef UTIL_SYNC_H
#define UTIL_SYNC_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 ((cond_t) {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 struct {
pthread_rwlock_t l;
// 0: unlocked
// -1: write-locked
// >0: read-locked with this many readers
atomic_int val;
const char *file;
int line;
int pid;
} 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->l, pattr)) __builtin_trap();
lock->val = lock->line = lock->pid = 0;
lock->file = NULL;
}
extern int current_pid(void);
static inline void wrlock_destroy(wrlock_t *lock) {
if (pthread_rwlock_destroy(&lock->l) != 0) __builtin_trap();
}
static inline void read_wrlock(wrlock_t *lock) {
if (pthread_rwlock_rdlock(&lock->l) != 0) __builtin_trap();
assert(lock->val >= 0);
lock->val++;
}
static inline void read_wrunlock(wrlock_t *lock) {
assert(lock->val > 0);
lock->val--;
if (pthread_rwlock_unlock(&lock->l) != 0) __builtin_trap();
}
static inline void __write_wrlock(wrlock_t *lock, const char *file, int line) {
if (pthread_rwlock_wrlock(&lock->l) != 0) __builtin_trap();
assert(lock->val == 0);
lock->val = -1;
lock->file = file;
lock->line = line;
lock->pid = current_pid();
}
#define write_wrlock(lock) __write_wrlock(lock, __FILE__, __LINE__)
static inline void write_wrunlock(wrlock_t *lock) {
assert(lock->val == -1);
lock->val = lock->line = lock->pid = 0;
lock->file = NULL;
if (pthread_rwlock_unlock(&lock->l) != 0) __builtin_trap();
}
extern __thread sigjmp_buf unwind_buf;
extern __thread bool should_unwind;
static inline int sigunwind_start(void) {
if (sigsetjmp(unwind_buf, 1)) {
should_unwind = false;
return 1;
} else {
should_unwind = true;
return 0;
}
}
static inline void sigunwind_end(void) {
should_unwind = false;
}
#endif