summaryrefslogtreecommitdiff
path: root/ot_fdstate.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_fdstate.c')
-rw-r--r--ot_fdstate.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/ot_fdstate.c b/ot_fdstate.c
new file mode 100644
index 0000000..7f73d3d
--- /dev/null
+++ b/ot_fdstate.c
@@ -0,0 +1,188 @@
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
102int fdstate_claim(int fd, uint32_t generation) {
103 ot_fdslot *slot = fdstate_slot(fd);
104 uint64_t word;
105
106 if (!slot)
107 return 0;
108
109 word = atomic_load_explicit(&slot->state, memory_order_acquire);
110 for (;;) {
111 if (FD_GEN(word) != generation || FD_ST(word) != FD_IDLE)
112 return 0;
113 if (atomic_compare_exchange_weak_explicit(&slot->state, &word, FD_SET_ST(word, FD_BUSY), memory_order_acquire,
114 memory_order_relaxed))
115 return 1;
116 /* word now holds what was really there, so the test above runs again */
117 }
118}
119
120void fdstate_release(int fd) {
121 ot_fdslot *slot = fdstate_slot(fd);
122 uint64_t word;
123
124 if (!slot)
125 return;
126
127 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
128 atomic_store_explicit(&slot->state, FD_SET_ST(word, FD_IDLE), memory_order_release);
129}
130
131int fdstate_claim_expired(int fd, ot_time now) {
132 ot_fdslot *slot = fdstate_slot(fd);
133 uint64_t word;
134 ot_time expiry;
135
136 if (!slot)
137 return 0;
138
139 /* Reading before asking for the slot keeps the common case, a descriptor
140 that has not expired, off the bus entirely */
141 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
142 if (FD_ST(word) != FD_IDLE)
143 return 0;
144 expiry = atomic_load_explicit(&slot->expiry, memory_order_relaxed);
145 if (!expiry || expiry > now)
146 return 0;
147
148 /* Nothing can move the expiry while the slot sits at FD_IDLE, so winning
149 the slot is the last word */
150 return atomic_compare_exchange_strong_explicit(&slot->state, &word, FD_SET_ST(word, FD_CLOSING), memory_order_acquire,
151 memory_order_relaxed);
152}
153
154void fdstate_close(int fd) {
155 ot_fdslot *slot = fdstate_slot(fd);
156 uint64_t word;
157
158 if (!slot)
159 return;
160
161 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
162 atomic_store_explicit(&slot->cookie, NULL, memory_order_relaxed);
163 atomic_store_explicit(&slot->expiry, 0, memory_order_relaxed);
164 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word) + 1, FD_FREE), memory_order_release);
165}
166
167int fdstate_next_expired(ot_time now, int *cursor) {
168 size_t highest = atomic_load_explicit(&g_slot_highest, memory_order_relaxed);
169
170 for (; (size_t)*cursor <= highest; ++*cursor)
171 if (fdstate_claim_expired(*cursor, now))
172 return (*cursor)++;
173
174 return -1;
175}
176
177void *fdstate_cookie(int fd) {
178 ot_fdslot *slot = fdstate_slot(fd);
179
180 return slot ? atomic_load_explicit(&slot->cookie, memory_order_relaxed) : NULL;
181}
182
183void fdstate_set_expiry(int fd, ot_time expiry) {
184 ot_fdslot *slot = fdstate_slot(fd);
185
186 if (slot)
187 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
188}