1 | #ifndef JEMALLOC_INTERNAL_SEQ_H |
2 | #define JEMALLOC_INTERNAL_SEQ_H |
3 | |
4 | #include "jemalloc/internal/atomic.h" |
5 | |
6 | /* |
7 | * A simple seqlock implementation. |
8 | */ |
9 | |
10 | #define seq_define(type, short_type) \ |
11 | typedef struct { \ |
12 | atomic_zu_t seq; \ |
13 | atomic_zu_t data[ \ |
14 | (sizeof(type) + sizeof(size_t) - 1) / sizeof(size_t)]; \ |
15 | } seq_##short_type##_t; \ |
16 | \ |
17 | /* \ |
18 | * No internal synchronization -- the caller must ensure that there's \ |
19 | * only a single writer at a time. \ |
20 | */ \ |
21 | static inline void \ |
22 | seq_store_##short_type(seq_##short_type##_t *dst, type *src) { \ |
23 | size_t buf[sizeof(dst->data) / sizeof(size_t)]; \ |
24 | buf[sizeof(buf) / sizeof(size_t) - 1] = 0; \ |
25 | memcpy(buf, src, sizeof(type)); \ |
26 | size_t old_seq = atomic_load_zu(&dst->seq, ATOMIC_RELAXED); \ |
27 | atomic_store_zu(&dst->seq, old_seq + 1, ATOMIC_RELAXED); \ |
28 | atomic_fence(ATOMIC_RELEASE); \ |
29 | for (size_t i = 0; i < sizeof(buf) / sizeof(size_t); i++) { \ |
30 | atomic_store_zu(&dst->data[i], buf[i], ATOMIC_RELAXED); \ |
31 | } \ |
32 | atomic_store_zu(&dst->seq, old_seq + 2, ATOMIC_RELEASE); \ |
33 | } \ |
34 | \ |
35 | /* Returns whether or not the read was consistent. */ \ |
36 | static inline bool \ |
37 | seq_try_load_##short_type(type *dst, seq_##short_type##_t *src) { \ |
38 | size_t buf[sizeof(src->data) / sizeof(size_t)]; \ |
39 | size_t seq1 = atomic_load_zu(&src->seq, ATOMIC_ACQUIRE); \ |
40 | if (seq1 % 2 != 0) { \ |
41 | return false; \ |
42 | } \ |
43 | for (size_t i = 0; i < sizeof(buf) / sizeof(size_t); i++) { \ |
44 | buf[i] = atomic_load_zu(&src->data[i], ATOMIC_RELAXED); \ |
45 | } \ |
46 | atomic_fence(ATOMIC_ACQUIRE); \ |
47 | size_t seq2 = atomic_load_zu(&src->seq, ATOMIC_RELAXED); \ |
48 | if (seq1 != seq2) { \ |
49 | return false; \ |
50 | } \ |
51 | memcpy(dst, buf, sizeof(type)); \ |
52 | return true; \ |
53 | } |
54 | |
55 | #endif /* JEMALLOC_INTERNAL_SEQ_H */ |
56 | |