summaryrefslogtreecommitdiff
path: root/ot_fdstate.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_fdstate.c')
-rw-r--r--ot_fdstate.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/ot_fdstate.c b/ot_fdstate.c
new file mode 100644
index 0000000..6ddbea3
--- /dev/null
+++ b/ot_fdstate.c
@@ -0,0 +1,164 @@
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
40int fdstate_init(void) {
41 struct rlimit limit;
42 size_t count = FD_SLOTS_MAX;
43
44 if (getrlimit(RLIMIT_NOFILE, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur < FD_SLOTS_MAX)
45 count = (size_t)limit.rlim_cur;
46
47 g_slots = mmap(NULL, count * sizeof(ot_fdslot), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
48 if (g_slots == MAP_FAILED) {
49 g_slots = NULL;
50 return -1;
51 }
52
53 g_slot_count = count;
54 return 0;
55}
56
57void fdstate_deinit(void) {
58 if (g_slots)
59 munmap(g_slots, g_slot_count * sizeof(ot_fdslot));
60 g_slots = NULL;
61 g_slot_count = 0;
62}
63
64size_t fdstate_slots(void) { return g_slot_count; }
65
66static ot_fdslot *fdstate_slot(int fd) {
67 if (fd < 0 || (size_t)fd >= g_slot_count)
68 return NULL;
69 return g_slots + fd;
70}
71
72uint32_t fdstate_open(int fd, void *cookie, ot_time expiry) {
73 ot_fdslot *slot = fdstate_slot(fd);
74 uint64_t word;
75
76 if (!slot)
77 return 0;
78
79 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
80 atomic_store_explicit(&slot->cookie, cookie, memory_order_relaxed);
81 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
82 /* Publishes the two above to whoever claims the slot next */
83 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word), FD_IDLE), memory_order_release);
84
85 return FD_GEN(word);
86}
87
88int fdstate_claim(int fd, uint32_t generation) {
89 ot_fdslot *slot = fdstate_slot(fd);
90 uint64_t word;
91
92 if (!slot)
93 return 0;
94
95 word = atomic_load_explicit(&slot->state, memory_order_acquire);
96 for (;;) {
97 if (FD_GEN(word) != generation || FD_ST(word) != FD_IDLE)
98 return 0;
99 if (atomic_compare_exchange_weak_explicit(&slot->state, &word, FD_SET_ST(word, FD_BUSY), memory_order_acquire,
100 memory_order_relaxed))
101 return 1;
102 /* word now holds what was really there, so the test above runs again */
103 }
104}
105
106void fdstate_release(int fd) {
107 ot_fdslot *slot = fdstate_slot(fd);
108 uint64_t word;
109
110 if (!slot)
111 return;
112
113 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
114 atomic_store_explicit(&slot->state, FD_SET_ST(word, FD_IDLE), memory_order_release);
115}
116
117int fdstate_claim_expired(int fd, ot_time now) {
118 ot_fdslot *slot = fdstate_slot(fd);
119 uint64_t word;
120 ot_time expiry;
121
122 if (!slot)
123 return 0;
124
125 /* Reading before asking for the slot keeps the common case, a descriptor
126 that has not expired, off the bus entirely */
127 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
128 if (FD_ST(word) != FD_IDLE)
129 return 0;
130 expiry = atomic_load_explicit(&slot->expiry, memory_order_relaxed);
131 if (!expiry || expiry > now)
132 return 0;
133
134 /* Nothing can move the expiry while the slot sits at FD_IDLE, so winning
135 the slot is the last word */
136 return atomic_compare_exchange_strong_explicit(&slot->state, &word, FD_SET_ST(word, FD_CLOSING), memory_order_acquire,
137 memory_order_relaxed);
138}
139
140void fdstate_close(int fd) {
141 ot_fdslot *slot = fdstate_slot(fd);
142 uint64_t word;
143
144 if (!slot)
145 return;
146
147 word = atomic_load_explicit(&slot->state, memory_order_relaxed);
148 atomic_store_explicit(&slot->cookie, NULL, memory_order_relaxed);
149 atomic_store_explicit(&slot->expiry, 0, memory_order_relaxed);
150 atomic_store_explicit(&slot->state, FD_WORD(FD_GEN(word) + 1, FD_FREE), memory_order_release);
151}
152
153void *fdstate_cookie(int fd) {
154 ot_fdslot *slot = fdstate_slot(fd);
155
156 return slot ? atomic_load_explicit(&slot->cookie, memory_order_relaxed) : NULL;
157}
158
159void fdstate_set_expiry(int fd, ot_time expiry) {
160 ot_fdslot *slot = fdstate_slot(fd);
161
162 if (slot)
163 atomic_store_explicit(&slot->expiry, expiry, memory_order_relaxed);
164}