diff --git a/runtime/include/client_socket.h b/runtime/include/client_socket.h new file mode 100644 index 0000000..276a6ff --- /dev/null +++ b/runtime/include/client_socket.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include + +#include "panic.h" +#include "debuglog.h" +#include "http_response.h" +#include "runtime.h" +#include "worker_thread.h" + + +static inline void +client_socket_close(int client_socket) +{ + int rc = epoll_ctl(worker_thread_epoll_file_descriptor, EPOLL_CTL_DEL, client_socket, NULL); + if (unlikely(rc < 0)) panic_err(); + + if (close(client_socket) < 0) debuglog("Error closing client socket - %s", strerror(errno)); +} + + +/** + * Rejects request due to admission control or error + * @param client_socket - the client we are rejecting + * @param status_code - either 503 or 400 + */ +static inline void +client_socket_send(int client_socket, int status_code) +{ + const char *response; + switch (status_code) { + case 503: + response = HTTP_RESPONSE_503_SERVICE_UNAVAILABLE; + atomic_fetch_add(&runtime_total_5XX_responses, 1); + break; + case 400: + response = HTTP_RESPONSE_400_BAD_REQUEST; + break; + default: + panic("%d is not a valid status code\n", status_code); + } + + int rc; + int sent = 0; + int to_send = strlen(response); + + while (sent < to_send) { + rc = write(client_socket, &response[sent], to_send - sent); + if (rc < 0) { + if (errno == EAGAIN) { debuglog("Unexpectedly blocking on write of %s\n", response); } + + goto send_err; + } + sent += rc; + }; + +done: + return; +send_err: + debuglog("Error sending to client: %s", strerror(errno)); + goto done; +} diff --git a/runtime/include/current_sandbox.h b/runtime/include/current_sandbox.h index 655dc1d..befdb29 100644 --- a/runtime/include/current_sandbox.h +++ b/runtime/include/current_sandbox.h @@ -5,7 +5,6 @@ void current_sandbox_close_file_descriptor(int io_handle_index); struct sandbox * current_sandbox_get(void); int current_sandbox_get_file_descriptor(int io_handle_index); -union uv_any_handle *current_sandbox_get_libuv_handle(int io_handle_index); int current_sandbox_initialize_io_handle(void); void current_sandbox_set(struct sandbox *sandbox); int current_sandbox_set_file_descriptor(int io_handle_index, int file_descriptor); diff --git a/runtime/include/http_response.h b/runtime/include/http_response.h index f02a769..fec4b6f 100644 --- a/runtime/include/http_response.h +++ b/runtime/include/http_response.h @@ -2,10 +2,6 @@ #include #include -/* Conditionally load libuv */ -#ifdef USE_HTTP_UVIO -#include -#endif #include "http.h" @@ -30,11 +26,7 @@ struct http_response { int body_length; char * status; int status_length; -#ifdef USE_HTTP_UVIO - uv_buf_t bufs[HTTP_MAX_HEADER_COUNT * 2 + 3]; /* max headers, one line for status code, remaining for body! */ -#else - struct iovec bufs[HTTP_MAX_HEADER_COUNT * 2 + 3]; -#endif + struct iovec bufs[HTTP_MAX_HEADER_COUNT * 2 + 3]; }; /*************************************************** diff --git a/runtime/include/libuv_callbacks.h b/runtime/include/libuv_callbacks.h deleted file mode 100644 index e05d9e8..0000000 --- a/runtime/include/libuv_callbacks.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "http_request.h" -#include "runtime.h" -#include "sandbox.h" -/** - * Parses data read by the libuv stream chunk-by-chunk until the message is complete - * Then stops the stream and wakes up the sandbox - * @param stream - * @param number_read bytes read - * @param buffer unused - * - * FIXME: is there some weird edge case where a UNICODE character might be split between reads? Do we care? - * Called after libuv has read a chunk of data. Issue #100 - */ -static inline void -libuv_callbacks_on_read_parse_http_request(uv_stream_t *stream, ssize_t number_read, const uv_buf_t *buffer) -{ - struct sandbox *sandbox = stream->data; - - /* Parse the chunks libuv has read on our behalf until we've parse to message end */ - if (number_read > 0) { - // FIXME: Broken by refactor to sandbox_parse_http_request changes to return code - if (sandbox_parse_http_request(sandbox, number_read) != 0) return; - sandbox->request_response_data_length += number_read; - struct http_request *rh = &sandbox->http_request; - if (!rh->message_end) return; - } - - /* When the entire message has been read, stop the stream and wakeup the sandbox */ - uv_read_stop(stream); - worker_thread_wakeup_sandbox(sandbox); -} - -/** - * On libuv close, executes this callback to wake the blocked sandbox back up - * @param stream - */ -static inline void -libuv_callbacks_on_close_wakeup_sakebox(uv_handle_t *stream) -{ - struct sandbox *sandbox = stream->data; - worker_thread_wakeup_sandbox(sandbox); -} - -/** - * On libuv shutdown, executes this callback to wake the blocked sandbox back up - * @param req shutdown request - * @param status unused in callback - */ -static inline void -libuv_callbacks_on_shutdown_wakeup_sakebox(uv_shutdown_t *req, int status) -{ - struct sandbox *sandbox = req->data; - worker_thread_wakeup_sandbox(sandbox); -} - -/** - * On libuv write, executes this callback to wake the blocked sandbox back up - * In case of error, shutdown the sandbox - * @param write shutdown request - * @param status status code - */ -static inline void -libuv_callbacks_on_write_wakeup_sandbox(uv_write_t *write, int status) -{ - struct sandbox *sandbox = write->data; - if (status < 0) { - sandbox->client_libuv_shutdown_request.data = sandbox; - uv_shutdown(&sandbox->client_libuv_shutdown_request, (uv_stream_t *)&sandbox->client_libuv_stream, - libuv_callbacks_on_shutdown_wakeup_sakebox); - return; - } - worker_thread_wakeup_sandbox(sandbox); -} - -static inline void -libuv_callbacks_on_allocate_setup_request_response_data(uv_handle_t *h, size_t suggested, uv_buf_t *buf) -{ - struct sandbox *sandbox = h->data; - size_t l = (sandbox->module->max_request_or_response_size - sandbox->request_response_data_length); - buf->base = (sandbox->request_response_data + sandbox->request_response_data_length); - buf->len = l > suggested ? suggested : l; -} diff --git a/runtime/include/module.h b/runtime/include/module.h index f9107ce..71ae8cd 100644 --- a/runtime/include/module.h +++ b/runtime/include/module.h @@ -1,7 +1,9 @@ #pragma once #include -#include +#include +#include +#include #include "http.h" #include "panic.h" diff --git a/runtime/include/panic.h b/runtime/include/panic.h index 07e558b..962956c 100644 --- a/runtime/include/panic.h +++ b/runtime/include/panic.h @@ -4,6 +4,7 @@ #include #include #include +#include #define panic(fmt, ...) \ { \ diff --git a/runtime/include/priority_queue.h b/runtime/include/priority_queue.h index f0b1791..819d84e 100644 --- a/runtime/include/priority_queue.h +++ b/runtime/include/priority_queue.h @@ -5,8 +5,6 @@ #include "runtime.h" #include "worker_thread.h" -#define MAX 4096 - /** * How to get the priority out of the generic element * We assume priority is expressed as an unsigned 64-bit integer (i.e. cycles or @@ -19,31 +17,51 @@ typedef uint64_t (*priority_queue_get_priority_fn_t)(void *element); /* We assume that priority is expressed in terms of a 64 bit unsigned integral */ struct priority_queue { + priority_queue_get_priority_fn_t get_priority_fn; + bool use_lock; lock_t lock; uint64_t highest_priority; - void * items[MAX]; - int first_free; - priority_queue_get_priority_fn_t get_priority_fn; + size_t size; + size_t capacity; + void * items[]; }; /** - * Checks if a priority queue is empty - * @param self the priority queue to check - * @returns true if empty, else otherwise + * Peek at the priority of the highest priority task without having to take the lock + * Because this is a min-heap PQ, the highest priority is the lowest 64-bit integer + * This is used to store an absolute deadline + * @returns value of highest priority value in queue or ULONG_MAX if empty */ -static inline bool -priority_queue_is_empty(struct priority_queue *self) +static inline uint64_t +priority_queue_peek(struct priority_queue *self) { - return self->highest_priority == ULONG_MAX; + return self->highest_priority; } -void priority_queue_initialize(struct priority_queue *self, priority_queue_get_priority_fn_t get_priority_fn); -int priority_queue_enqueue(struct priority_queue *self, void *value); -int priority_queue_dequeue(struct priority_queue *self, void **dequeued_element); -int priority_queue_dequeue_if_earlier(struct priority_queue *self, void **dequeued_element, uint64_t target_deadline); -int priority_queue_length(struct priority_queue *self); + +struct priority_queue * + priority_queue_initialize(size_t capacity, bool use_lock, priority_queue_get_priority_fn_t get_priority_fn); +void priority_queue_free(struct priority_queue *self); + +int priority_queue_length(struct priority_queue *self); +int priority_queue_length_nolock(struct priority_queue *self); + +int priority_queue_enqueue(struct priority_queue *self, void *value); +int priority_queue_enqueue_nolock(struct priority_queue *self, void *value); + +int priority_queue_delete(struct priority_queue *self, void *value); +int priority_queue_delete_nolock(struct priority_queue *self, void *value); + +int priority_queue_dequeue(struct priority_queue *self, void **dequeued_element); +int priority_queue_dequeue_nolock(struct priority_queue *self, void **dequeued_element); + +int priority_queue_dequeue_if_earlier(struct priority_queue *self, void **dequeued_element, uint64_t target_deadline); +int priority_queue_dequeue_if_earlier_nolock(struct priority_queue *self, void **dequeued_element, + uint64_t target_deadline); + uint64_t priority_queue_peek(struct priority_queue *self); -int priority_queue_delete(struct priority_queue *self, void *value); -int priority_queue_top(struct priority_queue *self, void **dequeued_element); + +int priority_queue_top(struct priority_queue *self, void **dequeued_element); +int priority_queue_top_nolock(struct priority_queue *self, void **dequeued_element); #endif /* PRIORITY_QUEUE_H */ diff --git a/runtime/include/sandbox.h b/runtime/include/sandbox.h index 07261ae..ab19ad6 100644 --- a/runtime/include/sandbox.h +++ b/runtime/include/sandbox.h @@ -1,10 +1,10 @@ #pragma once #include -#include #include #include "arch/context.h" +#include "client_socket.h" #include "debuglog.h" #include "deque.h" #include "http_request.h" @@ -23,8 +23,7 @@ ********************/ struct sandbox_io_handle { - int file_descriptor; - union uv_any_handle libuv_handle; + int file_descriptor; }; typedef enum @@ -97,8 +96,6 @@ struct sandbox { struct sandbox_io_handle io_handles[SANDBOX_MAX_IO_HANDLE_COUNT]; struct sockaddr client_address; /* client requesting connection! */ int client_socket_descriptor; - uv_tcp_t client_libuv_stream; - uv_shutdown_t client_libuv_shutdown_request; bool is_repeat_header; http_parser http_parser; @@ -227,7 +224,6 @@ sandbox_initialize_io_handle(struct sandbox *sandbox) } if (io_handle_index == SANDBOX_MAX_IO_HANDLE_COUNT) return -1; sandbox->io_handles[io_handle_index].file_descriptor = SANDBOX_FILE_DESCRIPTOR_PREOPEN_MAGIC; - memset(&sandbox->io_handles[io_handle_index].libuv_handle, 0, sizeof(union uv_any_handle)); return io_handle_index; } @@ -298,20 +294,6 @@ sandbox_close_file_descriptor(struct sandbox *sandbox, int io_handle_index) sandbox->io_handles[io_handle_index].file_descriptor = -1; } -/** - * Get the Libuv handle located at idx of the sandbox ith io_handle - * @param sandbox - * @param io_handle_index index of the handle containing libuv_handle??? - * @returns any libuv handle or a NULL pointer in case of error - */ -static inline union uv_any_handle * -sandbox_get_libuv_handle(struct sandbox *sandbox, int io_handle_index) -{ - if (!sandbox) return NULL; - if (io_handle_index >= SANDBOX_MAX_IO_HANDLE_COUNT || io_handle_index < 0) return NULL; - return &sandbox->io_handles[io_handle_index].libuv_handle; -} - /** * Prints key performance metrics for a sandbox to STDOUT * @param sandbox @@ -339,17 +321,7 @@ sandbox_close_http(struct sandbox *sandbox) { assert(sandbox != NULL); -#ifdef USE_HTTP_UVIO - uv_close((uv_handle_t *)&sandbox->client_libuv_stream, libuv_callbacks_on_close_wakeup_sakebox); - worker_thread_process_io(); -#else - int rc = epoll_ctl(worker_thread_epoll_file_descriptor, EPOLL_CTL_DEL, sandbox->client_socket_descriptor, NULL); - if (unlikely(rc < 0)) panic_err(); - - if (close(sandbox->client_socket_descriptor) < 0) { - panic("Error closing client socket - %s", strerror(errno)); - } -#endif + client_socket_close(sandbox->client_socket_descriptor); } diff --git a/runtime/include/sandbox_request.h b/runtime/include/sandbox_request.h index 71e3cd6..1b9c9be 100644 --- a/runtime/include/sandbox_request.h +++ b/runtime/include/sandbox_request.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "debuglog.h" #include "deque.h" diff --git a/runtime/include/worker_thread.h b/runtime/include/worker_thread.h index aa189b5..5df3f97 100644 --- a/runtime/include/worker_thread.h +++ b/runtime/include/worker_thread.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "runtime.h" #if NCORES == 1 @@ -10,10 +8,9 @@ #define WORKER_THREAD_CORE_COUNT (NCORES > 1 ? NCORES - 1 : NCORES) -extern __thread uint64_t worker_thread_lock_duration; -extern __thread uint64_t worker_thread_start_timestamp; -extern __thread uv_loop_t worker_thread_uvio_handle; -extern __thread int worker_thread_epoll_file_descriptor; +extern __thread uint64_t worker_thread_lock_duration; +extern __thread uint64_t worker_thread_start_timestamp; +extern __thread int worker_thread_epoll_file_descriptor; void *worker_thread_main(void *return_code); @@ -56,12 +53,3 @@ worker_thread_get_memory_string(uint32_t offset, uint32_t max_length) } return NULL; } - -/** - * Get global libuv handle - */ -static inline uv_loop_t * -worker_thread_get_libuv_handle(void) -{ - return &worker_thread_uvio_handle; -} diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index 6cf794a..b5aa137 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -91,15 +91,3 @@ current_sandbox_close_file_descriptor(int io_handle_index) struct sandbox *sandbox = current_sandbox_get(); sandbox_close_file_descriptor(sandbox, io_handle_index); } - -/** - * Get the Libuv handle located at idx of the sandbox ith io_handle - * @param io_handle_index index of the handle containing libuv_handle??? - * @returns any libuv handle - */ -union uv_any_handle * -current_sandbox_get_libuv_handle(int io_handle_index) -{ - struct sandbox *sandbox = current_sandbox_get(); - return sandbox_get_libuv_handle(sandbox, io_handle_index); -} diff --git a/runtime/src/global_request_scheduler_minheap.c b/runtime/src/global_request_scheduler_minheap.c index 6be64e9..f3721ae 100644 --- a/runtime/src/global_request_scheduler_minheap.c +++ b/runtime/src/global_request_scheduler_minheap.c @@ -5,7 +5,7 @@ #include "priority_queue.h" #include "runtime.h" -static struct priority_queue global_request_scheduler_minheap; +static struct priority_queue *global_request_scheduler_minheap; /** * Pushes a sandbox request to the global deque @@ -15,12 +15,11 @@ static struct priority_queue global_request_scheduler_minheap; static struct sandbox_request * global_request_scheduler_minheap_add(void *sandbox_request) { -/* This function is called by both the listener core and workers */ -#ifndef NDEBUG - if (runtime_is_worker()) assert(!software_interrupt_is_enabled()); -#endif + assert(sandbox_request); + assert(global_request_scheduler_minheap); + if (unlikely(runtime_is_worker())) panic("%s is only callable by the listener thread\n", __func__); - int return_code = priority_queue_enqueue(&global_request_scheduler_minheap, sandbox_request); + int return_code = priority_queue_enqueue(global_request_scheduler_minheap, sandbox_request); /* TODO: Propagate -1 to caller. Issue #91 */ if (return_code == -ENOSPC) panic("Request Queue is full\n"); return sandbox_request; @@ -34,7 +33,7 @@ int global_request_scheduler_minheap_remove(struct sandbox_request **removed_sandbox_request) { assert(!software_interrupt_is_enabled()); - return priority_queue_dequeue(&global_request_scheduler_minheap, (void **)removed_sandbox_request); + return priority_queue_dequeue(global_request_scheduler_minheap, (void **)removed_sandbox_request); } /** @@ -47,7 +46,7 @@ global_request_scheduler_minheap_remove_if_earlier(struct sandbox_request **remo uint64_t target_deadline) { assert(!software_interrupt_is_enabled()); - return priority_queue_dequeue_if_earlier(&global_request_scheduler_minheap, (void **)removed_sandbox_request, + return priority_queue_dequeue_if_earlier(global_request_scheduler_minheap, (void **)removed_sandbox_request, target_deadline); } @@ -60,7 +59,7 @@ global_request_scheduler_minheap_remove_if_earlier(struct sandbox_request **remo static uint64_t global_request_scheduler_minheap_peek(void) { - return priority_queue_peek(&global_request_scheduler_minheap); + return priority_queue_peek(global_request_scheduler_minheap); } uint64_t @@ -77,7 +76,7 @@ sandbox_request_get_priority_fn(void *element) void global_request_scheduler_minheap_initialize() { - priority_queue_initialize(&global_request_scheduler_minheap, sandbox_request_get_priority_fn); + global_request_scheduler_minheap = priority_queue_initialize(1000, true, sandbox_request_get_priority_fn); struct global_request_scheduler_config config = { .add_fn = global_request_scheduler_minheap_add, @@ -88,3 +87,9 @@ global_request_scheduler_minheap_initialize() global_request_scheduler_initialize(&config); } + +void +global_request_scheduler_minheap_free() +{ + priority_queue_free(global_request_scheduler_minheap); +} diff --git a/runtime/src/http_parser_settings.c b/runtime/src/http_parser_settings.c index a259aac..16ac6ba 100644 --- a/runtime/src/http_parser_settings.c +++ b/runtime/src/http_parser_settings.c @@ -1,5 +1,3 @@ -#include - #include "http.h" #include "http_request.h" #include "http_response.h" diff --git a/runtime/src/http_response.c b/runtime/src/http_response.c index df78a09..61d8cea 100644 --- a/runtime/src/http_response.c +++ b/runtime/src/http_response.c @@ -1,7 +1,4 @@ #include -#ifdef USE_HTTP_UVIO -#include -#endif #include "http_response.h" @@ -19,25 +16,6 @@ http_response_encode_as_vector(struct http_response *http_response) { int buffer_count = 0; -#ifdef USE_HTTP_UVIO - - http_response->bufs[buffer_count] = uv_buf_init(http_response->status, http_response->status_length); - buffer_count++; - for (int i = 0; i < http_response->header_count; i++) { - http_response->bufs[buffer_count] = uv_buf_init(http_response->headers[i].header, - http_response->headers[i].length); - buffer_count++; - } - - if (http_response->body) { - http_response->bufs[buffer_count] = uv_buf_init(http_response->body, http_response->body_length); - buffer_count++; - http_response->bufs[buffer_count] = uv_buf_init(http_response->status + http_response->status_length - - 2, - 2); /* for crlf */ - buffer_count++; - } -#else http_response->bufs[buffer_count].iov_base = http_response->status; http_response->bufs[buffer_count].iov_len = http_response->status_length; buffer_count++; @@ -57,7 +35,6 @@ http_response_encode_as_vector(struct http_response *http_response) http_response->bufs[buffer_count].iov_len = 2; buffer_count++; } -#endif return buffer_count; } diff --git a/runtime/src/libc/syscall.c b/runtime/src/libc/syscall.c index 69839d2..9540bd1 100644 --- a/runtime/src/libc/syscall.c +++ b/runtime/src/libc/syscall.c @@ -1,14 +1,14 @@ -#ifndef USE_HTTP_UVIO - /* * This code originally came from the aWsm compiler * It has since been updated * https://github.com/gwsystems/aWsm/blob/master/runtime/libc/libc_backing.c */ - -#include +#include #include #include +#include + +#include "current_sandbox.h" // What should we tell the child program its UID and GID are? #define UID 0xFF @@ -786,5 +786,3 @@ inner_syscall_handler(int32_t n, int32_t a, int32_t b, int32_t c, int32_t d, int return 0; } - -#endif diff --git a/runtime/src/libc/uvio.c b/runtime/src/libc/uvio.c deleted file mode 100644 index f911511..0000000 --- a/runtime/src/libc/uvio.c +++ /dev/null @@ -1,1158 +0,0 @@ -#ifdef USE_HTTP_UVIO - -#include -#include - -#include "current_sandbox.h" -#include "debuglog.h" -#include "http_request.h" -#include "panic.h" -#include "runtime.h" -#include "sandbox.h" -#include "worker_thread.h" - -// What should we tell the child program its UID and GID are? -#define UID 0xFF -#define GID 0xFE - -// Elf auxilary vector values (see google for what those are) -#define AT_NULL 0 -#define AT_IGNORE 1 -#define AT_EXECFD 2 -#define AT_PHDR 3 -#define AT_PHENT 4 -#define AT_PHNUM 5 -#define AT_PAGESZ 6 -#define AT_BASE 7 -#define AT_FLAGS 8 -#define AT_ENTRY 9 -#define AT_NOTELF 10 -#define AT_UID 11 -#define AT_EUID 12 -#define AT_GID 13 -#define AT_EGID 14 -#define AT_CLKTCK 17 -#define AT_SECURE 23 -#define AT_BASE_PLATFORM 24 -#define AT_RANDOM 25 - -// offset = a WASM ptr to memory the runtime can use -void -stub_init(int32_t offset) -{ - // What program name will we put in the auxiliary vectors - char *program_name = current_sandbox_get()->module->name; - // Copy the program name into WASM accessible memory - int32_t program_name_offset = offset; - strcpy(get_memory_ptr_for_runtime(offset, sizeof(program_name)), program_name); - offset += sizeof(program_name); - - // The construction of this is: - // evn1, env2, ..., NULL, auxv_n1, auxv_1, auxv_n2, auxv_2 ..., NULL - int32_t env_vec[] = { - // Env variables would live here, but we don't supply any - 0, - // We supply only the bare minimum AUX vectors - AT_PAGESZ, - WASM_PAGE_SIZE, - AT_UID, - UID, - AT_EUID, - UID, - AT_GID, - GID, - AT_EGID, - GID, - AT_SECURE, - 0, - AT_RANDOM, - (int32_t)rand(), // It's pretty stupid to use rand here, but w/e - 0, - }; - int32_t env_vec_offset = offset; - memcpy(get_memory_ptr_for_runtime(env_vec_offset, sizeof(env_vec)), env_vec, sizeof(env_vec)); - - module_initialize_libc(current_sandbox_get()->module, env_vec_offset, program_name_offset); -} - -// Emulated syscall implementations -static inline void * -uv_fs_get_data(uv_fs_t *req) -{ - return req->data; -} - -static inline ssize_t -uv_fs_get_result(uv_fs_t *req) -{ - return req->result; -} - -static inline uv_fs_type -uv_fs_get_type(uv_fs_t *req) -{ - return req->fs_type; -} - -#define UV_FS_REQ_INIT() \ - { \ - .data = current_sandbox_get(), .result = 0 \ - } - -static void -wasm_fs_callback(uv_fs_t *req) -{ - debuglog("[%p]\n", req->data); - worker_thread_wakeup_sandbox((struct sandbox *)req->data); -} - -// We define our own syscall numbers, because WASM uses x86_64 values even on systems that are not x86_64 -#define SYS_READ 0 -uint32_t -wasm_read(int32_t filedes, int32_t buf_offset, int32_t nbyte) -{ - if (filedes == 0) { - char * buffer = worker_thread_get_memory_ptr_void(buf_offset, nbyte); - struct sandbox * s = current_sandbox_get(); - struct http_request *r = &s->http_request; - if (r->body_length <= 0) return 0; - int l = nbyte > r->body_length ? r->body_length : nbyte; - memcpy(buffer, r->body + r->body_read_length, l); - r->body_read_length += l; - r->body_length -= l; - return l; - } - int f = current_sandbox_get_file_descriptor(filedes); - // TODO: read on other file types - uv_fs_t req = UV_FS_REQ_INIT(); - char * buffer = worker_thread_get_memory_ptr_void(buf_offset, nbyte); - - debuglog("[%p] start[%d:%d, n%d]\n", uv_fs_get_data(&req), filedes, f, nbyte); - uv_buf_t bufv = uv_buf_init(buffer, nbyte); - uv_fs_read(worker_thread_get_libuv_handle(), &req, f, &bufv, 1, -1, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - - return ret; -} - -#define SYS_WRITE 1 -int32_t -wasm_write(int32_t file_descriptor, int32_t buf_offset, int32_t buf_size) -{ - if (file_descriptor == 1 || file_descriptor == 2) { - char * buffer = worker_thread_get_memory_ptr_void(buf_offset, buf_size); - struct sandbox *s = current_sandbox_get(); - int l = s->module->max_response_size - s->request_response_data_length; - if (l > buf_size) l = buf_size; - if (l == 0) return 0; - memcpy(s->request_response_data + s->request_response_data_length, buffer, l); - s->request_response_data_length += l; - - return l; - } - int f = current_sandbox_get_file_descriptor(file_descriptor); - // TODO: read on other file types - uv_fs_t req = UV_FS_REQ_INIT(); - char * buffer = worker_thread_get_memory_ptr_void(buf_offset, buf_size); - - uv_buf_t bufv = uv_buf_init(buffer, buf_size); - uv_fs_write(worker_thread_get_libuv_handle(), &req, f, &bufv, 1, -1, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - uv_fs_req_cleanup(&req); - - return ret; -} - -#define WO_RDONLY 00 -#define WO_WRONLY 01 -#define WO_RDWR 02 -#define WO_CREAT 0100 -#define WO_EXCL 0200 -#define WO_NOCTTY 0400 -#define WO_TRUNC 01000 -#define WO_APPEND 02000 -#define WO_NONBLOCK 04000 -#define WO_DSYNC 010000 -#define WO_SYNC 04010000 -#define WO_RSYNC 04010000 -#define WO_DIRECTORY 0200000 -#define WO_NOFOLLOW 0400000 -#define WO_CLOEXEC 02000000 - -#define SYS_OPEN 2 -int32_t -wasm_open(int32_t path_off, int32_t flags, int32_t mode) -{ - uv_fs_t req = UV_FS_REQ_INIT(); - char * path = worker_thread_get_memory_string(path_off, MODULE_MAX_PATH_LENGTH); - - int iofd = current_sandbox_initialize_io_handle(); - if (iofd < 0) return -1; - int32_t modified_flags = 0; - if (flags & WO_RDONLY) modified_flags |= O_RDONLY; - if (flags & WO_WRONLY) modified_flags |= O_WRONLY; - if (flags & WO_RDWR) modified_flags |= O_RDWR; - if (flags & WO_APPEND) modified_flags |= O_APPEND; - if (flags & WO_CREAT) modified_flags |= O_CREAT; - if (flags & WO_EXCL) modified_flags |= O_EXCL; - debuglog("[%p] start[%s:%d:%d]\n", uv_fs_get_data(&req), path, flags, modified_flags); - - uv_fs_open(worker_thread_get_libuv_handle(), &req, path, modified_flags, mode, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - if (ret < 0) - current_sandbox_close_file_descriptor(iofd); - else - current_sandbox_set_file_descriptor(iofd, ret); - - return iofd; -} - -#define SYS_CLOSE 3 -int32_t -wasm_close(int32_t file_descriptor) -{ - if (file_descriptor >= 0 && file_descriptor <= 2) { return 0; } - struct sandbox * c = current_sandbox_get(); - int d = current_sandbox_get_file_descriptor(file_descriptor); - union uv_any_handle *h = current_sandbox_get_libuv_handle(file_descriptor); - uv_handle_type type = ((uv_handle_t *)h)->type; - debuglog("[%p] [%d,%d]\n", c, file_descriptor, d); - - if (type == UV_TCP) { - debuglog("[%p] close tcp\n", c); - // TODO: close! - return 0; - } else if (type == UV_UDP) { - debuglog("[%p] close udp\n", c); - // TODO: close! - return 0; - } - - uv_fs_t req = UV_FS_REQ_INIT(); - debuglog("[%p] file[%d,%d]\n", uv_fs_get_data(&req), file_descriptor, d); - uv_fs_close(worker_thread_get_libuv_handle(), &req, d, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - if (ret == 0) current_sandbox_close_file_descriptor(file_descriptor); - return ret; -} - -// What the wasm stat structure looks like -struct wasm_stat { - int64_t st_dev; - uint64_t st_ino; - uint32_t st_nlink; - - uint32_t st_mode; - uint32_t st_uid; - uint32_t st_gid; - uint32_t __pad0; - uint64_t st_rdev; - uint64_t st_size; - int32_t st_blksize; - int64_t st_blocks; - - struct { - int32_t tv_sec; - int32_t tv_nsec; - } st_atim; - struct { - int32_t tv_sec; - int32_t tv_nsec; - } st_mtim; - struct { - int32_t tv_sec; - int32_t tv_nsec; - } st_ctim; - int32_t __pad1[3]; -}; - -#define SYS_STAT 4 -// What the OSX stat structure looks like: -// struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */ -// dev_t st_dev; /* device inode resides on */ -// ino_t st_ino; /* inode's number */ -// mode_t st_mode; /* inode protection mode */ -// nlink_t st_nlink; /* number of hard links to the file */ -// uid_t st_uid; /* user-id of owner */ -// gid_t st_gid; /* group-id of owner */ -// dev_t st_rdev; /* device type, for special file inode */ -// struct timespec st_atimespec; /* time of last access */ -// struct timespec st_mtimespec; /* time of last data modification */ -// struct timespec st_ctimespec; /* time of last file status change */ -// off_t st_size; /* file size, in bytes */ -// quad_t st_blocks; /* blocks allocated for file */ -// u_long st_blksize;/* optimal file sys I/O ops blocksize */ -// u_long st_flags; /* user defined flags for file */ -// u_long st_gen; /* file generation number */ -// }; - -int32_t -wasm_stat(uint32_t path_str_offset, int32_t stat_offset) -{ - char * path = worker_thread_get_memory_string(path_str_offset, MODULE_MAX_PATH_LENGTH); - struct wasm_stat *stat_ptr = worker_thread_get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); - - struct stat stat; - int32_t res = lstat(path, &stat); - if (res == -1) return -errno; - - *stat_ptr = (struct wasm_stat){ - .st_dev = stat.st_dev, - .st_ino = stat.st_ino, - .st_nlink = stat.st_nlink, - .st_mode = stat.st_mode, - .st_uid = stat.st_uid, - .st_gid = stat.st_gid, - .st_rdev = stat.st_rdev, - .st_size = stat.st_size, - .st_blksize = stat.st_blksize, - .st_blocks = stat.st_blocks, - }; -#ifdef __APPLE__ - stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; - stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; - - stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; - stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; - - stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; - stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; -#else - stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; - stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; - - stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; - stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; - - stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; - stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; -#endif - - return res; -} - -#define SYS_FSTAT 5 -int32_t -wasm_fstat(int32_t filedes, int32_t stat_offset) -{ - struct wasm_stat *stat_ptr = worker_thread_get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); - - struct stat stat; - int d = current_sandbox_get_file_descriptor(filedes); - int32_t res = fstat(d, &stat); - if (res == -1) return -errno; - - *stat_ptr = (struct wasm_stat){ - .st_dev = stat.st_dev, - .st_ino = stat.st_ino, - .st_nlink = stat.st_nlink, - .st_mode = stat.st_mode, - .st_uid = stat.st_uid, - .st_gid = stat.st_gid, - .st_rdev = stat.st_rdev, - .st_size = stat.st_size, - .st_blksize = stat.st_blksize, - .st_blocks = stat.st_blocks, - }; -#ifdef __APPLE__ - stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; - stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; - - stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; - stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; - - stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; - stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; -#else - stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; - stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; - - stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; - stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; - - stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; - stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; -#endif - - return res; -} - -#define SYS_LSTAT 6 -int32_t -wasm_lstat(int32_t path_str_offset, int32_t stat_offset) -{ - char * path = worker_thread_get_memory_string(path_str_offset, MODULE_MAX_PATH_LENGTH); - struct wasm_stat *stat_ptr = worker_thread_get_memory_ptr_void(stat_offset, sizeof(struct wasm_stat)); - - struct stat stat; - int32_t res = lstat(path, &stat); - if (res == -1) return -errno; - - *stat_ptr = (struct wasm_stat){ - .st_dev = stat.st_dev, - .st_ino = stat.st_ino, - .st_nlink = stat.st_nlink, - .st_mode = stat.st_mode, - .st_uid = stat.st_uid, - .st_gid = stat.st_gid, - .st_rdev = stat.st_rdev, - .st_size = stat.st_size, - .st_blksize = stat.st_blksize, - .st_blocks = stat.st_blocks, - }; -#ifdef __APPLE__ - stat_ptr->st_atim.tv_sec = stat.st_atimespec.tv_sec; - stat_ptr->st_atim.tv_nsec = stat.st_atimespec.tv_nsec; - - stat_ptr->st_mtim.tv_sec = stat.st_mtimespec.tv_sec; - stat_ptr->st_mtim.tv_nsec = stat.st_mtimespec.tv_nsec; - - stat_ptr->st_ctim.tv_sec = stat.st_ctimespec.tv_sec; - stat_ptr->st_ctim.tv_nsec = stat.st_ctimespec.tv_nsec; -#else - stat_ptr->st_atim.tv_sec = stat.st_atim.tv_sec; - stat_ptr->st_atim.tv_nsec = stat.st_atim.tv_nsec; - - stat_ptr->st_mtim.tv_sec = stat.st_mtim.tv_sec; - stat_ptr->st_mtim.tv_nsec = stat.st_mtim.tv_nsec; - - stat_ptr->st_ctim.tv_sec = stat.st_ctim.tv_sec; - stat_ptr->st_ctim.tv_nsec = stat.st_ctim.tv_nsec; -#endif - - return res; -} - - -#define SYS_LSEEK 8 -int32_t -wasm_lseek(int32_t filedes, int32_t file_offset, int32_t whence) -{ - int d = current_sandbox_get_file_descriptor(filedes); - int32_t res = (int32_t)lseek(d, file_offset, whence); - - if (res == -1) return -errno; - - return res; -} - -#define SYS_MMAP 9 -uint32_t -wasm_mmap(int32_t addr, int32_t len, int32_t prot, int32_t flags, int32_t file_descriptor, int32_t offset) -{ - int d = current_sandbox_get_file_descriptor(file_descriptor); - if (file_descriptor >= 0) assert(d >= 0); - if (addr != 0) panic("parameter void *addr is not supported!\n"); - if (d != -1) panic("file mapping is not supported!\n"); - - assert(len % WASM_PAGE_SIZE == 0); - - int32_t result = local_sandbox_context_cache.linear_memory_size; - for (int i = 0; i < len / WASM_PAGE_SIZE; i++) { expand_memory(); } - - return result; -} - -#define SYS_MUNMAP 11 - -#define SYS_BRK 12 - -#define SYS_RT_SIGACTION 13 - -#define SYS_RT_SIGPROGMASK 14 - -#define SYS_IOCTL 16 -int32_t -wasm_ioctl(int32_t file_descriptor, int32_t request, int32_t data_offet) -{ - // int d = current_sandbox_get_file_descriptor(file_descriptor); - // musl libc does some ioctls to stdout, so just allow these to silently go through - // FIXME: The above is idiotic. Issue #102. - // assert(d == 1); - return 0; -} - -#define SYS_READV 19 -struct wasm_iovec { - int32_t base_offset; - int32_t len; -}; - -int32_t -wasm_readv(int32_t file_descriptor, int32_t iov_offset, int32_t iovcnt) -{ - if (file_descriptor == 0) { - // both 1 and 2 go to client. - int len = 0; - struct wasm_iovec * iov = worker_thread_get_memory_ptr_void(iov_offset, - iovcnt * sizeof(struct wasm_iovec)); - struct sandbox * s = current_sandbox_get(); - struct http_request *r = &s->http_request; - if (r->body_length <= 0) return 0; - for (int i = 0; i < iovcnt; i++) { - int l = iov[i].len > r->body_length ? r->body_length : iov[i].len; - if (l <= 0) break; - char *b = worker_thread_get_memory_ptr_void(iov[i].base_offset, iov[i].len); - // http request body! - memcpy(b, r->body + r->body_read_length + len, l); - len += l; - r->body_length -= l; - } - r->body_read_length += len; - - return len; - } - // TODO: read on other file types - int gret = 0; - int d = current_sandbox_get_file_descriptor(file_descriptor); - struct wasm_iovec *iov = worker_thread_get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec)); - - for (int i = 0; i < iovcnt; i += RUNTIME_READ_WRITE_VECTOR_LENGTH) { - uv_fs_t req = UV_FS_REQ_INIT(); - uv_buf_t bufs[RUNTIME_READ_WRITE_VECTOR_LENGTH] = { 0 }; // avoid mallocs here! - int j = 0; - - for (j = 0; j < RUNTIME_READ_WRITE_VECTOR_LENGTH && i + j < iovcnt; j++) { - bufs[j] = uv_buf_init(worker_thread_get_memory_ptr_void(iov[i + j].base_offset, iov[i + j].len), - iov[i + j].len); - } - debuglog("[%p] start[%d,%d, n%d:%d]\n", uv_fs_get_data(&req), file_descriptor, d, i, j); - uv_fs_read(worker_thread_get_libuv_handle(), &req, d, bufs, j, -1, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - if (ret < 0) return ret; - gret += ret; - } - debuglog(" gend[%d]\n", gret); - - return gret; -} - -#define SYS_WRITEV 20 -int32_t -wasm_writev(int32_t file_descriptor, int32_t iov_offset, int32_t iovcnt) -{ - struct sandbox *c = current_sandbox_get(); - if (file_descriptor == 1 || file_descriptor == 2) { - // both 1 and 2 go to client. - int len = 0; - struct wasm_iovec *iov = worker_thread_get_memory_ptr_void(iov_offset, - iovcnt * sizeof(struct wasm_iovec)); - for (int i = 0; i < iovcnt; i++) { - char *b = worker_thread_get_memory_ptr_void(iov[i].base_offset, iov[i].len); - memcpy(c->request_response_data + c->request_response_data_length, b, iov[i].len); - c->request_response_data_length += iov[i].len; - len += iov[i].len; - } - - return len; - } - // TODO: read on other file types - int d = current_sandbox_get_file_descriptor(file_descriptor); - int gret = 0; - struct wasm_iovec *iov = worker_thread_get_memory_ptr_void(iov_offset, iovcnt * sizeof(struct wasm_iovec)); - - for (int i = 0; i < iovcnt; i += RUNTIME_READ_WRITE_VECTOR_LENGTH) { - uv_fs_t req = UV_FS_REQ_INIT(); - uv_buf_t bufs[RUNTIME_READ_WRITE_VECTOR_LENGTH] = { 0 }; // avoid mallocs here! - int j = 0; - - for (j = 0; j < RUNTIME_READ_WRITE_VECTOR_LENGTH && i + j < iovcnt; j++) { - bufs[j] = uv_buf_init(worker_thread_get_memory_ptr_void(iov[i + j].base_offset, iov[i + j].len), - iov[i + j].len); - } - debuglog("[%p] start[%d,%d, n%d:%d]\n", uv_fs_get_data(&req), file_descriptor, d, i, j); - uv_fs_write(worker_thread_get_libuv_handle(), &req, d, bufs, j, -1, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - if (ret < 0) return ret; - gret += ret; - } - debuglog(" gend[%d]\n", gret); - - return gret; -} - -#define SYS_MADVISE 28 - -#define SYS_GETPID 39 -uint32_t -wasm_getpid() -{ - return (uint32_t)getpid(); -} - - -#define WF_DUPFD 0 -#define WF_GETFD 1 -#define WF_SETFD 2 -#define WF_GETFL 3 -#define WF_SETFL 4 - -#define WF_SETOWN 8 -#define WF_GETOWN 9 -#define WF_SETSIG 10 -#define WF_GETSIG 11 - -#define WF_GETLK 5 -#define WF_SETLK 6 -#define WF_SETLKW 7 - -#define SYS_FCNTL 72 -uint32_t -wasm_fcntl(uint32_t file_descriptor, uint32_t cmd, uint32_t arg_or_lock_ptr) -{ - int d = current_sandbox_get_file_descriptor(file_descriptor); - switch (cmd) { - case WF_SETFD: - // return fcntl(d, F_SETFD, arg_or_lock_ptr); - return 0; - case WF_SETLK: - return 0; - default: - panic("Invalid command %d\n", cmd); - } -} - -#define SYS_FSYNC 74 -uint32_t -wasm_fsync(uint32_t file_descriptor) -{ - int d = current_sandbox_get_file_descriptor(file_descriptor); - uv_fs_t req = UV_FS_REQ_INIT(); - debuglog("[%p] start[%d,%d]\n", uv_fs_get_data(&req), file_descriptor, d); - uv_fs_fsync(worker_thread_get_libuv_handle(), &req, d, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - - return ret; -} - -#define SYS_GETCWD 79 -uint32_t -wasm_getcwd(uint32_t buf_offset, uint32_t buf_size) -{ - char *buffer = worker_thread_get_memory_ptr_void(buf_offset, buf_size); - char *res = getcwd(buffer, buf_size); - - if (!res) return 0; - return buf_offset; -} - -#define SYS_UNLINK 87 -uint32_t -wasm_unlink(uint32_t path_str_offset) -{ - char * path = worker_thread_get_memory_string(path_str_offset, MODULE_MAX_PATH_LENGTH); - uv_fs_t req = UV_FS_REQ_INIT(); - debuglog("[%p] start[%s]\n", uv_fs_get_data(&req), path); - uv_fs_unlink(worker_thread_get_libuv_handle(), &req, path, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - - return ret; -} - -#define SYS_GETEUID 107 -uint32_t -wasm_geteuid() -{ - return (uint32_t)geteuid(); -} - -#define SYS_SET_THREAD_AREA 205 - -#define SYS_SET_TID_ADDRESS 218 - -#define SYS_GET_TIME 228 -struct wasm_time_spec { - uint64_t sec; - uint32_t nanosec; -}; - -int32_t -wasm_get_time(int32_t clock_id, int32_t timespec_off) -{ - clockid_t real_clock; - switch (clock_id) { - case 0: - real_clock = CLOCK_REALTIME; - break; - case 1: - real_clock = CLOCK_MONOTONIC; - break; - case 2: - real_clock = CLOCK_PROCESS_CPUTIME_ID; - break; - default: - assert(0); - } - - struct wasm_time_spec *timespec = worker_thread_get_memory_ptr_void(timespec_off, - sizeof(struct wasm_time_spec)); - - struct timespec native_timespec = { 0, 0 }; - int res = clock_gettime(real_clock, &native_timespec); - if (res == -1) return -errno; - - timespec->sec = native_timespec.tv_sec; - timespec->nanosec = native_timespec.tv_nsec; - - return res; -} - -#define SYS_EXIT_GROUP 231 -int32_t -wasm_exit_group(int32_t status) -{ - exit(status); - return 0; -} - -#define SYS_FCHOWN 93 -int32_t -wasm_fchown(int32_t file_descriptor, uint32_t owner, uint32_t group) -{ - int d = current_sandbox_get_file_descriptor(file_descriptor); - uv_fs_t req = UV_FS_REQ_INIT(); - debuglog("[%p] start[%d,%d]\n", uv_fs_get_data(&req), file_descriptor, d); - uv_fs_fchown(worker_thread_get_libuv_handle(), &req, d, owner, group, wasm_fs_callback); - worker_thread_block_current_sandbox(); - - int ret = uv_fs_get_result(&req); - debuglog("[%p] end[%d]\n", uv_fs_get_data(&req), ret); - uv_fs_req_cleanup(&req); - - return ret; -} - -// networking syscalls -#define SYS_SOCKET 41 -#define SYS_CONNECT 42 -#define SYS_ACCEPT 43 -#define SYS_BIND 49 -#define SYS_LISTEN 50 - -static void -wasm_connection_callback(uv_stream_t *srv, int status) -{ - struct sandbox *s = srv->data; - debuglog(" [%p]\n", s); - s->return_value = status; - worker_thread_wakeup_sandbox(s); -} - -static void -wasm_connect_callback(uv_connect_t *req, int status) -{ - // TODO: how do we use the handle in uv_connect_t ?? - struct sandbox *s = req->data; - debuglog(" [%p]\n", s); - s->return_value = status; - worker_thread_wakeup_sandbox(s); -} - -int32_t -wasm_socket(int32_t domain, int32_t type, int32_t protocol) -{ - struct sandbox *c = current_sandbox_get(); - int pfd = current_sandbox_initialize_io_handle(), file_descriptor = -1; - // TODO: use domain, type and protocol in libuv? - debuglog("[%p] => %d %d %d\n", c, domain, type, protocol); - if (pfd < 0) return pfd; - union uv_any_handle *h = current_sandbox_get_libuv_handle(pfd); - - if (type & SOCK_DGRAM) { - uv_udp_t *uh = (uv_udp_t *)h; - uh->data = c; - uv_udp_init(worker_thread_get_libuv_handle(), uh); - debuglog(" udp init done!\n"); - } else if (type & SOCK_STREAM) { - uv_tcp_t *uh = (uv_tcp_t *)h; - uh->data = c; - uv_tcp_init(worker_thread_get_libuv_handle(), uh); - debuglog(" tcp init done!\n"); - } else { - assert(0); // not supported yet! - } - - return pfd; -} - -int32_t -wasm_connect(int32_t sockfd, int32_t sockaddr_offset, int32_t addrlen) -{ - struct sandbox *c = current_sandbox_get(); - int file_descriptor = current_sandbox_get_file_descriptor(sockfd); - debuglog("[%p] [%d, %d]\n", c, sockfd, file_descriptor); - union uv_any_handle *h = current_sandbox_get_libuv_handle(sockfd); - uv_handle_type t = ((uv_handle_t *)h)->type; - - if (t == UV_TCP) { - uv_connect_t req = { .data = c }; - debuglog("[%p] connect\n", c); - int r = uv_tcp_connect(&req, (uv_tcp_t *)h, worker_thread_get_memory_ptr_void(sockaddr_offset, addrlen), - wasm_connect_callback); - worker_thread_block_current_sandbox(); - - debuglog("[%p] %d\n", c, c->return_value); - return c->return_value; - } else if (t == UV_UDP) { - debuglog(" UDP connect not implemented!\n"); - // TODO: this api is in the doc online but not in the libuv installed.. perhaps update?? - // return uv_udp_connect((uv_udp_t *)h, worker_thread_get_memory_ptr_void(sockaddr_offset, addrlen)); - } - debuglog(" unsupported\n"); - assert(0); - return -1; -} - -int32_t -wasm_accept(int32_t sockfd, int32_t sockaddr_offset, int32_t addrlen_offset) -{ - // what do we do with the sockaddr TODO: ???? - socklen_t * addrlen = worker_thread_get_memory_ptr_void(addrlen_offset, sizeof(socklen_t)); - struct sockaddr * socket_address = worker_thread_get_memory_ptr_void(sockaddr_offset, *addrlen); - union uv_any_handle *s = current_sandbox_get_libuv_handle(sockfd); - int cfd = current_sandbox_initialize_io_handle(); - if (cfd < 0) return -1; - struct sandbox *c = current_sandbox_get(); - debuglog("[%p] [%d, %d]\n", c, sockfd, current_sandbox_get_file_descriptor(sockfd)); - - // assert so we can look into whether we need to implement UDP or others.. - assert(((uv_handle_t *)s)->type == UV_TCP); - union uv_any_handle *h = current_sandbox_get_libuv_handle(cfd); - uv_tcp_init(worker_thread_get_libuv_handle(), (uv_tcp_t *)h); - debuglog("[%p] tcp init %d\n", c, cfd); - int r = uv_accept((uv_stream_t *)s, (uv_stream_t *)h); - if (r < 0) return r; - // TODO: if accept fails, what do we do with the preopened handle? - // if (r < 0) current_sandbox_close_file_descriptor(cfd); - // we've to also remove it from the runtime loop?? - - int r2 = -1, f = -1; - r2 = uv_fileno((uv_handle_t *)h, &f); - if (r2 < 0 || f < 0) assert(0); - current_sandbox_set_file_descriptor(cfd, f); - debuglog("[%p] done[%d,%d]\n", c, cfd, f); - - return cfd; -} - -int32_t -wasm_bind(int32_t sockfd, int32_t sockaddr_offset, int32_t addrlen) -{ - struct sandbox *c = current_sandbox_get(); - int file_descriptor = current_sandbox_get_file_descriptor(sockfd); - debuglog("[%p] [%d,%d]\n", c, sockfd, file_descriptor); - union uv_any_handle *h = current_sandbox_get_libuv_handle(sockfd); - uv_handle_type t = ((uv_handle_t *)h)->type; - - if (t == UV_TCP) { - debuglog("[%p] tcp\n", c); - int r1 = uv_tcp_bind((uv_tcp_t *)h, worker_thread_get_memory_ptr_void(sockaddr_offset, addrlen), - 0 /* TODO: flags */); - if (file_descriptor == SANDBOX_FILE_DESCRIPTOR_PREOPEN_MAGIC) { - int r2 = -1, f = -1; - r2 = uv_fileno((uv_handle_t *)h, &f); - debuglog("[%p] [%d,%d]\n", c, f, file_descriptor); - current_sandbox_set_file_descriptor(sockfd, f); - } - return r1; - } else if (t == UV_UDP) { - debuglog("[%p] udp\n", c); - int r1 = uv_udp_bind((uv_udp_t *)h, worker_thread_get_memory_ptr_void(sockaddr_offset, addrlen), - 0 /* TODO: flags */); - if (file_descriptor == SANDBOX_FILE_DESCRIPTOR_PREOPEN_MAGIC) { - int r2 = -1, f = -1; - r2 = uv_fileno((uv_handle_t *)h, &f); - debuglog("[%p] [%d,%d]\n", c, f, file_descriptor); - current_sandbox_set_file_descriptor(sockfd, f); - } - return r1; - } - debuglog("[%p] unimplemented\n", c); - assert(0); - return -1; -} - -int32_t -wasm_listen(int32_t sockfd, int32_t backlog) -{ - struct sandbox * c = current_sandbox_get(); - union uv_any_handle *h = current_sandbox_get_libuv_handle(sockfd); - assert(c == (struct sandbox *)(((uv_tcp_t *)h)->data)); - debuglog("[%p] [%d,%d]\n", c, sockfd, current_sandbox_get_file_descriptor(sockfd)); - uv_handle_type t = ((uv_handle_t *)h)->type; - - // assert so we can look into whether we need to implement UDP or others.. - assert(t == UV_TCP); - - int r = uv_listen((uv_stream_t *)h, backlog, wasm_connection_callback); - worker_thread_block_current_sandbox(); - - debuglog("[%p] %d\n", c, c->return_value); - return c->return_value; -} - -#define SYS_SENDTO 44 -#define SYS_RECVFROM 45 - -void -wasm_read_callback(uv_stream_t *s, ssize_t nread, const uv_buf_t *buffer) -{ - struct sandbox *c = s->data; - - debuglog("[%p] %ld %p\n", c, nread, buffer); - if (nread < 0) c->return_value = -EIO; - c->read_length = nread; - debuglog("[%p] %ld\n", c, c->read_length); - uv_read_stop(s); - worker_thread_wakeup_sandbox(c); -} - -void -wasm_write_callback(uv_write_t *req, int status) -{ - struct sandbox *c = req->data; - c->return_value = status; - debuglog("[%p] %d\n", c, status); - - worker_thread_wakeup_sandbox(c); -} - -void -wasm_udp_recv_callback(uv_udp_t *h, ssize_t nread, const uv_buf_t *buffer, const struct sockaddr *socket_address, - unsigned flags) -{ - struct sandbox *c = h->data; - - debuglog("[%p] %ld %p\n", c, nread, buffer); - if (nread < 0) c->return_value = -EIO; - c->read_length = nread; - debuglog("[%p] %ld\n", c, c->read_length); - uv_udp_recv_stop(h); - worker_thread_wakeup_sandbox(c); -} - -void -wasm_udp_send_callback(uv_udp_send_t *req, int status) -{ - struct sandbox *c = req->data; - c->return_value = status; - debuglog("[%p] %d\n", c, status); - - worker_thread_wakeup_sandbox(c); -} - -int32_t -wasm_sendto(int32_t file_descriptor, int32_t buff_offset, int32_t len, int32_t flags, int32_t sockaddr_offset, - int32_t sockaddr_len) -{ - char *buffer = worker_thread_get_memory_ptr_void(buff_offset, len); - // TODO: only support "send" api for now - assert(sockaddr_len == 0); - struct sandbox * c = current_sandbox_get(); - union uv_any_handle *h = current_sandbox_get_libuv_handle(file_descriptor); - uv_handle_type t = ((uv_handle_t *)h)->type; - debuglog("[%p] [%d,%d]\n", c, file_descriptor, current_sandbox_get_file_descriptor(file_descriptor)); - - if (t == UV_TCP) { - uv_write_t req = { - .data = c, - }; - uv_buf_t b = uv_buf_init(buffer, len); - debuglog("[%p] tcp\n", c); - int ret = uv_write(&req, (uv_stream_t *)h, &b, 1, wasm_write_callback); - worker_thread_block_current_sandbox(); - - debuglog("[%p] %d\n", c, c->return_value); - return c->return_value; - } else if (t == UV_UDP) { - uv_udp_send_t req = { - .data = c, - }; - uv_buf_t b = uv_buf_init(buffer, len); - debuglog("[%p] udp\n", c); - // TODO: sockaddr! - int r = uv_udp_send(&req, (uv_udp_t *)h, &b, 1, NULL, wasm_udp_send_callback); - worker_thread_block_current_sandbox(); - - debuglog("[%p] %d\n", c, c->return_value); - return c->return_value; - } - debuglog("[%p] unimplemented\n", c); - assert(0); - return 0; -} - -static inline void -wasm_alloc_callback(uv_handle_t *h, size_t suggested, uv_buf_t *buffer) -{ - struct sandbox *s = h->data; - - // just let it use what is passed from caller! - buffer->base = s->read_buffer; - buffer->len = s->read_size; -} - -int32_t -wasm_recvfrom(int32_t file_descriptor, int32_t buff_offset, int32_t size, int32_t flags, int32_t sockaddr_offset, - int32_t socklen_offset) -{ - char * buffer = worker_thread_get_memory_ptr_void(buff_offset, size); - socklen_t *len = worker_thread_get_memory_ptr_void(socklen_offset, sizeof(socklen_t)); - // TODO: only support "recv" api for now - assert(*len == 0); - struct sandbox * c = current_sandbox_get(); - union uv_any_handle *h = current_sandbox_get_libuv_handle(file_descriptor); - uv_handle_type t = ((uv_handle_t *)h)->type; - debuglog("[%p] [%d,%d]\n", c, file_descriptor, current_sandbox_get_file_descriptor(file_descriptor)); - - // uv stream API are not simple wrappers on read/write.. - // and there will only be one system call pending.. - // so we keep the read buffer pointers in sandbox structure.. - // for use in the callbacks.. - c->read_buffer = buffer; - c->read_size = size; - c->read_length = 0; - c->return_value = 0; - // TODO: what if stream read more than what "size" is here?? - - if (t == UV_TCP) { - ((uv_stream_t *)h)->data = c; - debuglog("[%p] tcp\n", c); - int r = uv_read_start((uv_stream_t *)h, wasm_alloc_callback, wasm_read_callback); - worker_thread_block_current_sandbox(); - debuglog("[%p] %d\n", c, c->return_value); - if (c->return_value == -EIO) { - // TODO: buffer errors?? - } - if (r >= 0 && c->return_value == 0) { return c->read_length; } - return -EIO; - } else if (t == UV_UDP) { - ((uv_udp_t *)h)->data = c; - debuglog("[%p] udp\n", c); - int r = uv_udp_recv_start((uv_udp_t *)h, wasm_alloc_callback, wasm_udp_recv_callback); - worker_thread_block_current_sandbox(); - debuglog("[%p] %d\n", c, c->return_value); - if (c->return_value == -EIO) { - // TODO: buffer errors?? - } - if (r >= 0 && c->return_value == 0) { return c->read_length; } - return -EIO; - } - debuglog("[%p] unimplemented\n", c); - assert(0); - return 0; -} - -int32_t -inner_syscall_handler(int32_t n, int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, int32_t f) -{ - int32_t res; - switch (n) { - case SYS_READ: - return wasm_read(a, b, c); - case SYS_WRITE: - return wasm_write(a, b, c); - case SYS_OPEN: - return wasm_open(a, b, c); - case SYS_CLOSE: - return wasm_close(a); - case SYS_STAT: - return wasm_stat(a, b); - case SYS_FSTAT: - return wasm_fstat(a, b); - case SYS_LSTAT: - return wasm_lstat(a, b); - case SYS_LSEEK: - return wasm_lseek(a, b, c); - case SYS_MMAP: - return wasm_mmap(a, b, c, d, e, f); - case SYS_MUNMAP: - return 0; - case SYS_BRK: - return 0; - case SYS_RT_SIGACTION: - return 0; - case SYS_RT_SIGPROGMASK: - return 0; - case SYS_IOCTL: - return wasm_ioctl(a, b, c); - case SYS_READV: - return wasm_readv(a, b, c); - case SYS_WRITEV: - return wasm_writev(a, b, c); - case SYS_MADVISE: - return 0; - case SYS_GETPID: - return wasm_getpid(); - case SYS_FCNTL: - return wasm_fcntl(a, b, c); - case SYS_FSYNC: - return wasm_fsync(a); - case SYS_UNLINK: - return wasm_unlink(a); - case SYS_GETCWD: - return wasm_getcwd(a, b); - case SYS_GETEUID: - return wasm_geteuid(); - case SYS_SET_THREAD_AREA: - return 0; - case SYS_SET_TID_ADDRESS: - return 0; - case SYS_GET_TIME: - return wasm_get_time(a, b); - case SYS_EXIT_GROUP: - return wasm_exit_group(a); - case SYS_FCHOWN: - return wasm_fchown(a, b, c); - - case SYS_SOCKET: - return wasm_socket(a, b, c); - case SYS_CONNECT: - return wasm_connect(a, b, c); - case SYS_ACCEPT: - return wasm_accept(a, b, c); - case SYS_BIND: - return wasm_bind(a, b, c); - case SYS_LISTEN: - return wasm_listen(a, b); - case SYS_SENDTO: - return wasm_sendto(a, b, c, d, e, f); - case SYS_RECVFROM: - return wasm_recvfrom(a, b, c, d, e, f); - } - - panic("syscall %d (%d, %d, %d, %d, %d, %d)\n", n, a, b, c, d, e, f); - return 0; -} - -#endif diff --git a/runtime/src/local_runqueue_list.c b/runtime/src/local_runqueue_list.c index 8e84cfa..e1595b1 100644 --- a/runtime/src/local_runqueue_list.c +++ b/runtime/src/local_runqueue_list.c @@ -1,3 +1,4 @@ +#include "client_socket.h" #include "debuglog.h" #include "local_runqueue_list.h" #include "local_runqueue.h" @@ -51,18 +52,17 @@ local_runqueue_list_get_next() if (global_request_scheduler_remove(&sandbox_request) < 0) goto err; struct sandbox *sandbox = sandbox_allocate(sandbox_request); - if (!sandbox) goto sandbox_allocate_err; + if (!sandbox) goto err; sandbox->state = SANDBOX_RUNNABLE; local_runqueue_add(sandbox); done: return sandbox; - sandbox_allocate_err: - debuglog("local_runqueue_list_get_next failed to allocate sandbox, returning request to global request " - "scheduler\n"); - global_request_scheduler_add(sandbox_request); err: + client_socket_send(sandbox_request->socket_descriptor, 503); + client_socket_close(sandbox_request->socket_descriptor); + free(sandbox_request); sandbox = NULL; goto done; } diff --git a/runtime/src/local_runqueue_minheap.c b/runtime/src/local_runqueue_minheap.c index 578f0ac..05fc340 100644 --- a/runtime/src/local_runqueue_minheap.c +++ b/runtime/src/local_runqueue_minheap.c @@ -1,5 +1,6 @@ #include +#include "client_socket.h" #include "current_sandbox.h" #include "debuglog.h" #include "global_request_scheduler.h" @@ -9,7 +10,7 @@ #include "priority_queue.h" #include "software_interrupt.h" -__thread static struct priority_queue local_runqueue_minheap; +__thread static struct priority_queue *local_runqueue_minheap; /** * Checks if the run queue is empty @@ -18,7 +19,7 @@ __thread static struct priority_queue local_runqueue_minheap; bool local_runqueue_minheap_is_empty() { - return priority_queue_is_empty(&local_runqueue_minheap); + return priority_queue_length_nolock(local_runqueue_minheap) == 0; } /** @@ -31,7 +32,7 @@ local_runqueue_minheap_add(struct sandbox *sandbox) { assert(!software_interrupt_is_enabled()); - int return_code = priority_queue_enqueue(&local_runqueue_minheap, sandbox); + int return_code = priority_queue_enqueue_nolock(local_runqueue_minheap, sandbox); /* TODO: propagate RC to caller. Issue #92 */ if (return_code == -ENOSPC) panic("Thread Runqueue is full!\n"); } @@ -45,7 +46,7 @@ static int local_runqueue_minheap_remove(struct sandbox **to_remove) { assert(!software_interrupt_is_enabled()); - return priority_queue_dequeue(&local_runqueue_minheap, (void **)to_remove); + return priority_queue_dequeue_nolock(local_runqueue_minheap, (void **)to_remove); } /** @@ -58,7 +59,7 @@ local_runqueue_minheap_delete(struct sandbox *sandbox) assert(!software_interrupt_is_enabled()); assert(sandbox != NULL); - int rc = priority_queue_delete(&local_runqueue_minheap, sandbox); + int rc = priority_queue_delete_nolock(local_runqueue_minheap, sandbox); if (rc == -1) panic("Tried to delete sandbox %lu from runqueue, but was not present\n", sandbox->id); } @@ -77,9 +78,9 @@ local_runqueue_minheap_get_next() struct sandbox * sandbox = NULL; struct sandbox_request *sandbox_request = NULL; - int sandbox_rc = priority_queue_top(&local_runqueue_minheap, (void **)&sandbox); + int sandbox_rc = priority_queue_top_nolock(local_runqueue_minheap, (void **)&sandbox); - if (sandbox_rc == -ENOENT && global_request_scheduler_peek() < ULONG_MAX) { + while (sandbox_rc == -ENOENT && global_request_scheduler_peek() < ULONG_MAX && sandbox == NULL) { /* local runqueue empty, try to pull a sandbox request */ if (global_request_scheduler_remove(&sandbox_request) < 0) { /* Assumption: Sandbox request should not be set in case of an error */ @@ -87,9 +88,14 @@ local_runqueue_minheap_get_next() goto done; } - /* Try to allocate a sandbox, returning the request on failure */ + /* Try to allocate a sandbox. Try again on failure */ sandbox = sandbox_allocate(sandbox_request); - if (!sandbox) goto sandbox_allocate_err; + if (!sandbox) { + client_socket_send(sandbox_request->socket_descriptor, 503); + client_socket_close(sandbox_request->socket_descriptor); + free(sandbox_request); + continue; + }; assert(sandbox->state == SANDBOX_INITIALIZED); sandbox_set_as_runnable(sandbox, SANDBOX_INITIALIZED); @@ -97,10 +103,6 @@ local_runqueue_minheap_get_next() done: return sandbox; -sandbox_allocate_err: - debuglog("local_runqueue_minheap_get_next failed to allocate sandbox. Adding request back to global " - "request scheduler\n"); - global_request_scheduler_add(sandbox_request); err: sandbox = NULL; goto done; @@ -132,9 +134,8 @@ local_runqueue_minheap_preempt(ucontext_t *user_context) assert(local_runqueue_minheap_is_empty() == false); bool should_enable_software_interrupt = true; - uint64_t local_deadline = priority_queue_peek(&local_runqueue_minheap); + uint64_t local_deadline = priority_queue_peek(local_runqueue_minheap); uint64_t global_deadline = global_request_scheduler_peek(); - /* If we're able to get a sandbox request with a tighter deadline, preempt the current context and run it */ struct sandbox_request *sandbox_request = NULL; if (global_deadline < local_deadline) { @@ -191,10 +192,9 @@ done: if (should_enable_software_interrupt) software_interrupt_enable(); return; err_sandbox_allocate: - assert(sandbox_request); - debuglog("local_runqueue_minheap_preempt failed to allocate sandbox, returning request to global request " - "scheduler\n"); - global_request_scheduler_add(sandbox_request); + client_socket_send(sandbox_request->socket_descriptor, 503); + client_socket_close(sandbox_request->socket_descriptor); + debuglog("local_runqueue_minheap_preempt failed to allocate sandbox\n"); err: goto done; } @@ -215,7 +215,7 @@ local_runqueue_minheap_initialize() { /* Initialize local state */ software_interrupt_disable(); - priority_queue_initialize(&local_runqueue_minheap, sandbox_get_priority); + local_runqueue_minheap = priority_queue_initialize(256, false, sandbox_get_priority); software_interrupt_enable(); /* Register Function Pointers for Abstract Scheduling API */ diff --git a/runtime/src/module.c b/runtime/src/module.c index c6155e3..92c9c50 100644 --- a/runtime/src/module.c +++ b/runtime/src/module.c @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include #include "debuglog.h" #include "http.h" diff --git a/runtime/src/priority_queue.c b/runtime/src/priority_queue.c index 49a627b..d050458 100644 --- a/runtime/src/priority_queue.c +++ b/runtime/src/priority_queue.c @@ -23,14 +23,13 @@ priority_queue_append(struct priority_queue *self, void *new_item) { assert(self != NULL); assert(new_item != NULL); - - assert(LOCK_IS_LOCKED(&self->lock)); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); int rc; - if (self->first_free >= MAX) goto err_enospc; - - self->items[self->first_free++] = new_item; + /* Add one and prefix because we use 1-based indices in our backing array */ + if (self->size + 1 == self->capacity) goto err_enospc; + self->items[++self->size] = new_item; rc = 0; done: @@ -40,6 +39,21 @@ err_enospc: goto done; } +/** + * Checks if a priority queue is empty + * @param self the priority queue to check + * @returns true if empty, else otherwise + */ +static inline bool +priority_queue_is_empty(struct priority_queue *self) +{ + assert(self != NULL); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); + assert(!runtime_is_worker() || !software_interrupt_is_enabled()); + + return self->size == 0; +} + /** * Shifts an appended value upwards to restore heap structure property * @param self the priority queue @@ -49,9 +63,15 @@ priority_queue_percolate_up(struct priority_queue *self) { assert(self != NULL); assert(self->get_priority_fn != NULL); - assert(LOCK_IS_LOCKED(&self->lock)); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); - for (int i = self->first_free - 1; + /* If there's only one element, set memoized lookup and early out */ + if (self->size == 1) { + self->highest_priority = self->get_priority_fn(self->items[1]); + return; + } + + for (int i = self->size; i / 2 != 0 && self->get_priority_fn(self->items[i]) < self->get_priority_fn(self->items[i / 2]); i /= 2) { assert(self->get_priority_fn(self->items[i]) != ULONG_MAX); void *temp = self->items[i / 2]; @@ -72,9 +92,9 @@ static inline int priority_queue_find_smallest_child(struct priority_queue *self, int parent_index) { assert(self != NULL); - assert(parent_index >= 1 && parent_index < self->first_free); + assert(parent_index >= 1 && parent_index <= self->size); assert(self->get_priority_fn != NULL); - assert(LOCK_IS_LOCKED(&self->lock)); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); int left_child_index = 2 * parent_index; int right_child_index = 2 * parent_index + 1; @@ -83,7 +103,7 @@ priority_queue_find_smallest_child(struct priority_queue *self, int parent_index int smallest_child_idx; /* If we don't have a right child or the left child is smaller, return it */ - if (right_child_index == self->first_free) { + if (right_child_index > self->size) { smallest_child_idx = left_child_index; } else if (self->get_priority_fn(self->items[left_child_index]) < self->get_priority_fn(self->items[right_child_index])) { @@ -106,12 +126,12 @@ priority_queue_percolate_down(struct priority_queue *self, int parent_index) { assert(self != NULL); assert(self->get_priority_fn != NULL); - assert(LOCK_IS_LOCKED(&self->lock)); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); assert(runtime_is_worker()); assert(!software_interrupt_is_enabled()); int left_child_index = 2 * parent_index; - while (left_child_index >= 2 && left_child_index < self->first_free) { + while (left_child_index >= 2 && left_child_index <= self->size) { int smallest_child_index = priority_queue_find_smallest_child(self, parent_index); /* Once the parent is equal to or less than its smallest child, break; */ if (self->get_priority_fn(self->items[parent_index]) @@ -125,21 +145,15 @@ priority_queue_percolate_down(struct priority_queue *self, int parent_index) parent_index = smallest_child_index; left_child_index = 2 * parent_index; } -} -/** - * Checks if a priority queue is empty - * @param self the priority queue to check - * @returns true if empty, else otherwise - */ -static inline bool -priority_queue_is_empty_locked(struct priority_queue *self) -{ - assert(self != NULL); - assert(LOCK_IS_LOCKED(&self->lock)); - assert(!runtime_is_worker() || !software_interrupt_is_enabled()); - - return self->first_free == 1; + /* Update memoized value if we touched the head */ + if (parent_index == 1) { + if (!priority_queue_is_empty(self)) { + self->highest_priority = self->get_priority_fn(self->items[1]); + } else { + self->highest_priority = ULONG_MAX; + } + } } /********************* @@ -148,24 +162,44 @@ priority_queue_is_empty_locked(struct priority_queue *self) /** * Initialized the Priority Queue Data structure - * @param self the priority_queue to initialize + * @param capacity the number of elements to store in the data structure + * @param use_lock indicates that we want a concurrent data structure * @param get_priority_fn pointer to a function that returns the priority of an element + * @return priority queue */ -void -priority_queue_initialize(struct priority_queue *self, priority_queue_get_priority_fn_t get_priority_fn) +struct priority_queue * +priority_queue_initialize(size_t capacity, bool use_lock, priority_queue_get_priority_fn_t get_priority_fn) { - assert(self != NULL); assert(get_priority_fn != NULL); assert(!runtime_is_worker() || !software_interrupt_is_enabled()); - memset(self->items, 0, sizeof(void *) * MAX); + /* Add one to capacity because this data structure ignores the element at 0 */ + struct priority_queue *self = calloc(sizeof(struct priority_queue) + sizeof(void *) * (capacity + 1), 1); - LOCK_INIT(&self->lock); - self->first_free = 1; - self->get_priority_fn = get_priority_fn; /* We're assuming a min-heap implementation, so set to larget possible value */ self->highest_priority = ULONG_MAX; + self->size = 0; + self->capacity = capacity; + self->get_priority_fn = get_priority_fn; + self->use_lock = use_lock; + + if (use_lock) LOCK_INIT(&self->lock); + + return self; +} + +/** + * Free the Priority Queue Data structure + * @param self the priority_queue to initialize + */ +void +priority_queue_free(struct priority_queue *self) +{ + assert(self != NULL); + assert(!runtime_is_worker() || !software_interrupt_is_enabled()); + + free(self); } /** @@ -173,16 +207,27 @@ priority_queue_initialize(struct priority_queue *self, priority_queue_get_priori * @returns the number of elements in the priority queue */ int -priority_queue_length(struct priority_queue *self) +priority_queue_length_nolock(struct priority_queue *self) { assert(self != NULL); assert(runtime_is_worker()); assert(!software_interrupt_is_enabled()); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); + + return self->size; +} +/** + * @param self the priority_queue + * @returns the number of elements in the priority queue + */ +int +priority_queue_length(struct priority_queue *self) +{ LOCK_LOCK(&self->lock); - int length = self->first_free - 1; + int size = priority_queue_length_nolock(self); LOCK_UNLOCK(&self->lock); - return length; + return size; } /** @@ -191,33 +236,42 @@ priority_queue_length(struct priority_queue *self) * @returns 0 on success. -ENOSPC on full. */ int -priority_queue_enqueue(struct priority_queue *self, void *value) +priority_queue_enqueue_nolock(struct priority_queue *self, void *value) { assert(self != NULL); assert(value != NULL); assert(!runtime_is_worker() || !software_interrupt_is_enabled()); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); int rc; - LOCK_LOCK(&self->lock); - if (priority_queue_append(self, value) == -ENOSPC) goto err_enospc; - /* If this is the first element we add, update the highest priority */ - if (self->first_free == 2) { - self->highest_priority = self->get_priority_fn(value); - } else { - priority_queue_percolate_up(self); - } + priority_queue_percolate_up(self); rc = 0; -release_lock: - LOCK_UNLOCK(&self->lock); done: return rc; err_enospc: rc = -ENOSPC; - goto release_lock; + goto done; +} + +/** + * @param self - the priority queue we want to add to + * @param value - the value we want to add + * @returns 0 on success. -ENOSPC on full. + */ +int +priority_queue_enqueue(struct priority_queue *self, void *value) +{ + int rc; + + LOCK_LOCK(&self->lock); + rc = priority_queue_enqueue_nolock(self, value); + LOCK_UNLOCK(&self->lock); + + return rc; } /** @@ -226,29 +280,41 @@ err_enospc: * @returns 0 on success. -1 on not found */ int -priority_queue_delete(struct priority_queue *self, void *value) +priority_queue_delete_nolock(struct priority_queue *self, void *value) { assert(self != NULL); assert(value != NULL); assert(runtime_is_worker()); assert(!software_interrupt_is_enabled()); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); - LOCK_LOCK(&self->lock); - - bool did_delete = false; - for (int i = 1; i < self->first_free; i++) { + for (int i = 1; i <= self->size; i++) { if (self->items[i] == value) { - self->items[i] = self->items[--self->first_free]; - self->items[self->first_free] = NULL; + self->items[i] = self->items[self->size]; + self->items[self->size--] = NULL; priority_queue_percolate_down(self, i); - did_delete = true; + return 0; } } + return -1; +} + +/** + * @param self - the priority queue we want to delete from + * @param value - the value we want to delete + * @returns 0 on success. -1 on not found + */ +int +priority_queue_delete(struct priority_queue *self, void *value) +{ + int rc; + + LOCK_LOCK(&self->lock); + rc = priority_queue_delete_nolock(self, value); LOCK_UNLOCK(&self->lock); - if (!did_delete) return -1; - return 0; + return rc; } /** @@ -262,6 +328,17 @@ priority_queue_dequeue(struct priority_queue *self, void **dequeued_element) return priority_queue_dequeue_if_earlier(self, dequeued_element, UINT64_MAX); } +/** + * @param self - the priority queue we want to add to + * @param dequeued_element a pointer to set to the dequeued element + * @returns RC 0 if successfully set dequeued_element, -ENOENT if empty + */ +int +priority_queue_dequeue_nolock(struct priority_queue *self, void **dequeued_element) +{ + return priority_queue_dequeue_if_earlier_nolock(self, dequeued_element, UINT64_MAX); +} + /** * @param self - the priority queue we want to add to * @param dequeued_element a pointer to set to the dequeued element @@ -269,42 +346,57 @@ priority_queue_dequeue(struct priority_queue *self, void **dequeued_element) * @returns RC 0 if successfully set dequeued_element, -ENOENT if empty or if none meet target_deadline */ int -priority_queue_dequeue_if_earlier(struct priority_queue *self, void **dequeued_element, uint64_t target_deadline) +priority_queue_dequeue_if_earlier_nolock(struct priority_queue *self, void **dequeued_element, uint64_t target_deadline) { assert(self != NULL); assert(dequeued_element != NULL); assert(self->get_priority_fn != NULL); assert(runtime_is_worker()); assert(!software_interrupt_is_enabled()); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); int return_code; - LOCK_LOCK(&self->lock); - /* If the dequeue is not higher priority (earlier timestamp) than targed_deadline, return immediately */ - if (priority_queue_is_empty_locked(self) || self->highest_priority >= target_deadline) goto err_enoent; + if (priority_queue_is_empty(self) || self->highest_priority >= target_deadline) goto err_enoent; + + *dequeued_element = self->items[1]; + self->items[1] = self->items[self->size]; + self->items[self->size--] = NULL; - *dequeued_element = self->items[1]; - self->items[1] = self->items[--self->first_free]; - self->items[self->first_free] = NULL; - /* Because of 1-based indices, first_free is 2 when there is only one element */ - if (self->first_free > 2) priority_queue_percolate_down(self, 1); + if (self->size > 1) priority_queue_percolate_down(self, 1); /* Update the highest priority */ - if (!priority_queue_is_empty_locked(self)) { + if (!priority_queue_is_empty(self)) { self->highest_priority = self->get_priority_fn(self->items[1]); } else { self->highest_priority = ULONG_MAX; } return_code = 0; -release_lock: - LOCK_UNLOCK(&self->lock); done: return return_code; err_enoent: return_code = -ENOENT; - goto release_lock; + goto done; +} + +/** + * @param self - the priority queue we want to add to + * @param dequeued_element a pointer to set to the dequeued element + * @param target_deadline the deadline that the request must be earlier than in order to dequeue + * @returns RC 0 if successfully set dequeued_element, -ENOENT if empty or if none meet target_deadline + */ +int +priority_queue_dequeue_if_earlier(struct priority_queue *self, void **dequeued_element, uint64_t target_deadline) +{ + int return_code; + + LOCK_LOCK(&self->lock); + return_code = priority_queue_dequeue_if_earlier_nolock(self, dequeued_element, target_deadline); + LOCK_UNLOCK(&self->lock); + + return return_code; } /** @@ -314,40 +406,43 @@ err_enoent: * @returns RC 0 if successfully set dequeued_element, -ENOENT if empty */ int -priority_queue_top(struct priority_queue *self, void **dequeued_element) +priority_queue_top_nolock(struct priority_queue *self, void **dequeued_element) { assert(self != NULL); assert(dequeued_element != NULL); assert(self->get_priority_fn != NULL); assert(runtime_is_worker()); assert(!software_interrupt_is_enabled()); + assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); int return_code; - LOCK_LOCK(&self->lock); - - if (priority_queue_is_empty_locked(self)) goto err_enoent; + if (priority_queue_is_empty(self)) goto err_enoent; *dequeued_element = self->items[1]; return_code = 0; -release_lock: - LOCK_UNLOCK(&self->lock); done: return return_code; err_enoent: return_code = -ENOENT; - goto release_lock; + goto done; } /** - * Peek at the priority of the highest priority task without having to take the lock - * Because this is a min-heap PQ, the highest priority is the lowest 64-bit integer - * This is used to store an absolute deadline - * @returns value of highest priority value in queue or ULONG_MAX if empty + * Returns the top of the priority queue without removing it + * @param self - the priority queue we want to add to + * @param dequeued_element a pointer to set to the top element + * @returns RC 0 if successfully set dequeued_element, -ENOENT if empty */ -uint64_t -priority_queue_peek(struct priority_queue *self) +int +priority_queue_top(struct priority_queue *self, void **dequeued_element) { - return self->highest_priority; + int return_code; + + LOCK_LOCK(&self->lock); + return_code = priority_queue_top_nolock(self, dequeued_element); + LOCK_UNLOCK(&self->lock); + + return return_code; } diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index ee8d5b5..5de9cb7 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -1,9 +1,9 @@ #include #include #include -#include #include "arch/context.h" +#include "client_socket.h" #include "debuglog.h" #include "global_request_scheduler_deque.h" #include "global_request_scheduler_minheap.h" @@ -151,39 +151,6 @@ runtime_initialize(void) * Listener Thread Logic * ************************/ -/** - * Rejects Requests as determined by admissions control - * @param client_socket - the client we are rejecting - */ -static inline void -listener_thread_reject(int client_socket) -{ - assert(client_socket >= 0); - - int rc; - int sent = 0; - int to_send = strlen(HTTP_RESPONSE_503_SERVICE_UNAVAILABLE); - - while (sent < to_send) { - rc = write(client_socket, &HTTP_RESPONSE_503_SERVICE_UNAVAILABLE[sent], to_send - sent); - if (rc < 0) { - if (errno == EAGAIN) continue; - - goto send_504_err; - } - sent += rc; - }; - - atomic_fetch_add(&runtime_total_5XX_responses, 1); - -close: - if (close(client_socket) < 0) panic("Error closing client socket - %s", strerror(errno)); - return; -send_504_err: - debuglog("Error sending 504: %s", strerror(errno)); - goto close; -} - /** * @brief Execution Loop of the listener core, io_handles HTTP requests, allocates sandbox request objects, and pushes * the sandbox object to the global dequeue @@ -285,7 +252,9 @@ listener_thread_main(void *dummy) / module->relative_deadline; if (runtime_admitted + admissions_estimate >= runtime_admissions_capacity) { - listener_thread_reject(client_socket); + client_socket_send(client_socket, 503); + if (close(client_socket) < 0) + debuglog("Error closing client socket - %s", strerror(errno)); continue; } diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index 709a56c..2487177 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -2,12 +2,10 @@ #include #include #include -#include #include "current_sandbox.h" #include "debuglog.h" #include "http_parser_settings.h" -#include "libuv_callbacks.h" #include "local_completion_queue.h" #include "local_runqueue.h" #include "panic.h" @@ -60,8 +58,6 @@ sandbox_receive_and_parse_client_request(struct sandbox *sandbox) int rc = 0; -#ifndef USE_HTTP_UVIO - while (!sandbox->http_request.message_end) { /* Read from the Socket */ @@ -106,13 +102,6 @@ sandbox_receive_and_parse_client_request(struct sandbox *sandbox) sandbox->request_length = sandbox->request_response_data_length; -#else - rc = uv_read_start((uv_stream_t *)&sandbox->client_libuv_stream, - libuv_callbacks_on_allocate_setup_request_response_data, - libuv_callbacks_on_read_parse_http_request); - worker_thread_process_io(); -#endif - rc = 0; done: return rc; @@ -204,11 +193,10 @@ sandbox_build_and_send_client_response(struct sandbox *sandbox) sandbox->total_time = end_time - sandbox->request_arrival_timestamp; uint64_t total_time_us = sandbox->total_time / runtime_processor_speed_MHz; -#ifndef USE_HTTP_UVIO int rc; int sent = 0; while (sent < response_cursor) { - rc = write(sandbox->client_socket_descriptor, sandbox->request_response_data + sent, + rc = write(sandbox->client_socket_descriptor, &sandbox->request_response_data[sent], response_cursor - sent); if (rc < 0) { if (errno == EAGAIN) @@ -221,15 +209,6 @@ sandbox_build_and_send_client_response(struct sandbox *sandbox) sent += rc; } -#else - uv_write_t req = { - .data = sandbox, - }; - uv_buf_t bufv = uv_buf_init(sandbox->request_response_data, response_cursor); - int r = uv_write(&req, (uv_stream_t *)&sandbox->client_libuv_stream, &bufv, 1, - libuv_callbacks_on_write_wakeup_sandbox); - worker_thread_process_io(); -#endif return 0; } @@ -243,19 +222,6 @@ sandbox_open_http(struct sandbox *sandbox) /* Set the sandbox as the data the http-parser has access to */ sandbox->http_parser.data = sandbox; -#ifdef USE_HTTP_UVIO - - /* Initialize libuv TCP stream */ - int r = uv_tcp_init(worker_thread_get_libuv_handle(), (uv_tcp_t *)&sandbox->client_libuv_stream); - assert(r == 0); - - /* Set the current sandbox as the data the libuv callbacks have access to */ - sandbox->client_libuv_stream.data = sandbox; - - /* Open the libuv TCP stream */ - r = uv_tcp_open((uv_tcp_t *)&sandbox->client_libuv_stream, sandbox->client_socket_descriptor); - assert(r == 0); -#else /* Freshly allocated sandbox going runnable for first time, so register client socket with epoll */ struct epoll_event accept_evt; accept_evt.data.ptr = (void *)sandbox; @@ -263,7 +229,6 @@ sandbox_open_http(struct sandbox *sandbox) int rc = epoll_ctl(worker_thread_epoll_file_descriptor, EPOLL_CTL_ADD, sandbox->client_socket_descriptor, &accept_evt); if (unlikely(rc < 0)) panic_err(); -#endif } /** @@ -357,24 +322,7 @@ err: assert(sandbox->state == SANDBOX_RUNNING); /* Send a 400 error back to the client */ - rc = 0; - int sent = 0; - int to_send = strlen(HTTP_RESPONSE_400_BAD_REQUEST); - while (sent < to_send) { - rc = write(sandbox->client_socket_descriptor, &HTTP_RESPONSE_400_BAD_REQUEST[sent], to_send - sent); - if (rc < 0) { - if (errno == EAGAIN) { - debuglog("Unexpectedly blocking on write of 4XX error"); - worker_thread_block_current_sandbox(); - continue; - } - - debuglog("Failed to send 400: %s", strerror(errno)); - break; - } - - sent += rc; - } + client_socket_send(sandbox->client_socket_descriptor, 400); #ifdef LOG_TOTAL_REQS_RESPS if (rc >= 0) { @@ -384,7 +332,7 @@ err: #endif software_interrupt_disable(); - sandbox_close_http(sandbox); + client_socket_close(sandbox->client_socket_descriptor); sandbox_set_as_error(sandbox, SANDBOX_RUNNING); goto done; } @@ -876,7 +824,6 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state) /* * TODO: Enhance to include "spinning" or better "local|global scheduling latency" as well. - * Given the async I/O model of libuv, it is ambiguous how to model "spinning" */ perf_window_add(&sandbox->module->perf_window, sandbox->running_duration); @@ -946,9 +893,9 @@ err_stack_allocation_failed: sandbox->state = SANDBOX_SET_AS_INITIALIZED; sandbox->last_state_change_timestamp = now; ps_list_init_d(sandbox); - sandbox_set_as_error(sandbox, SANDBOX_SET_AS_INITIALIZED); err_memory_allocation_failed: err: + sandbox_set_as_error(sandbox, SANDBOX_SET_AS_INITIALIZED); perror(error_message); sandbox = NULL; goto done; diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index f987814..90df9fd 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -82,9 +82,9 @@ sigalrm_handler(siginfo_t *signal_info, ucontext_t *user_context, struct sandbox if (!software_interrupt_is_enabled()) return; /* - * if a SIGALRM fires while the worker thread is between sandboxes, executing libuv, completion queue - * cleanup, etc. current_sandbox might be NULL. In this case, we should just allow return to allow the - * worker thread to run the main loop until it loads a new sandbox. + * if a SIGALRM fires while the worker thread is between sandboxes doing runtime tasks such as processing + * the epoll loop, performing completion queue cleanup, etc. current_sandbox might be NULL. In this case, + * we should just allow return to allow the worker thread to run the main loop until it loads a new sandbox. * * TODO: Consider if this should be an invarient and the worker thread should disable software * interrupts when doing this work. Issue #95 diff --git a/runtime/src/worker_thread.c b/runtime/src/worker_thread.c index 391bf78..6f54d82 100644 --- a/runtime/src/worker_thread.c +++ b/runtime/src/worker_thread.c @@ -4,8 +4,8 @@ #include #include #include -#include +#include "client_socket.h" #include "current_sandbox.h" #include "debuglog.h" #include "global_request_scheduler.h" @@ -24,15 +24,7 @@ /* context of the runtime thread before running sandboxes or to resume its "main". */ __thread struct arch_context worker_thread_base_context; -#ifdef USE_HTTP_UVIO -/* libuv i/o loop handle per sandboxing thread! */ -__thread uv_loop_t worker_thread_uvio_handle; - -/* Flag to signify if the thread is currently running callbacks in the libuv event loop */ -static __thread bool worker_thread_is_in_libuv_event_loop = false; -#else __thread int worker_thread_epoll_file_descriptor; -#endif /* USE_HTTP_UVIO */ /* Total Lock Contention in Cycles */ __thread uint64_t worker_thread_lock_duration; @@ -189,9 +181,6 @@ worker_thread_wakeup_sandbox(struct sandbox *sandbox) void worker_thread_block_current_sandbox(void) { -#ifdef USE_HTTP_UVIO - assert(worker_thread_is_in_libuv_event_loop == false); -#endif software_interrupt_disable(); /* Remove the sandbox we were just executing from the runqueue and mark as blocked */ @@ -200,53 +189,19 @@ worker_thread_block_current_sandbox(void) assert(current_sandbox->state == SANDBOX_RUNNING); sandbox_set_as_blocked(current_sandbox, SANDBOX_RUNNING); - worker_thread_switch_to_base_context(); -} - - -/** - * Execute I/O - */ -void -worker_thread_process_io(void) -{ -#ifdef USE_HTTP_UVIO -#ifdef USE_HTTP_SYNC - /* - * TODO: 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, perhaps RUN_ONCE and check if your I/O is processed, if yes, - * return else do async block! Issue #98 - */ - uv_run(worker_thread_get_libuv_handle(), UV_RUN_DEFAULT); -#else /* USE_HTTP_SYNC */ - worker_thread_block_current_sandbox(); -#endif /* USE_HTTP_UVIO */ -#else - assert(false); - /* it should not be called if not using uvio for http */ -#endif + /* The worker thread seems to "spin" on a blocked sandbox, so try to execute another sandbox for one quantum + * after blocking to give time for the action to resolve */ + struct sandbox *next_sandbox = local_runqueue_get_next(); + if (next_sandbox != NULL) { + worker_thread_switch_to_sandbox(next_sandbox); + } else { + worker_thread_switch_to_base_context(); + }; } -/** - * Run all outstanding events in the local thread's libuv event loop - */ -void -worker_thread_execute_libuv_event_loop(void) -{ -#ifdef USE_HTTP_UVIO - worker_thread_is_in_libuv_event_loop = true; - int n = uv_run(worker_thread_get_libuv_handle(), UV_RUN_NOWAIT), i = 0; - while (n > 0) { - n--; - uv_run(worker_thread_get_libuv_handle(), UV_RUN_NOWAIT); - } - worker_thread_is_in_libuv_event_loop = false; -#endif - return; -} /** - * Run all outstanding events in the local thread's libuv event loop + * Run all outstanding events in the local thread's epoll loop */ static inline void worker_thread_execute_epoll_loop(void) @@ -295,7 +250,8 @@ worker_thread_execute_epoll_loop(void) case SANDBOX_ERROR: panic("Expected to have closed socket"); default: - sandbox_close_http(sandbox); + client_socket_send(sandbox->client_socket_descriptor, 503); + client_socket_close(sandbox->client_socket_descriptor); sandbox_set_as_error(sandbox, sandbox->state); } }; @@ -303,34 +259,9 @@ worker_thread_execute_epoll_loop(void) } } -static inline void -worker_thread_initialize_async_io() -{ -#ifdef USE_HTTP_UVIO - worker_thread_is_in_libuv_event_loop = false; - /* Initialize libuv event loop handle */ - uv_loop_init(&worker_thread_uvio_handle); -#else - /* Initialize epoll */ - worker_thread_epoll_file_descriptor = epoll_create1(0); - if (unlikely(worker_thread_epoll_file_descriptor < 0)) panic_err(); -#endif -} -static inline void -worker_thread_process_async_io() -{ -#ifdef USE_HTTP_UVIO - /* Execute libuv event loop */ - if (!worker_thread_is_in_libuv_event_loop) worker_thread_execute_libuv_event_loop(); -#else - /* Execute non-blocking epoll_wait to add sandboxes back on the runqueue */ - worker_thread_execute_epoll_loop(); -#endif -} - /** * The entry function for sandbox worker threads - * Initializes thread-local state, unmasks signals, sets up libuv loop and + * Initializes thread-local state, unmasks signals, sets up epoll loop and * @param return_code - argument provided by pthread API. We set to -1 on error */ void * @@ -369,7 +300,9 @@ worker_thread_main(void *return_code) #endif signal(SIGPIPE, SIG_IGN); - worker_thread_initialize_async_io(); + /* Initialize epoll */ + worker_thread_epoll_file_descriptor = epoll_create1(0); + if (unlikely(worker_thread_epoll_file_descriptor < 0)) panic_err(); /* Begin Worker Execution Loop */ struct sandbox *next_sandbox; @@ -377,7 +310,7 @@ worker_thread_main(void *return_code) /* Assumption: current_sandbox should be unset at start of loop */ assert(current_sandbox_get() == NULL); - worker_thread_process_async_io(); + worker_thread_execute_epoll_loop(); /* Switch to a sandbox if one is ready to run */ software_interrupt_disable();