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