summaryrefslogtreecommitdiff
path: root/src/ot_fdstate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ot_fdstate.c')
-rw-r--r--src/ot_fdstate.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/ot_fdstate.c b/src/ot_fdstate.c
new file mode 100644
index 0000000..8e4bd89
--- /dev/null
+++ b/src/ot_fdstate.c
@@ -0,0 +1,202 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <stdatomic.h>
6#include <stdint.h>
7#include <stdlib.h>
8#include <sys/mman.h>
9#include <sys/resource.h>
10#include <sys/types.h>
11
12/* Opentracker */
13#include "ot_fdstate.h"
14
15enum { FD_FREE, FD_IDLE, FD_BUSY, FD_CLOSING };
16
17/* Generation and state share one word so that taking the state is also a
18 check that the generation has not moved */
19#define FD_GEN(w) ((uint32_t)((w) >> 32))
20#define FD_ST(w) ((uint32_t)(((w) >> 24) & 0xff))
21#define FD_WORD(g, s) (((uint64_t)(g) << 32) | ((uint64_t)(s) << 24))
22#define FD_SET_ST(w, s) (((w) & ~(uint64_t)0xff000000) | ((uint64_t)(s) << 24))
23
24typedef struct {
25 _Atomic uint64_t state;
26 _Atomic(void *) cookie;
27 _Atomic ot_time expiry;
28} ot_fdslot;
29
30/* A descriptor the kernel can hand out is below the limit it was handed out
31 under, but the limit can be raised after we sized the table, so an out of
32 range descriptor is rejected */
33#define FD_SLOTS_MAX (1u << 20)
34
35static ot_fdslot *g_slots;
36static size_t g_slot_count;
37/* Descriptors are handed out lowest first, so a sweep only has to walk as
38 far as the highest one ever seen rather than the whole table. */
39static _Atomic size_t g_slot_highest;
40
41int fdstate_init(void) {
42 struct rlimit limit;
43 size_t count = FD_SLOTS_MAX;
44
45 if (getrlimit(RLIMIT_NOFILE, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur < FD_SLOTS_MAX)
46 count = (size_t)limit.rlim_cur;
47
48 g_slots = mmap(NULL, count * sizeof(ot_fdslot), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
49 if (g_slots == MAP_FAILED) {
50 g_slots = NULL;
51 return -1;
52 }
53
54 g_slot_count = count;
55 atomic_store(&g_slot_highest, 0);
56 return 0;
57}
58
59void fdstate_deinit(void) {
60 if (g_slots)
61 munmap(g_slots, g_slot_count * sizeof(ot_fdslot));
62 g_slots = NULL;
63 g_slot_count = 0;
64 atomic_store(&g_slot_highest, 0);
65}
66
67size_t fdstate_slots(void) { return g_slot_count; }
68
69static ot_fdslot *fdstate_slot(int fd) {
70 if (fd < 0 || (size_t)fd >= g_slot_count)
71 return NULL;
72 return g_slots + fd;
73}
74
75uint32_t fdstate_open(int fd, void *cookie, ot_time expiry) {
76 ot_fdslot *slot = fdstate_slot(fd);
77 uint64_t word;
78
79 if (!slot)
80 return 0;
81
82 {
83 size_t highest = atomic_load_explicit(&g_slot_highest, memory_order_relaxed);
84
85 while ((size_t)fd > highest &&
86 !atomic_compare_exchange_weak_explicit(&g_slot_highest, &highest, (size_t)fd, memory_order_relaxed,
87 memory_order_relaxed))
88 ;
89 }
90
91 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
92 atomic_store_explicit(&slot->cookie, cookie, memory_order_release);
93 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
94 /* Publishes the two above to whoever claims the slot next */
95 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word), FD_IDLE), memory_order_release);
96
97 return FD_GEN(word);
98}
99
100uint32_t fdstate_generation(int fd) {
101 ot_fdslot *slot = fdstate_slot(fd);
102
103 return slot ? FD_GEN(atomic_load_explicit(&slot->state, memory_order_relaxed)) : 0;
104}
105
106int fdstate_claim(int fd, uint32_t generation) {
107 ot_fdslot *slot = fdstate_slot(fd);
108 uint64_t word;
109
110 if (!slot)
111 return 0;
112
113 word = atomic_load_explicit(&slot->state, memory_order_acquire);
114 for (;;) {
115 if (FD_GEN(word) != generation || FD_ST(word) != FD_IDLE)
116 return 0;
117 if (atomic_compare_exchange_weak_explicit(&slot->state, &word, FD_SET_ST(word, FD_BUSY), memory_order_acquire,
118 memory_order_relaxed))
119 return 1;
120 /* word now holds what was really there, so the test above runs again */
121 }
122}
123
124void fdstate_release(int fd) {
125 ot_fdslot *slot = fdstate_slot(fd);
126 uint64_t word;
127
128 if (!slot)
129 return;
130
131 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
132 atomic_store_explicit(&slot->state, FD_SET_ST(word, FD_IDLE), memory_order_release);
133}
134
135int fdstate_claim_expired(int fd, ot_time now) {
136 ot_fdslot *slot = fdstate_slot(fd);
137 uint64_t word;
138 ot_time expiry;
139
140 if (!slot)
141 return 0;
142
143 /* Reading before asking for the slot keeps the common case, a descriptor
144 that has not expired, off the bus entirely */
145 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
146 if (FD_ST(word) != FD_IDLE)
147 return 0;
148 expiry = atomic_load_explicit(&slot->expiry, memory_order_relaxed);
149 if (!expiry || expiry > now)
150 return 0;
151
152 /* Nothing can move the expiry while the slot sits at FD_IDLE, so winning
153 the slot is the last word */
154 return atomic_compare_exchange_strong_explicit(&slot->state, &word, FD_SET_ST(word, FD_CLOSING), memory_order_acquire,
155 memory_order_relaxed);
156}
157
158void fdstate_close(int fd) {
159 ot_fdslot *slot = fdstate_slot(fd);
160 uint64_t word;
161
162 if (!slot)
163 return;
164
165 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
166 atomic_store_explicit(&slot->cookie, NULL, memory_order_relaxed);
167 atomic_store_explicit(&slot->expiry, 0, memory_order_relaxed);
168 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word) + 1, FD_FREE), memory_order_release);
169}
170
171int fdstate_next_expired(ot_time now, int *cursor) {
172 size_t highest = atomic_load_explicit(&g_slot_highest, memory_order_relaxed);
173
174 for (; (size_t)*cursor <= highest; ++*cursor)
175 if (fdstate_claim_expired(*cursor, now))
176 return (*cursor)++;
177
178 return -1;
179}
180
181void *fdstate_cookie(int fd) {
182 ot_fdslot *slot = fdstate_slot(fd);
183
184 /* Pairs with the release in fdstate_set_cookie and fdstate_open */
185 return slot ? atomic_load_explicit(&slot->cookie, memory_order_acquire) : NULL;
186}
187
188void fdstate_set_cookie(int fd, void *cookie) {
189 ot_fdslot *slot = fdstate_slot(fd);
190
191 /* Publishes what the cookie points at to whoever reads it next, which may
192 be another thread entirely */
193 if (slot)
194 atomic_store_explicit(&slot->cookie, cookie, memory_order_release);
195}
196
197void fdstate_set_expiry(int fd, ot_time expiry) {
198 ot_fdslot *slot = fdstate_slot(fd);
199
200 if (slot)
201 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
202}