summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opentracker.c42
-rw-r--r--ot_event.c2
-rw-r--r--ot_fdstate.c7
-rw-r--r--ot_fdstate.h3
-rw-r--r--ot_http.c29
-rw-r--r--ot_http.h7
-rw-r--r--tests/t_event.c23
7 files changed, 94 insertions, 19 deletions
diff --git a/opentracker.c b/opentracker.c
index ec92fe0..b47759e 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -190,29 +190,45 @@ static void handle_write(const int64_t sock) {
190 http_close(sock); 190 http_close(sock);
191} 191}
192 192
193/* The fast path, assuming that the complete request has usually arrived by the time a
194 connection is accepted, so it is answered here outside the event loop. The more load
195 a tracker has, the more likely it is that by the time it accepts, the roundtrip to
196 the peer has completed.
197 If the request could not be completely handled, the stack cookie is promoted to heap
198 and the connection handed over to the event loop to finish */
193static void handle_accept(const int64_t serversocket, struct ot_workstruct *ws) { 199static void handle_accept(const int64_t serversocket, struct ot_workstruct *ws) {
194 struct http_data *cookie; 200 int64_t sock;
195 int64_t sock; 201 ot_ip6 ip;
196 ot_ip6 ip; 202 uint16_t port;
197 uint16_t port;
198 203
199 while ((sock = socket_accept(serversocket, ip, &port)) != -1) { 204 while ((sock = socket_accept(serversocket, ip, &port)) != -1) {
205 struct http_data cookie;
206 uint32_t generation;
200 207
201 if (!(cookie = (struct http_data *)calloc(1, sizeof(struct http_data)))) { 208 memset(&cookie, 0, sizeof(cookie));
202 close((int)sock); 209 memcpy(cookie.ip, ip, sizeof(ot_ip6));
203 continue; 210 cookie.interest = EVENT_IN;
204 } 211 cookie.flag = STRUCT_HTTP_FLAG_ONSTACK;
205 memcpy(cookie->ip, ip, sizeof(ot_ip6));
206 cookie->interest = EVENT_IN;
207 212
208 if (event_add((int)sock, EVENT_IN, fdstate_open(sock, cookie, time_future_seconds(OT_CLIENT_TIMEOUT))) == -1) { 213 generation = fdstate_open(sock, &cookie, time_future_seconds(OT_CLIENT_TIMEOUT));
214 if (!fdstate_claim((int)sock, generation)) {
209 fdstate_close(sock); 215 fdstate_close(sock);
210 free(cookie); 216 event_close((int)sock);
211 close((int)sock);
212 continue; 217 continue;
213 } 218 }
214 219
215 stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); 220 stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
221
222 handle_read(sock, ws);
223
224 /* Gone already if it was answered and closed inline */
225 if (fdstate_generation((int)sock) != generation)
226 continue;
227
228 if (http_adopt(sock, &cookie) == -1)
229 http_close(sock);
230 else
231 fdstate_release((int)sock);
216 } 232 }
217} 233}
218 234
diff --git a/ot_event.c b/ot_event.c
index 8f55817..b4c4a55 100644
--- a/ot_event.c
+++ b/ot_event.c
@@ -171,7 +171,7 @@ static int event_change(int fd, int what, uint32_t generation, int deleting) {
171 return 0; 171 return 0;
172} 172}
173 173
174int event_add(int fd, int what, uint32_t generation) { return event_change(fd, what, generation, 0); } 174int event_add(int fd, int what, uint32_t generation) { return what ? event_change(fd, what, generation, 0) : 0; }
175int event_modify(int fd, int what, uint32_t generation) { return event_change(fd, what, generation, 0); } 175int event_modify(int fd, int what, uint32_t generation) { return event_change(fd, what, generation, 0); }
176int event_del(int fd) { return event_change(fd, 0, 0, 1); } 176int event_del(int fd) { return event_change(fd, 0, 0, 1); }
177 177
diff --git a/ot_fdstate.c b/ot_fdstate.c
index 537bcb0..7d742da 100644
--- a/ot_fdstate.c
+++ b/ot_fdstate.c
@@ -186,6 +186,13 @@ void *fdstate_cookie(int fd) {
186 return slot ? atomic_load_explicit(&slot->cookie, memory_order_relaxed) : NULL; 186 return slot ? atomic_load_explicit(&slot->cookie, memory_order_relaxed) : NULL;
187} 187}
188 188
189void fdstate_set_cookie(int fd, void *cookie) {
190 ot_fdslot *slot = fdstate_slot(fd);
191
192 if (slot)
193 atomic_store_explicit(&slot->cookie, cookie, memory_order_relaxed);
194}
195
189void fdstate_set_expiry(int fd, ot_time expiry) { 196void fdstate_set_expiry(int fd, ot_time expiry) {
190 ot_fdslot *slot = fdstate_slot(fd); 197 ot_fdslot *slot = fdstate_slot(fd);
191 198
diff --git a/ot_fdstate.h b/ot_fdstate.h
index c09f018..93890d1 100644
--- a/ot_fdstate.h
+++ b/ot_fdstate.h
@@ -56,6 +56,9 @@ int fdstate_next_expired(ot_time now, int *cursor);
56 56
57/* Both only valid while the caller holds the descriptor. */ 57/* Both only valid while the caller holds the descriptor. */
58void *fdstate_cookie(int fd); 58void *fdstate_cookie(int fd);
59
60/* Hand the slot a different cookie, for a connection whose state has moved */
61void fdstate_set_cookie(int fd, void *cookie);
59void fdstate_set_expiry(int fd, ot_time expiry); 62void fdstate_set_expiry(int fd, ot_time expiry);
60 63
61#endif 64#endif
diff --git a/ot_http.c b/ot_http.c
index 66f0102..bb6ff24 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -39,9 +39,25 @@ ssize_t g_stats_path_len;
39 39
40enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 }; 40enum { SUCCESS_HTTP_HEADER_LENGTH = 80, SUCCESS_HTTP_SIZE_OFF = 17 };
41 41
42/* Give up a connection: the one place that knows everything hanging off it. 42int http_adopt(const int64_t sock, struct http_data *cookie) {
43 The slot is released before anything is freed, so that a late event or the 43 struct http_data *heap = malloc(sizeof(*heap));
44 expiry sweep finds a closed descriptor rather than a freed cookie. */ 44
45 if (!heap)
46 return -1;
47
48 *heap = *cookie;
49 heap->flag &= ~STRUCT_HTTP_FLAG_ONSTACK;
50 fdstate_set_cookie((int)sock, heap);
51
52 if (event_add((int)sock, heap->interest, fdstate_generation((int)sock)) == -1) {
53 fdstate_set_cookie((int)sock, cookie);
54 free(heap);
55 return -1;
56 }
57 return 0;
58}
59
60/* Give up a connection: the one place that knows everything hanging off it. */
45void http_close(const int64_t sock) { 61void http_close(const int64_t sock) {
46 struct http_data *cookie = fdstate_cookie(sock); 62 struct http_data *cookie = fdstate_cookie(sock);
47 63
@@ -54,7 +70,8 @@ void http_close(const int64_t sock) {
54 buf_reset(&cookie->request); 70 buf_reset(&cookie->request);
55 if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) 71 if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))
56 mutex_workqueue_canceltask(sock); 72 mutex_workqueue_canceltask(sock);
57 free(cookie); 73 if (!(cookie->flag & STRUCT_HTTP_FLAG_ONSTACK))
74 free(cookie);
58 } 75 }
59 76
60 event_close((int)sock); 77 event_close((int)sock);
@@ -140,6 +157,10 @@ void http_interest_set(const int64_t sock, struct http_data *cookie, int what) {
140 if (cookie->interest == what) 157 if (cookie->interest == what)
141 return; 158 return;
142 cookie->interest = what; 159 cookie->interest = what;
160 /* Still on a stack, so not in the event loop yet: whoever adopts this
161 connection registers whatever it asked for last */
162 if (cookie->flag & STRUCT_HTTP_FLAG_ONSTACK)
163 return;
143 event_modify((int)sock, what, fdstate_generation((int)sock)); 164 event_modify((int)sock, what, fdstate_generation((int)sock));
144} 165}
145 166
diff --git a/ot_http.h b/ot_http.h
index ba16de4..82d645b 100644
--- a/ot_http.h
+++ b/ot_http.h
@@ -22,7 +22,8 @@ typedef enum {
22 STRUCT_HTTP_FLAG_ZSTD = 8, 22 STRUCT_HTTP_FLAG_ZSTD = 8,
23 STRUCT_HTTP_FLAG_CHUNKED = 16, 23 STRUCT_HTTP_FLAG_CHUNKED = 16,
24 STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER = 32, 24 STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER = 32,
25 STRUCT_HTTP_FLAG_KEEPALIVE = 64 25 STRUCT_HTTP_FLAG_KEEPALIVE = 64,
26 STRUCT_HTTP_FLAG_ONSTACK = 128
26} STRUCT_HTTP_FLAG; 27} STRUCT_HTTP_FLAG;
27 28
28struct http_data { 29struct http_data {
@@ -39,6 +40,10 @@ void http_interest_set(const int64_t s, struct http_data *cookie, int
39/* Close a connection and release everything that belongs to it */ 40/* Close a connection and release everything that belongs to it */
40void http_close(const int64_t s); 41void http_close(const int64_t s);
41 42
43/* Promote a stack cookie to a heap one for persistence and handing it over
44 to the event loop. */
45int http_adopt(const int64_t s, struct http_data *cookie);
46
42ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws); 47ssize_t http_handle_request(const int64_t s, struct ot_workstruct *ws);
43ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial); 48ssize_t http_sendiovecdata(const int64_t s, struct ot_workstruct *ws, size_t iovec_entries, struct iovec *iovector, int is_partial);
44ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code); 49ssize_t http_issue_error(const int64_t s, struct ot_workstruct *ws, int code);
diff --git a/tests/t_event.c b/tests/t_event.c
index ad3948f..706a285 100644
--- a/tests/t_event.c
+++ b/tests/t_event.c
@@ -241,6 +241,28 @@ static void case_close_takes_it_out(void) {
241 pair_close(); 241 pair_close();
242} 242}
243 243
244/* A descriptor can be taken on before anybody waits for anything on it: the
245 full scrape parks a connection while a worker thread builds the reply. The
246 mechanisms disagree about what registering nothing means, so what is
247 checked is that asking for a filter afterwards still works. */
248static void case_add_nothing_then_ask(void) {
249 test_case("a descriptor added with no interest can be given some later");
250 pair_open();
251 CHECK(sv[0] != -1);
252 CHECK_INT(event_add(sv[0], 0, 7), 0);
253 CHECK_INT((int)write(sv[1], "x", 1), 1);
254 CHECK_INT(wait_for(sv[0], EVENT_IN, 200, NULL), 0);
255
256 CHECK_INT(event_modify(sv[0], EVENT_IN, 7), 0);
257 {
258 uint32_t generation = 0;
259 CHECK_INT(wait_for(sv[0], EVENT_IN, 500, &generation), 1);
260 CHECK_INT((int)generation, 7);
261 }
262 event_del(sv[0]);
263 pair_close();
264}
265
244void test_event(void) { 266void test_event(void) {
245 CHECK_INT(event_init(), 0); 267 CHECK_INT(event_init(), 0);
246 CHECK(event_mechanism() != NULL); 268 CHECK(event_mechanism() != NULL);
@@ -254,6 +276,7 @@ void test_event(void) {
254 case_timeout(); 276 case_timeout();
255 case_unknown_descriptor(); 277 case_unknown_descriptor();
256 case_close_takes_it_out(); 278 case_close_takes_it_out();
279 case_add_nothing_then_ask();
257 280
258 event_deinit(); 281 event_deinit();
259} 282}