12#define READ_LOCKS_MAX 63
16#define MAX_FUTEX_WAIT_MS 100
19#define DEAD_LOCK_TIMEOUT_MS 10000
23 uint32_t write_lock: 1;
24 uint32_t read_locks: 5;
26 uint32_t write_seq: 22;
28static struct lock decode(uint32_t val) {
29 val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
30 val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8 ;
32 lock.blocked = ((val >> 0) & 1);
33 lock.write_lock = ((val >> 1) & 1);
34 lock.read_locks = ((val >> 2) & 31);
35 lock.read_seq = ((val >> 7) & 7);
36 lock.write_seq = (uint64_t)(val >> 10) & 0x3FFFFF;
39static uint32_t encode(
const struct lock lock) {
41 (uint32_t)
lock.blocked << 0|
42 (uint32_t)
lock.write_lock << 1|
43 (uint32_t)
lock.read_locks << 2|
44 (uint32_t)
lock.read_seq << 7|
45 (uint32_t)
lock.write_seq << 10;
46 val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
47 val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8 ;
50static bool equal(
const struct lock lock1,
const struct lock lock2) {
52 lock1.blocked == lock2.blocked &&
53 lock1.write_lock == lock2.write_lock &&
54 lock1.read_locks == lock2.read_locks &&
55 lock1.read_seq == lock2.read_seq &&
56 lock1.write_seq == lock2.write_seq;
58static struct lock load(const uint32_t *ptr) {
return decode(clod_atomic_load(ptr)); }
59static bool cas(uint32_t *ptr,
struct lock *expected,
const struct lock desired) {
60 uint32_t expected_val = encode(*expected);
61 if (!clod_atomic_cas(ptr, &expected_val, encode(desired))) {
62 *expected = decode(expected_val);
68static struct timespec now() {
70 if (timespec_get(&now, CLOCK_MONOTONIC) == 0) {
71 debug(CLOD_RWSEQ_DEBUG,
"System clock doesn't support monotonic time.");
72 return (
struct timespec){0};
76static int time_delta(
struct timespec *last) {
77 struct timespec current = now();
79 (int)((current.tv_sec - last->tv_sec) * 1000) +
80 (int)((current.tv_nsec - last->tv_nsec) / 1000000);
85static struct lock wait(const uint32_t *ptr,
const struct lock expected, int *timeout) {
93 while (equal(
lock, expected)) {
98 bool timed_out = clod_futex_wait((
int*)ptr, (
int)encode(expected), *timeout < MAX_FUTEX_WAIT_MS ? *timeout : MAX_FUTEX_WAIT_MS);
100 *timeout -= time_delta(&time);
107 if (
lock.blocked == 0 && expected.blocked)
108 debug(CLOD_RWSEQ_DEBUG,
"%ptr Blocked flag was unset, but we weren't woken up. Possible bug in other thread.\n", (
void*)ptr);
109 if (
lock.write_lock == 0 && expected.write_lock)
110 debug(CLOD_RWSEQ_DEBUG,
"%ptr Write lock released, but we weren't woken up. Possible bug in other thread.\n", (
void*)ptr);
111 if (
lock.read_locks == 0 && expected.read_locks)
112 debug(CLOD_RWSEQ_DEBUG,
"%ptr Read locks released, but we weren't woken up. Possible bug in other thread.\n", (
void*)ptr);
117static void wake(
const uint32_t *ptr) { clod_futex_wake_all((
int*)ptr); }
119enum clod_rwseq_result clod_rwseq_ro_lock(
const uint32_t *ptr, uint32_t *seq_out) {
120 int timeout = DEAD_LOCK_TIMEOUT_MS;
122 while (
lock.blocked ||
lock.write_lock) {
126 struct lock actual = wait(ptr,
lock, &timeout);
128 if (actual.write_seq !=
lock.write_seq)
129 timeout = DEAD_LOCK_TIMEOUT_MS;
134 *seq_out =
lock.write_seq;
137enum clod_rwseq_result clod_rwseq_ro_unlock(
const uint32_t *ptr,
const uint32_t seq) {
138 const struct lock current = load(ptr);
140 if (current.write_seq != seq)
146bool clod_rwseq_rd_heartbeat(
void *ptr) {
150 if (
lock.read_locks == 0) {
151 debug(CLOD_RWSEQ_DEBUG,
"%ptr Possible failure to unlock read lock. Read sequence heartbeat task left running on lock with no read locks.", ptr);
157 }
while (!cas(ptr, &
lock, want));
161 int timeout = DEAD_LOCK_TIMEOUT_MS;
165 while (
lock.blocked ||
lock.write_lock ||
lock.read_locks == READ_LOCKS_MAX) {
169 struct lock actual = wait(ptr,
lock, &timeout);
171 if (actual.write_seq !=
lock.write_seq)
172 timeout = DEAD_LOCK_TIMEOUT_MS;
177 }
while (!cas(ptr, &
lock, want));
179 clod_rwseq_rd_keepalive_start((
int*)ptr);
183 clod_rwseq_rd_keepalive_end((
int*)ptr);
187 if (
lock.read_locks == 0) {
188 debug(CLOD_RWSEQ_DEBUG,
"%ptr Attempted to unlock already unlocked read lock.", (
void*)ptr);
194 }
while (!cas(ptr, &
lock, want));
196 if (
lock.read_locks == 0)
@ CLOD_RWSEQ_OK
No worries at all!