refactor: Namespace cleanup

master
Sean McBride 4 years ago
parent faacc5c785
commit d269c04c69

@ -12,6 +12,7 @@
#include "arch/reg_t.h"
#include "arch/ureg_t.h"
#include "software_interrupt.h"
#include "worker_thread.h"
/*
* This file contains the common dependencies of the architecture-dependent code.
@ -27,7 +28,6 @@
* This is the slowpath switch to a preempted sandbox!
* SIGUSR1 on the current thread and restore mcontext there!
*/
extern __thread struct arch_context worker_thread_base_context;
/* Cannot be inlined because called in assembly */
void __attribute__((noinline)) __attribute__((noreturn)) arch_context_restore_preempted(void);

@ -86,7 +86,7 @@ static inline int
arch_context_switch(struct arch_context *a, struct arch_context *b)
{
/* Assumption: Software Interrupts are disabled by caller */
assert(software_interrupt_is_disabled);
assert(!software_interrupt_is_enabled());
/* if both a and b are NULL, there is no state change */
assert(a != NULL || b != NULL);

@ -4,3 +4,4 @@
struct sandbox *current_sandbox_get(void);
void current_sandbox_set(struct sandbox *sandbox);
void current_sandbox_block(void);

@ -114,15 +114,58 @@ void sandbox_free_linear_memory(struct sandbox *sandbox);
int sandbox_get_file_descriptor(struct sandbox *sandbox, int io_handle_index);
int sandbox_initialize_io_handle(struct sandbox *sandbox);
void sandbox_main(struct sandbox *sandbox);
void sandbox_close_http(struct sandbox *sandbox);
INLINE void sandbox_set_as_initialized(struct sandbox *sandbox, struct sandbox_request *sandbox_request,
void sandbox_set_as_initialized(struct sandbox *sandbox, struct sandbox_request *sandbox_request,
uint64_t allocation_timestamp);
INLINE void sandbox_set_as_runnable(struct sandbox *sandbox, sandbox_state_t last_state);
INLINE void sandbox_set_as_running(struct sandbox *sandbox, sandbox_state_t last_state);
INLINE void sandbox_set_as_blocked(struct sandbox *sandbox, sandbox_state_t last_state);
INLINE void sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state);
INLINE void sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state);
INLINE void sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state);
INLINE void sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_set_as_runnable(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_set_as_running(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_set_as_blocked(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state);
void sandbox_switch_to(struct sandbox *next_sandbox);
/**
* Conditionally triggers appropriate state changes for exiting sandboxes
* @param exiting_sandbox - The sandbox that ran to completion
*/
static inline void
sandbox_exit(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, SANDBOX_RETURNED);
break;
case SANDBOX_BLOCKED:
/* Cooperative yield, so just break */
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));
}
}
/**
* Mark a blocked sandbox as runnable and add it to the runqueue
* @param sandbox the sandbox to check and update if blocked
*/
static inline void
sandbox_wakeup(struct sandbox *sandbox)
{
assert(sandbox != NULL);
assert(sandbox->state == SANDBOX_BLOCKED);
software_interrupt_disable();
sandbox_set_as_runnable(sandbox, SANDBOX_BLOCKED);
software_interrupt_enable();
}

@ -16,7 +16,6 @@
***********/
extern __thread volatile sig_atomic_t software_interrupt_is_disabled;
extern uint64_t software_interrupt_interval_duration_in_cycles;
/*************************
* Public Static Inlines *
@ -30,7 +29,6 @@ software_interrupt_disable(void)
}
}
/**
* Enables signals
*/
@ -107,3 +105,4 @@ software_interrupt_unmask_signal(int signal)
void software_interrupt_initialize(void);
void software_interrupt_arm_timer(void);
void software_interrupt_disarm_timer(void);
void software_interrupt_set_interval_duration(uint64_t cycles);

@ -3,6 +3,7 @@
#include "generic_thread.h"
#include "runtime.h"
extern __thread struct arch_context worker_thread_base_context;
extern __thread int worker_thread_epoll_file_descriptor;
extern __thread int worker_thread_idx;
@ -48,5 +49,4 @@ worker_thread_get_memory_string(uint32_t offset, uint32_t max_length)
return NULL;
}
void worker_thread_block_current_sandbox(void);
__attribute__((noreturn)) void worker_thread_on_sandbox_exit();
void worker_thread_switch_to_base_context();

@ -1,4 +1,6 @@
#include "current_sandbox.h"
#include "local_runqueue.h"
#include "worker_thread.h"
/* current sandbox that is active.. */
static __thread struct sandbox *worker_thread_current_sandbox = NULL;
@ -43,3 +45,29 @@ current_sandbox_set(struct sandbox *sandbox)
worker_thread_current_sandbox = sandbox;
}
}
/**
* Mark the currently executing sandbox as blocked, remove it from the local runqueue,
* and switch to base context
*/
void
current_sandbox_block(void)
{
software_interrupt_disable();
/* Remove the sandbox we were just executing from the runqueue and mark as blocked */
struct sandbox *current_sandbox = current_sandbox_get();
assert(current_sandbox->state == SANDBOX_RUNNING);
sandbox_set_as_blocked(current_sandbox, SANDBOX_RUNNING);
generic_thread_dump_lock_overhead();
/* 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) {
sandbox_switch_to(next_sandbox);
} else {
worker_thread_switch_to_base_context();
};
}

@ -7,6 +7,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
#include "current_sandbox.h"
@ -111,7 +112,7 @@ wasm_read(int32_t filedes, int32_t buf_offset, int32_t nbyte)
int32_t length_read = (int32_t)read(filedes, buf, nbyte);
if (length_read < 0) {
if (errno == EAGAIN)
worker_thread_block_current_sandbox();
current_sandbox_block();
else {
/* All other errors */
debuglog("Error reading socket %d - %s\n", filedes, strerror(errno));
@ -154,7 +155,7 @@ wasm_write(int32_t fd, int32_t buf_offset, int32_t buf_size)
int32_t length_written = (int32_t)write(f, buf, buf_size);
if (length_written < 0) {
if (errno == EAGAIN)
worker_thread_block_current_sandbox();
current_sandbox_block();
else {
/* All other errors */
debuglog("Error reading socket %d - %s\n", fd, strerror(errno));

@ -341,7 +341,8 @@ main(int argc, char **argv)
runtime_processor_speed_MHz = runtime_get_processor_speed_MHz();
if (unlikely(runtime_processor_speed_MHz == 0)) panic("Failed to detect processor speed\n");
software_interrupt_interval_duration_in_cycles = (uint64_t)runtime_quantum_us * runtime_processor_speed_MHz;
software_interrupt_set_interval_duration(runtime_quantum_us * runtime_processor_speed_MHz);
printf("\tProcessor Speed: %u MHz\n", runtime_processor_speed_MHz);
runtime_set_resource_limits_to_max();

@ -154,7 +154,7 @@ sandbox_receive_and_parse_client_request(struct sandbox *sandbox)
if (recved < 0) {
if (errno == EAGAIN) {
worker_thread_block_current_sandbox();
current_sandbox_block();
continue;
} else {
/* All other errors */
@ -297,7 +297,7 @@ sandbox_build_and_send_client_response(struct sandbox *sandbox)
response_cursor - sent);
if (rc < 0) {
if (errno == EAGAIN)
worker_thread_block_current_sandbox();
current_sandbox_block();
else {
perror("write");
return -1;
@ -505,7 +505,8 @@ sandbox_start(void)
done:
/* Cleanup connection and exit sandbox */
worker_thread_on_sandbox_exit(sandbox);
generic_thread_dump_lock_overhead();
worker_thread_switch_to_base_context();
/* This assert prevents a segfault discussed in
* https://github.com/phanikishoreg/awsm-Serverless-Framework/issues/66
@ -932,9 +933,7 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state)
uint64_t sandbox_id = sandbox->id;
sandbox->state = SANDBOX_ERROR;
sandbox_print_perf(sandbox);
#ifdef LOG_SANDBOX_MEMORY_PROFILE
sandbox_summarize_page_allocations(sandbox);
#endif
sandbox_free_linear_memory(sandbox);
admissions_control_subtract(sandbox->admissions_estimate);
/* Do not touch sandbox after adding to completion queue to avoid use-after-free bugs */
@ -979,9 +978,7 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state)
uint64_t sandbox_id = sandbox->id;
sandbox->state = SANDBOX_COMPLETE;
sandbox_print_perf(sandbox);
#ifdef LOG_SANDBOX_MEMORY_PROFILE
sandbox_summarize_page_allocations(sandbox);
#endif
/* Admissions Control Post Processing */
admissions_info_update(&sandbox->module->admissions_info, sandbox->running_duration);
admissions_control_subtract(sandbox->admissions_estimate);
@ -1116,3 +1113,65 @@ err:
/* Errors freeing memory is a fatal error */
panic("Failed to free Sandbox %lu\n", sandbox->id);
}
/**
* @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
*/
void
sandbox_switch_to(struct sandbox *next_sandbox)
{
/* Assumption: The caller disables interrupts */
assert(!software_interrupt_is_enabled());
assert(next_sandbox != NULL);
struct arch_context *next_context = &next_sandbox->ctxt;
/* Get the old sandbox we're switching from */
struct sandbox *current_sandbox = current_sandbox_get();
/* Update the worker's absolute deadline */
runtime_worker_threads_deadline[worker_thread_idx] = next_sandbox->absolute_deadline;
if (current_sandbox == NULL) {
/* Switching from "Base Context" */
sandbox_set_as_running(next_sandbox, next_sandbox->state);
#ifdef LOG_CONTEXT_SWITCHES
debuglog("Base Context (@%p) (%s) > Sandbox %lu (@%p) (%s)\n", &worker_thread_base_context,
arch_context_variant_print(worker_thread_base_context.variant), next_sandbox->id, next_context,
arch_context_variant_print(next_context->variant));
#endif
/* Assumption: If a slow context switch, current sandbox should be set to the target */
assert(next_context->variant != ARCH_CONTEXT_VARIANT_SLOW
|| &current_sandbox_get()->ctxt == next_context);
arch_context_switch(NULL, next_context);
} else {
/* Set the current sandbox to the next */
assert(next_sandbox != current_sandbox);
struct arch_context *current_context = &current_sandbox->ctxt;
#ifdef LOG_CONTEXT_SWITCHES
debuglog("Sandbox %lu (@%p) (%s) > Sandbox %lu (@%p) (%s)\n", current_sandbox->id,
&current_sandbox->ctxt, arch_context_variant_print(current_sandbox->ctxt.variant),
next_sandbox->id, &next_sandbox->ctxt, arch_context_variant_print(next_context->variant));
#endif
sandbox_exit(current_sandbox);
sandbox_set_as_running(next_sandbox, next_sandbox->state);
#ifndef NDEBUG
assert(next_context->variant != ARCH_CONTEXT_VARIANT_SLOW
|| &current_sandbox_get()->ctxt == next_context);
#endif
/* Switch to the associated context. */
arch_context_switch(current_context, next_context);
}
software_interrupt_enable();
}

@ -3,6 +3,7 @@
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <ucontext.h>
@ -24,7 +25,7 @@
******************/
static const int software_interrupt_supported_signals[] = { SIGALRM, SIGUSR1 };
uint64_t software_interrupt_interval_duration_in_cycles;
static uint64_t software_interrupt_interval_duration_in_cycles;
/******************
* Thread Globals *
@ -45,9 +46,6 @@ extern pthread_t runtime_worker_threads[];
* Private Static Inlines *
*************************/
static inline void software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void *user_context_raw);
/**
* A POSIX signal is delivered to only one thread.
* This function broadcasts the sigalarm signal to all other worker threads
@ -68,15 +66,20 @@ sigalrm_propagate_workers(siginfo_t *signal_info)
assert(runtime_worker_threads[i] != 0);
/* If using EDF, conditionally send signals. If not, broadcast */
if (runtime_sigalrm_handler == RUNTIME_SIGALRM_HANDLER_TRIAGED) {
switch (runtime_sigalrm_handler) {
case RUNTIME_SIGALRM_HANDLER_TRIAGED: {
uint64_t local_deadline = runtime_worker_threads_deadline[i];
uint64_t global_deadline = global_request_scheduler_peek();
if (global_deadline < local_deadline) pthread_kill(runtime_worker_threads[i], SIGALRM);
return;
} else if (runtime_sigalrm_handler == RUNTIME_SIGALRM_HANDLER_BROADCAST) {
continue;
}
case RUNTIME_SIGALRM_HANDLER_BROADCAST: {
pthread_kill(runtime_worker_threads[i], SIGALRM);
} else {
panic("Unexpected SIGALRM Handler: %d\n", runtime_sigalrm_handler)
continue;
}
default: {
panic("Unexpected SIGALRM Handler: %d\n", runtime_sigalrm_handler);
}
}
}
} else {
@ -100,7 +103,6 @@ sigalrm_handler(siginfo_t *signal_info, ucontext_t *user_context, struct sandbox
{
sigalrm_propagate_workers(signal_info);
/* NOOP if software interrupts not enabled */
if (!software_interrupt_is_enabled()) return;
@ -279,3 +281,9 @@ software_interrupt_initialize(void)
}
}
}
void
software_interrupt_set_interval_duration(uint64_t cycles)
{
software_interrupt_interval_duration_in_cycles = cycles;
}

@ -34,102 +34,10 @@ __thread int worker_thread_idx;
* 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, SANDBOX_RETURNED);
break;
case SANDBOX_BLOCKED:
/* Cooperative yield, so just break */
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
*/
static inline void
worker_thread_switch_to_sandbox(struct sandbox *next_sandbox)
{
/* Assumption: The caller disables interrupts */
assert(software_interrupt_is_disabled);
assert(next_sandbox != NULL);
struct arch_context *next_context = &next_sandbox->ctxt;
/* Get the old sandbox we're switching from */
struct sandbox *current_sandbox = current_sandbox_get();
/* Update the worker's absolute deadline */
runtime_worker_threads_deadline[worker_thread_idx] = next_sandbox->absolute_deadline;
if (current_sandbox == NULL) {
/* Switching from "Base Context" */
sandbox_set_as_running(next_sandbox, next_sandbox->state);
#ifdef LOG_CONTEXT_SWITCHES
debuglog("Base Context (@%p) (%s) > Sandbox %lu (@%p) (%s)\n", &worker_thread_base_context,
arch_context_variant_print(worker_thread_base_context.variant), next_sandbox->id, next_context,
arch_context_variant_print(next_context->variant));
#endif
/* Assumption: If a slow context switch, current sandbox should be set to the target */
assert(next_context->variant != ARCH_CONTEXT_VARIANT_SLOW
|| &current_sandbox_get()->ctxt == next_context);
arch_context_switch(NULL, next_context);
} else {
/* Set the current sandbox to the next */
assert(next_sandbox != current_sandbox);
struct arch_context *current_context = &current_sandbox->ctxt;
#ifdef LOG_CONTEXT_SWITCHES
debuglog("Sandbox %lu (@%p) (%s) > Sandbox %lu (@%p) (%s)\n", current_sandbox->id,
&current_sandbox->ctxt, arch_context_variant_print(current_sandbox->ctxt.variant),
next_sandbox->id, &next_sandbox->ctxt, arch_context_variant_print(next_context->variant));
#endif
worker_thread_transition_exiting_sandbox(current_sandbox);
sandbox_set_as_running(next_sandbox, next_sandbox->state);
#ifndef NDEBUG
assert(next_context->variant != ARCH_CONTEXT_VARIANT_SLOW
|| &current_sandbox_get()->ctxt == next_context);
#endif
/* Switch to the associated context. */
arch_context_switch(current_context, next_context);
}
software_interrupt_enable();
}
/**
* @brief Switches to the base context, placing the current sandbox on the completion queue if in RETURNED state
*/
static inline void
void
worker_thread_switch_to_base_context()
{
assert(!software_interrupt_is_enabled());
@ -148,12 +56,12 @@ worker_thread_switch_to_base_context()
assert(current_context != &worker_thread_base_context);
#ifdef LOG_CONTEXT_SWITCHES
debuglog("Sandbox %lu (@%p) (%s)> Base Context (@%p) (%s)\n", current_sandbox->id, current_context,
debuglog("Sandbox %lu (@%p) (%s) > Base Context (@%p) (%s)\n", current_sandbox->id, current_context,
arch_context_variant_print(current_sandbox->ctxt.variant), &worker_thread_base_context,
arch_context_variant_print(worker_thread_base_context.variant));
#endif
worker_thread_transition_exiting_sandbox(current_sandbox);
sandbox_exit(current_sandbox);
current_sandbox_set(NULL);
assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST);
runtime_worker_threads_deadline[worker_thread_idx] = UINT64_MAX;
@ -161,48 +69,6 @@ worker_thread_switch_to_base_context()
software_interrupt_enable();
}
/**
* Mark a blocked sandbox as runnable and add it to the runqueue
* @param sandbox the sandbox to check and update if blocked
*/
void
worker_thread_wakeup_sandbox(struct sandbox *sandbox)
{
assert(sandbox != NULL);
assert(sandbox->state == SANDBOX_BLOCKED);
software_interrupt_disable();
sandbox_set_as_runnable(sandbox, SANDBOX_BLOCKED);
software_interrupt_enable();
}
/**
* Mark the currently executing sandbox as blocked, remove it from the local runqueue,
* and switch to base context
*/
void
worker_thread_block_current_sandbox(void)
{
software_interrupt_disable();
/* Remove the sandbox we were just executing from the runqueue and mark as blocked */
struct sandbox *current_sandbox = current_sandbox_get();
assert(current_sandbox->state == SANDBOX_RUNNING);
sandbox_set_as_blocked(current_sandbox, SANDBOX_RUNNING);
/* 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 epoll loop
*/
@ -228,7 +94,7 @@ worker_thread_execute_epoll_loop(void)
struct sandbox *sandbox = (struct sandbox *)epoll_events[i].data.ptr;
assert(sandbox);
if (sandbox->state == SANDBOX_BLOCKED) worker_thread_wakeup_sandbox(sandbox);
if (sandbox->state == SANDBOX_BLOCKED) sandbox_wakeup(sandbox);
} else if (epoll_events[i].events & (EPOLLERR | EPOLLHUP)) {
/* Mystery: This seems to never fire. Why? Issue #130 */
@ -298,8 +164,7 @@ worker_thread_main(void *argument)
local_completion_queue_initialize();
/* Initialize Flags */
software_interrupt_is_disabled = false;
software_interrupt_disable();
/* Unmask signals */
if (runtime_preemption_enabled) {
@ -323,7 +188,7 @@ worker_thread_main(void *argument)
software_interrupt_disable();
next_sandbox = local_runqueue_get_next();
if (next_sandbox != NULL) {
worker_thread_switch_to_sandbox(next_sandbox);
sandbox_switch_to(next_sandbox);
} else {
software_interrupt_enable();
};
@ -334,17 +199,3 @@ worker_thread_main(void *argument)
panic("Worker Thread unexpectedly completed run loop.");
}
/**
* Called when the function in the sandbox exits
* Removes the standbox from the thread-local runqueue, sets its state to SANDBOX_RETURNED,
* releases the linear memory, and then returns to the base context
*/
__attribute__((noreturn)) void
worker_thread_on_sandbox_exit()
{
assert(!software_interrupt_is_enabled());
generic_thread_dump_lock_overhead();
worker_thread_switch_to_base_context();
panic("Unexpected return\n");
}

Loading…
Cancel
Save