From 9528f65b322ad566dad34c78dbb67c29927c4839 Mon Sep 17 00:00:00 2001 From: phani Date: Sat, 11 Jan 2020 14:32:45 -0500 Subject: [PATCH] Synchronous I/O for http request/response fixes 99%tile latencies * Async I/O is being used for reading and writing http request and response respectively. This causes the worker core to steal new tasks as when one sandbox blocks on "read" or "write". This causes true round-robin on timer interrupts, leading to those high latencies. * Ideally, the request/response should be synchronous I belive because we are expected to have "low"/near-realtime latencies and the read/writes are only performed when a client connects, so ideally not block! Plus, we expect only small data transfers (in KBs) so that should also support synchronous request/response processing. --- runtime/Makefile | 1 + runtime/include/http.h | 5 +++++ runtime/include/http_api.h | 6 +++--- runtime/include/sandbox.h | 5 ++++- runtime/include/types.h | 2 +- runtime/src/http.c | 24 +++++++++++++++++++++++- runtime/src/runtime.c | 32 ++++++++++++++++++++++++++------ runtime/src/sandbox.c | 35 +++++++++++++++++------------------ runtime/src/softint.c | 2 +- 9 files changed, 81 insertions(+), 31 deletions(-) diff --git a/runtime/Makefile b/runtime/Makefile index f0816a8..c8d4de4 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -16,6 +16,7 @@ CFLAGS += -D_GNU_SOURCE #CFLAGS += -DNOSTDIO #CFLAGS += -DSTANDALONE CFLAGS += -DUSE_UVIO +#CFLAGS += -DUSE_HTTP_UVIO -DUSE_HTTP_SYNC CFLAGS += -DSBOX_SCALE_ALLOC #CFLAGS += -DUSE_SYSCALL #CFLAGS += -DPREEMPT_DISABLE diff --git a/runtime/include/http.h b/runtime/include/http.h index 04b9e4d..ac0fd09 100644 --- a/runtime/include/http.h +++ b/runtime/include/http.h @@ -4,6 +4,7 @@ #include #include #include +#include /* all in-memory ptrs.. don't mess around with that! */ struct http_header { @@ -34,7 +35,11 @@ struct http_response { int bodylen; char *status; int stlen; +#ifdef USE_HTTP_UVIO uv_buf_t bufs[HTTP_HEADERS_MAX * 2 + 3]; //max headers, one line for status code, remaining for body! +#else + struct iovec bufs[HTTP_HEADERS_MAX * 2 + 3]; +#endif }; #endif /* SFRT_HTTP_H */ diff --git a/runtime/include/http_api.h b/runtime/include/http_api.h index ba7c493..2e99a0d 100644 --- a/runtime/include/http_api.h +++ b/runtime/include/http_api.h @@ -8,7 +8,7 @@ int http_request_parse_sb(struct sandbox *s, size_t l); int http_response_header_set_sb(struct sandbox *s, char *h, int len); int http_response_body_set_sb(struct sandbox *s, char *body, int len); int http_response_status_set_sb(struct sandbox *s, char *status, int len); -int http_response_uv_sb(struct sandbox *s); +int http_response_vector_sb(struct sandbox *s); void http_init(void); @@ -37,9 +37,9 @@ http_response_status_set(char *status, int len) } static inline int -http_response_uv(void) +http_response_vector(void) { - return http_response_uv_sb(sandbox_current()); + return http_response_vector_sb(sandbox_current()); } static inline int diff --git a/runtime/include/sandbox.h b/runtime/include/sandbox.h index e4b99c0..5e348d2 100644 --- a/runtime/include/sandbox.h +++ b/runtime/include/sandbox.h @@ -193,9 +193,12 @@ sandbox_args(void) //void sandbox_run(struct sandbox *s); void *sandbox_run_func(void *data); -struct sandbox *sandbox_schedule(void); +struct sandbox *sandbox_schedule(int interrupt); void sandbox_block(void); void sandbox_wakeup(sandbox_t *sb); +// called in sandbox_entry() before and after fn() execution +// for http request/response processing using uvio +void sandbox_block_http(void); void sandbox_response(void); // should be the entry-point for each sandbox so it can do per-sandbox mem/etc init. diff --git a/runtime/include/types.h b/runtime/include/types.h index b2e34a6..27d5839 100644 --- a/runtime/include/types.h +++ b/runtime/include/types.h @@ -116,7 +116,7 @@ typedef enum { #define JSON_ELE_MAX 16 // FIXME: some naive work-stealing here.. -#define SBOX_PULL_MAX 16 +#define SBOX_PULL_MAX 1 #define SBOX_MAX_OPEN 32 #define SBOX_PREOPEN_MAGIC (707707707) // reads lol lol lol upside down diff --git a/runtime/src/http.c b/runtime/src/http.c index 17a3fb4..806de88 100644 --- a/runtime/src/http.c +++ b/runtime/src/http.c @@ -163,12 +163,13 @@ http_response_status_set_sb(struct sandbox *c, char *status, int len) } int -http_response_uv_sb(struct sandbox *c) +http_response_vector_sb(struct sandbox *c) { int nb = 0; #ifndef STANDALONE struct http_response *r = &c->rsi; +#ifdef USE_HTTP_UVIO r->bufs[nb] = uv_buf_init(r->status, r->stlen); nb++; @@ -183,6 +184,27 @@ http_response_uv_sb(struct sandbox *c) r->bufs[nb] = uv_buf_init(r->status + r->stlen - 2, 2); //for crlf nb++; } +#else + r->bufs[nb].iov_base = r->status; + r->bufs[nb].iov_len = r->stlen; + nb++; + + for (int i = 0; i < r->nheaders; i++) { + r->bufs[nb].iov_base = r->headers[i].hdr; + r->bufs[nb].iov_len = r->headers[i].len; + nb++; + } + + if (r->body) { + r->bufs[nb].iov_base = r->body; + r->bufs[nb].iov_len = r->bodylen; + nb++; + + r->bufs[nb].iov_base = r->status + r->stlen - 2; + r->bufs[nb].iov_len = 2; + nb++; + } +#endif #endif return nb; diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index 7ff4347..4683eda 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -87,10 +87,12 @@ sandbox_io_nowait(void) } struct sandbox * -sandbox_schedule(void) +sandbox_schedule(int interrupt) { struct sandbox *s = NULL; if (ps_list_head_empty(&runq)) { + // this is in an interrupt context, don't steal work here! + if (interrupt) return NULL; if (sandbox_pull() == 0) { //debuglog("[null: null]\n"); return NULL; @@ -130,7 +132,7 @@ sandbox_schedule_io(void) if (!in_callback) sandbox_io_nowait(); softint_disable(); - struct sandbox *s = sandbox_schedule(); + struct sandbox *s = sandbox_schedule(0); softint_enable(); assert(s == NULL || s->state == SANDBOX_RUNNABLE); @@ -143,11 +145,12 @@ sandbox_wakeup(sandbox_t *s) #ifndef STANDALONE softint_disable(); debuglog("[%p: %s]\n", s, s->mod->name); - // perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist.. + if (s->state != SANDBOX_BLOCKED) goto done; assert(s->state == SANDBOX_BLOCKED); assert(ps_list_singleton_d(s)); s->state = SANDBOX_RUNNABLE; ps_list_head_append_d(&runq, s); +done: softint_enable(); #endif } @@ -156,13 +159,12 @@ void sandbox_block(void) { #ifndef STANDALONE - // perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist.. assert(in_callback == 0); softint_disable(); struct sandbox *c = sandbox_current(); ps_list_rem_d(c); c->state = SANDBOX_BLOCKED; - struct sandbox *s = sandbox_schedule(); + struct sandbox *s = sandbox_schedule(0); debuglog("[%p: %s, %p: %s]\n", c, c->mod->name, s, s ? s->mod->name: ""); softint_enable(); sandbox_switch(s); @@ -171,6 +173,24 @@ sandbox_block(void) #endif } +void +sandbox_block_http(void) +{ +#ifdef USE_HTTP_UVIO +#ifdef USE_HTTP_SYNC + // realistically, we're processing all async I/O on this core when a sandbox blocks on http processing, not great! + // if there is a way (TODO), perhaps RUN_ONCE and check if your I/O is processed, if yes, return + // else do async block! + uv_run(runtime_uvio(), UV_RUN_DEFAULT); +#else + sandbox_block(); +#endif +#else + assert(0); + //it should not be called if not using uvio for http +#endif +} + void __attribute__((noinline)) __attribute__((noreturn)) sandbox_switch_preempt(void) { @@ -248,7 +268,7 @@ sandbox_exit(void) sandbox_local_stop(curr); curr->state = SANDBOX_RETURNED; // free resources from "main function execution", as stack still in use. - struct sandbox *n = sandbox_schedule(); + struct sandbox *n = sandbox_schedule(0); assert(n != curr); softint_enable(); //sandbox_local_end(curr); diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index 4502ed6..b854d04 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -133,11 +133,11 @@ sandbox_client_request_get(void) struct sandbox *curr = sandbox_current(); curr->rr_data_len = 0; -#ifndef USE_UVIO +#ifndef USE_HTTP_UVIO int r = 0; r = recv(curr->csock, (curr->req_resp_data), curr->mod->max_req_sz, 0); if (r <= 0) { - perror("recv"); + if (r < 0) perror("recv1"); return r; } while (r > 0) { @@ -148,13 +148,13 @@ sandbox_client_request_get(void) r = recv(curr->csock, (curr->req_resp_data + r), curr->mod->max_req_sz - r, 0); if (r < 0) { - perror("recv"); + perror("recv2"); return r; } } #else int r = uv_read_start((uv_stream_t *)&curr->cuv, sb_alloc_callback, sb_read_callback); - sandbox_block(); + sandbox_block_http(); if (curr->rr_data_len == 0) return 0; #endif @@ -170,15 +170,6 @@ sandbox_client_response_set(void) #ifndef STANDALONE struct sandbox *curr = sandbox_current(); -#ifndef USE_UVIO - strcpy(curr->req_resp_data + curr->rr_data_len, "HTTP/1.1 200 OK\r\n"); - - // TODO: response set in req_resp_data - curr->rr_data_len += strlen("HTTP/1.1 200 OK\r\n"); - - int r = send(curr->csock, curr->req_resp_data, curr->rr_data_len, 0); - if (r < 0) perror("send"); -#else int bodylen = curr->rr_data_len; if (bodylen > 0) { http_response_body_set(curr->req_resp_data, bodylen); @@ -217,10 +208,14 @@ sandbox_client_response_set(void) curr->rr_data_len += strlen("HTTP/1.1 200 OK\r\n"); http_response_status_set(st, strlen("HTTP/1.1 200 OK\r\n")); + int n = http_response_vector(); +#ifndef USE_HTTP_UVIO + int r = writev(curr->csock, curr->rsi.bufs, n); + if (r < 0) perror("writev"); +#else uv_write_t req = { .data = curr, }; - int n = http_response_uv(); int r = uv_write(&req, (uv_stream_t *)&curr->cuv, curr->rsi.bufs, n, sb_write_callback); - sandbox_block(); + sandbox_block_http(); #endif return r; #else @@ -253,7 +248,11 @@ sandbox_entry(void) #ifndef STANDALONE http_parser_init(&curr->hp, HTTP_REQUEST); curr->hp.data = curr; -#ifdef USE_UVIO +#ifdef USE_HTTP_UVIO +#ifndef USE_UVIO + printf("UVIO not enabled!\n"); + assert(0); +#endif int r = uv_tcp_init(runtime_uvio(), (uv_tcp_t *)&curr->cuv); assert(r == 0); curr->cuv.data = curr; @@ -277,9 +276,9 @@ sandbox_entry(void) } #ifndef STANDALONE -#ifdef USE_UVIO +#ifdef USE_HTTP_UVIO uv_close((uv_handle_t *)&curr->cuv, sb_close_callback); - sandbox_block(); + sandbox_block_http(); #else close(curr->csock); #endif diff --git a/runtime/src/softint.c b/runtime/src/softint.c index dff8011..bf3b50c 100644 --- a/runtime/src/softint.c +++ b/runtime/src/softint.c @@ -67,7 +67,7 @@ softint_alarm_schedule(void *u) if (curr == NULL) goto done; // find a next sandbox to run.. - struct sandbox *next = sandbox_schedule(); + struct sandbox *next = sandbox_schedule(1); if (next == NULL) goto done; if (next == curr) goto done; // only this sandbox to schedule.. return to it! // save the current sandbox, state from uc!