From b85d0895373affeb81e20c99258407461a8311fe Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Fri, 24 Jul 2020 15:56:26 -0400 Subject: [PATCH] feat: running, preempted, complete transitions --- runtime/include/sandbox.h | 3 + runtime/src/local_runqueue_minheap.c | 3 +- runtime/src/sandbox.c | 119 ++++++++++++++++++++++++++- runtime/src/worker_thread.c | 36 +++++++- 4 files changed, 155 insertions(+), 6 deletions(-) diff --git a/runtime/include/sandbox.h b/runtime/include/sandbox.h index dcad022..299833a 100644 --- a/runtime/include/sandbox.h +++ b/runtime/include/sandbox.h @@ -280,3 +280,6 @@ sandbox_print_perf(struct sandbox *sandbox) void sandbox_set_as_initialized(struct sandbox *sandbox, struct sandbox_request *sandbox_request, uint64_t allocation_timestamp); void sandbox_set_as_runnable(struct sandbox *sandbox); +void sandbox_set_as_running(struct sandbox *sandbox); +void sandbox_set_as_preempted(struct sandbox *sandbox); +void sandbox_set_as_complete(struct sandbox *sandbox); diff --git a/runtime/src/local_runqueue_minheap.c b/runtime/src/local_runqueue_minheap.c index e252823..3e18da3 100644 --- a/runtime/src/local_runqueue_minheap.c +++ b/runtime/src/local_runqueue_minheap.c @@ -159,11 +159,12 @@ local_runqueue_minheap_preempt(ucontext_t *user_context) /* Set as runnable and add it to the runqueue */ sandbox_set_as_runnable(next_sandbox); + sandbox_set_as_preempted(current_sandbox); /* Save the context of the currently executing sandbox before switching from it */ arch_mcontext_save(¤t_sandbox->ctxt, &user_context->uc_mcontext); /* Update current_sandbox to the next sandbox */ - current_sandbox_set(next_sandbox); + sandbox_set_as_running(next_sandbox); /* * Restore the context of this new sandbox diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index 3c5083c..eac9096 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -7,6 +7,7 @@ #include "current_sandbox.h" #include "http_parser_settings.h" #include "libuv_callbacks.h" +#include "local_completion_queue.h" #include "local_runqueue.h" #include "panic.h" #include "runtime.h" @@ -330,7 +331,7 @@ current_sandbox_main(void) { struct sandbox *sandbox = current_sandbox_get(); assert(sandbox != NULL); - assert(sandbox->state == SANDBOX_RUNNABLE); + assert(sandbox->state == SANDBOX_RUNNING); char *error_message = ""; @@ -572,6 +573,122 @@ sandbox_set_as_runnable(struct sandbox *sandbox) sandbox->state = SANDBOX_RUNNABLE; } +/** + * Transitions a sandbox to the SANDBOX_RUNNING state. + * + * This occurs in the following scenarios: + * - A sandbox is in a RUNNABLE state + * - after initialization. This sandbox has thus not yet been executed + * - after previously executing, blocking, waking up. + * - A sandbox in the PREEMPTED state is now the highest priority work to execute + * + * @param sandbox + * @param active_context - the worker thread context that is going to execute this sandbox. Only provided + * when performing a full mcontext restore + **/ +void +sandbox_set_as_running(struct sandbox *sandbox) +{ + assert(sandbox); + uint64_t now = __getcycles(); + uint64_t duration_of_last_state = now - sandbox->last_state_change_timestamp; + sandbox_state_t last_state = sandbox->state; + + sandbox->state = SANDBOX_SET_AS_RUNNING; + debuglog("Thread %lu | Sandbox %lu | %s => Running\n", pthread_self(), sandbox->allocation_timestamp, + sandbox_state_stringify(last_state)); + + switch (last_state) { + case SANDBOX_RUNNABLE: { + sandbox->runnable_duration += duration_of_last_state; + current_sandbox_set(sandbox); + break; + } + case SANDBOX_PREEMPTED: { + sandbox->preempted_duration += duration_of_last_state; + current_sandbox_set(sandbox); + break; + } + default: { + panic("Thread %lu | Sandbox %lu | Illegal transition from %s to Running\n", pthread_self(), + sandbox->allocation_timestamp, sandbox_state_stringify(last_state)); + } + } + + sandbox->last_state_change_timestamp = now; + sandbox->state = SANDBOX_RUNNING; +} + +/** + * Transitions a sandbox to the SANDBOX_PREEMPTED state. + * + * This occurs when a sandbox is executing and in a RUNNING state and a SIGALRM software interrupt fires + * and pulls a sandbox with an earlier absolute deadline from the global request scheduler. + * + * @param sandbox the sandbox being preempted + */ +void +sandbox_set_as_preempted(struct sandbox *sandbox) +{ + assert(sandbox); + uint64_t now = __getcycles(); + uint64_t duration_of_last_state = now - sandbox->last_state_change_timestamp; + sandbox_state_t last_state = sandbox->state; + sandbox->state = SANDBOX_SET_AS_PREEMPTED; + debuglog("Thread %lu | Sandbox %lu | %s => Preempted\n", pthread_self(), sandbox->allocation_timestamp, + sandbox_state_stringify(last_state)); + + switch (last_state) { + case SANDBOX_RUNNING: { + sandbox->running_duration += duration_of_last_state; + break; + } + default: { + panic("Thread %lu | Sandbox %lu | Illegal transition from %s to Preempted\n", pthread_self(), + sandbox->allocation_timestamp, sandbox_state_stringify(last_state)); + } + } + + sandbox->last_state_change_timestamp = now; + sandbox->state = SANDBOX_PREEMPTED; +} + +/** + * Transitions a sandbox from the SANDBOX_RETURNED state to the SANDBOX_COMPLETE state. + * Adds the sandbox to the completion queue + * @param sandbox the sandbox erroring out + */ +void +sandbox_set_as_complete(struct sandbox *sandbox) +{ + assert(sandbox); + uint64_t now = __getcycles(); + uint64_t duration_of_last_state = now - sandbox->last_state_change_timestamp; + sandbox_state_t last_state = sandbox->state; + sandbox->state = SANDBOX_SET_AS_COMPLETE; + debuglog("Thread %lu | Sandbox %lu | %s => Complete\n", pthread_self(), sandbox->allocation_timestamp, + sandbox_state_stringify(last_state)); + + switch (last_state) { + case SANDBOX_RETURNED: { + sandbox->completion_timestamp = now; + sandbox->returned_duration += duration_of_last_state; + break; + } + default: { + panic("Thread %lu | Sandbox %lu | Illegal transition from %s to Error\n", pthread_self(), + sandbox->allocation_timestamp, sandbox_state_stringify(last_state)); + } + } + + sandbox->last_state_change_timestamp = now; + sandbox->state = SANDBOX_COMPLETE; + + sandbox_print_perf(sandbox); + + /* Do not touch sandbox state after adding to the completion queue to avoid use-after-free bugs */ + local_completion_queue_add(sandbox); +} /** * Allocates a new sandbox from a sandbox request diff --git a/runtime/src/worker_thread.c b/runtime/src/worker_thread.c index 49d789c..f389b0c 100644 --- a/runtime/src/worker_thread.c +++ b/runtime/src/worker_thread.c @@ -33,6 +33,32 @@ static __thread bool worker_thread_is_in_libuv_event_loop = false; * Worker Thread Logic * **********************/ +/** + * Conditionally triggers appropriate state changes for exiting sandboxes + * @param exiting_sandbox - The sandbox that ran to completion + */ +static inline void +worker_thread_transition_exiting_sandbox(struct sandbox *exiting_sandbox) +{ + assert(exiting_sandbox != NULL); + + switch (exiting_sandbox->state) { + case SANDBOX_RETURNED: + /* + * We draw a distinction between RETURNED and COMPLETED because a sandbox cannot add itself to the + * completion queue + */ + sandbox_set_as_complete(exiting_sandbox); + break; + case SANDBOX_ERROR: + /* Terminal State, so just break */ + break; + default: + panic("Cooperatively switching from a sandbox in a non-terminal %s state\n", + sandbox_state_stringify(exiting_sandbox->state)); + } +} + /** * @brief Switches to the next sandbox, placing the current sandbox on the completion queue if in SANDBOX_RETURNED state * @param next_sandbox The Sandbox Context to switch to @@ -54,24 +80,26 @@ worker_thread_switch_to_sandbox(struct sandbox *next_sandbox) if (current_sandbox == NULL) { /* Switching from "Base Context" */ + sandbox_set_as_running(next_sandbox); + debuglog("Base Context (%s) > Sandbox %lu (%s)\n", arch_context_variant_print(worker_thread_base_context.variant), next_sandbox->request_arrival_timestamp, arch_context_variant_print(next_context->variant)); - current_sandbox_set(next_sandbox); - arch_context_switch(NULL, next_context); } else { /* Set the current sandbox to the next */ assert(next_sandbox != current_sandbox); + worker_thread_transition_exiting_sandbox(current_sandbox); + + sandbox_set_as_running(next_sandbox); + struct arch_context *current_context = ¤t_sandbox->ctxt; debuglog("Sandbox %lu > Sandbox %lu\n", current_sandbox->request_arrival_timestamp, next_sandbox->request_arrival_timestamp); - current_sandbox_set(next_sandbox); - /* Switch to the associated context. */ arch_context_switch(current_context, next_context); }