feat: simplified sigalrms

master
Sean McBride 4 years ago
parent 38d9093880
commit 91c429cd8f

@ -2,8 +2,11 @@
#if defined(AARCH64) || defined(aarch64)
#include <assert.h>
#include <stdint.h>
#include "arch/common.h"
#include "current_sandbox.h"
#include "software_interrupt.h"
#define ARCH_SIG_JMP_OFF 0x100 /* Based on code generated! */
@ -29,6 +32,7 @@ arch_context_init(struct arch_context *actx, reg_t ip, reg_t sp)
actx->regs[UREG_SP] = sp;
actx->regs[UREG_IP] = ip;
actx->preemptable = false;
}
/**
@ -81,6 +85,12 @@ arch_context_switch(struct arch_context *a, struct arch_context *b)
reg_t *a_registers = a->regs, *b_registers = b->regs;
assert(a_registers && b_registers);
/* If switching back to a sandbox context marked as preemptable, reenable
* interrupts before jumping
* TODO: What if we receive a signal inside the inline assembly?
*/
if (b->preemptable) software_interrupt_enable();
asm volatile("mov x0, sp\n\t"
"adr x1, reset%=\n\t"
"str x1, [%[a], 8]\n\t"

@ -1,5 +1,8 @@
#pragma once
#include "arch/common.h"
#include "software_interrupt.h"
/*
* This header is the single entry point into the arch_context code.
* It includes processor independent code and conditionally includes architecture
@ -53,6 +56,9 @@ arch_mcontext_restore(mcontext_t *active_context, struct arch_context *sandbox_c
/* Restore mcontext */
memcpy(active_context, &sandbox_context->mctx, sizeof(mcontext_t));
/* Reenable software interrupts if we restored a preemptable sandbox */
if (sandbox_context->preemptable) software_interrupt_enable();
}
@ -73,9 +79,8 @@ arch_mcontext_save(struct arch_context *sandbox_context, const mcontext_t *activ
/* Assumption: The base context should never be modified */
assert(sandbox_context != &worker_thread_base_context);
/* Transitioning from {Unused, Running} -> Slow */
assert(sandbox_context->variant == ARCH_CONTEXT_VARIANT_UNUSED
|| sandbox_context->variant == ARCH_CONTEXT_VARIANT_RUNNING);
/* Transitioning from Running -> Slow */
assert(sandbox_context->variant == ARCH_CONTEXT_VARIANT_RUNNING);
sandbox_context->variant = ARCH_CONTEXT_VARIANT_SLOW;
/* Copy mcontext */

@ -1,8 +1,7 @@
#pragma once
#include "arch/common.h"
#define ARCH_SIG_JMP_OFF 8
#include "software_interrupt.h"
/**
* Initializes a context, zeros out registers, and sets the Instruction and
@ -15,6 +14,7 @@ static inline void
arch_context_init(struct arch_context *actx, reg_t ip, reg_t sp)
{
assert(actx != NULL);
assert(!software_interrupt_is_enabled());
if (ip == 0 && sp == 0) {
actx->variant = ARCH_CONTEXT_VARIANT_UNUSED;
@ -22,6 +22,8 @@ arch_context_init(struct arch_context *actx, reg_t ip, reg_t sp)
actx->variant = ARCH_CONTEXT_VARIANT_FAST;
}
actx->preemptable = false;
if (sp) {
/*
* context_switch conventions: bp is expected to be on top of the stack
@ -57,6 +59,7 @@ arch_context_init(struct arch_context *actx, reg_t ip, reg_t sp)
static inline void
arch_context_restore_new(mcontext_t *active_context, struct arch_context *sandbox_context)
{
assert(!software_interrupt_is_enabled());
assert(active_context != NULL);
assert(sandbox_context != NULL);
@ -98,8 +101,8 @@ arch_context_switch(struct arch_context *a, struct arch_context *b)
if (a == NULL) a = &worker_thread_base_context;
if (b == NULL) b = &worker_thread_base_context;
/* A Transition {Unused, Running} -> Fast */
assert(a->variant == ARCH_CONTEXT_VARIANT_UNUSED || a->variant == ARCH_CONTEXT_VARIANT_RUNNING);
/* A Transition {Running} -> Fast */
assert(a->variant == ARCH_CONTEXT_VARIANT_RUNNING);
/* B Transition {Fast, Slow} -> Running */
assert(b->variant == ARCH_CONTEXT_VARIANT_FAST || b->variant == ARCH_CONTEXT_VARIANT_SLOW);
@ -113,6 +116,13 @@ arch_context_switch(struct arch_context *a, struct arch_context *b)
reg_t *a_registers = a->regs, *b_registers = b->regs;
assert(a_registers && b_registers);
/* If fast switching back to a sandbox context marked as preemptable, reenable
* interrupts before jumping. If this is a slow context switch, defer renabling until
* arch_mcontext_restore
* TODO: What if we receive a signal inside the inline assemly?
*/
if (b->variant == ARCH_CONTEXT_VARIANT_FAST && b->preemptable) software_interrupt_enable();
asm volatile(
/* Create a new stack frame */
"pushq %%rbp\n\t" /* stack[stack_len++] = base_pointer */
@ -140,6 +150,7 @@ arch_context_switch(struct arch_context *a, struct arch_context *b)
* Fast Path
* We can just write update the stack pointer and jump to the target instruction
*/
"movq $3, (%%rdx)\n\t" /* b->variant = ARCH_CONTEXT_VARIANT_RUNNING; */
"movq (%%rbx), %%rsp\n\t" /* stack_pointer = b_registers[0] (stack_pointer) */
"jmpq *8(%%rbx)\n\t" /* immediate jump to b_registers[1] (instruction_pointer) */
@ -150,10 +161,10 @@ arch_context_switch(struct arch_context *a, struct arch_context *b)
/*
* Slow Path
* If the variant is ARCH_CONTEXT_VARIANT_SLOW, that means the sandbox was preempted and we need to
* fallback to a full mcontext-based context switch. We do this by invoking
* arch_context_restore_preempted, which fires a SIGUSR1 signal. The SIGUSR1 signal handler
* executes the mcontext-based context switch.
* If the context we're switching to is ARCH_CONTEXT_VARIANT_SLOW, that means the sandbox was
* preempted and we need to restore its context via a full mcontext-based context switch. We do
* this by invoking arch_context_restore_preempted, which fires a SIGUSR1 signal. The SIGUSR1
* signal handler executes the mcontext-based context switch.
*/
"1:\n\t"
"call arch_context_restore_preempted\n\t"
@ -163,16 +174,6 @@ arch_context_switch(struct arch_context *a, struct arch_context *b)
* This label is saved as the IP of a context during a fastpath context switch
*/
"2:\n\t"
"movq $3, (%%rdx)\n\t" /* b->variant = ARCH_CONTEXT_VARIANT_RUNNING; */
".align 8\n\t"
/*
* During slowpath context switches caused by preemption, the caller function
* arch_context_restore sets the variant to running in C and then restores at label
* 3 using the address of label 2 plus an offset equal to ARCH_SIG_JMP_OFF
* This must always equal the value in the .align instruction above!
*/
"3:\n\t"
"popq %%rbp\n\t" /* base_pointer = stack[--stack_len]; Base Pointer is restored */
:
: "a"(a_registers), "b"(b_registers), "c"(&a->variant), "d"(&b->variant)

@ -17,11 +17,16 @@
static inline 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();
/* We might either have blocked in start reading the request or while executing within the WebAssembly
* entrypoint. The preemptable flag on the context is used to differentiate. In either case, we should
* have disabled interrupts.
*/
if (current_sandbox->ctxt.preemptable) software_interrupt_disable();
assert(!software_interrupt_is_enabled());
assert(current_sandbox->state == SANDBOX_RUNNING);
sandbox_set_as_blocked(current_sandbox, SANDBOX_RUNNING);
generic_thread_dump_lock_overhead();

@ -42,5 +42,4 @@ current_sandbox_yield()
assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST);
runtime_worker_threads_deadline[worker_thread_idx] = UINT64_MAX;
arch_context_switch(current_context, &worker_thread_base_context);
software_interrupt_enable();
}

@ -10,12 +10,16 @@
#include <stdlib.h>
#include "debuglog.h"
#include "runtime.h"
#include "worker_thread.h"
/************
* Externs *
***********/
extern __thread volatile sig_atomic_t software_interrupt_is_disabled;
extern __thread volatile sig_atomic_t software_interrupt_is_disabled;
extern _Atomic __thread volatile sig_atomic_t software_interrupt_deferred_sigalrm;
extern _Atomic volatile sig_atomic_t software_interrupt_deferred_sigalrm_max[RUNTIME_WORKER_THREAD_CORE_COUNT];
/*************************
* Public Static Inlines *
@ -38,6 +42,13 @@ software_interrupt_enable(void)
if (__sync_bool_compare_and_swap(&software_interrupt_is_disabled, 1, 0) == false) {
panic("Recursive call to software_interrupt_enable\n");
}
if (software_interrupt_deferred_sigalrm > 0) {
// TODO: Atomic set?
software_interrupt_deferred_sigalrm_max[worker_thread_idx] = software_interrupt_deferred_sigalrm;
software_interrupt_deferred_sigalrm = 0;
// TODO: REPLAY sigalrm;
}
}
/**
@ -106,3 +117,4 @@ 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);
void software_interrupt_deferred_sigalrm_max_print(void);

@ -4,6 +4,7 @@
#include <stdlib.h>
#include "panic.h"
#include "software_interrupt.h"
/**
* Called by the inline assembly in arch_context_switch to send a SIGUSR1 in order to restore a previously preempted
@ -13,6 +14,7 @@
*/
void __attribute__((noinline)) __attribute__((noreturn)) arch_context_restore_preempted(void)
{
assert(!software_interrupt_is_enabled());
pthread_kill(pthread_self(), SIGUSR1);
panic("Unexpectedly reached code after sending self SIGUSR1\n");
}

@ -16,6 +16,30 @@ __thread struct sandbox_context_cache local_sandbox_context_cache = {
.module_indirect_table = NULL,
};
static inline void
current_sandbox_enable_preemption(struct sandbox *sandbox)
{
#ifdef LOG_PREEMPTION
debuglog("Sandbox %lu - enabling preemption\n", sandbox->id);
fflush(stderr);
#endif
assert(sandbox->ctxt.preemptable == false);
sandbox->ctxt.preemptable = true;
software_interrupt_enable();
}
static inline void
current_sandbox_disable_preemption(struct sandbox *sandbox)
{
#ifdef LOG_PREEMPTION
debuglog("Sandbox %lu - disabling preemption\n", sandbox->id);
fflush(stderr);
#endif
assert(sandbox->ctxt.preemptable == true);
software_interrupt_disable();
sandbox->ctxt.preemptable = false;
}
/**
* Sandbox execution logic
* Handles setup, request parsing, WebAssembly initialization, function execution, response building and
@ -24,16 +48,14 @@ __thread struct sandbox_context_cache local_sandbox_context_cache = {
void
current_sandbox_start(void)
{
assert(!software_interrupt_is_enabled());
struct sandbox *sandbox = current_sandbox_get();
assert(sandbox != NULL);
assert(sandbox->state == SANDBOX_RUNNING);
char *error_message = "";
assert(!software_interrupt_is_enabled());
arch_context_init(&sandbox->ctxt, 0, 0);
software_interrupt_enable();
sandbox_initialize_stdio(sandbox);
sandbox_open_http(sandbox);
@ -50,8 +72,10 @@ current_sandbox_start(void)
sandbox_setup_arguments(sandbox);
/* Executing the function */
int32_t argument_count = module_get_argument_count(current_module);
sandbox->return_value = module_main(current_module, argument_count, sandbox->arguments_offset);
int32_t argument_count = module_get_argument_count(current_module);
current_sandbox_enable_preemption(sandbox);
sandbox->return_value = module_main(current_module, argument_count, sandbox->arguments_offset);
current_sandbox_disable_preemption(sandbox);
sandbox->completion_timestamp = __getcycles();
/* Retrieve the result, construct the HTTP response, and send to client */
@ -64,8 +88,6 @@ current_sandbox_start(void)
sandbox->response_timestamp = __getcycles();
software_interrupt_disable();
assert(sandbox->state == SANDBOX_RUNNING);
sandbox_close_http(sandbox);
sandbox_set_as_returned(sandbox, SANDBOX_RUNNING);
@ -86,7 +108,6 @@ err:
/* Send a 400 error back to the client */
client_socket_send(sandbox->client_socket_descriptor, 400);
software_interrupt_disable();
sandbox_close_http(sandbox);
sandbox_set_as_error(sandbox, SANDBOX_RUNNING);
goto done;

@ -1,5 +1,6 @@
#include <stdint.h>
#include "arch/context.h"
#include "client_socket.h"
#include "current_sandbox.h"
#include "debuglog.h"
@ -111,24 +112,18 @@ void
local_runqueue_minheap_preempt(ucontext_t *user_context)
{
assert(user_context != NULL);
/* Prevent nested preemption */
software_interrupt_disable();
assert(!software_interrupt_is_enabled());
struct sandbox *current_sandbox = current_sandbox_get();
/* If current_sandbox is null, there's nothing to preempt, so let the "main" scheduler run its course. */
if (current_sandbox == NULL) {
software_interrupt_enable();
return;
};
if (current_sandbox == NULL) return;
/* The current sandbox should be the head of the runqueue */
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 global_deadline = global_request_scheduler_peek();
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) {
@ -179,11 +174,10 @@ local_runqueue_minheap_preempt(ucontext_t *user_context)
* TODO: Review the interrupt logic here. Issue #63
*/
runtime_worker_threads_deadline[worker_thread_idx] = next_sandbox->absolute_deadline;
assert(!software_interrupt_is_enabled());
arch_context_restore_new(&user_context->uc_mcontext, &next_sandbox->ctxt);
should_enable_software_interrupt = false;
}
done:
if (should_enable_software_interrupt) software_interrupt_enable();
return;
err_sandbox_allocate:
client_socket_send(sandbox_request->socket_descriptor, 503);
@ -199,10 +193,9 @@ err:
void
local_runqueue_minheap_initialize()
{
assert(software_interrupt_is_disabled);
/* Initialize local state */
software_interrupt_disable();
local_runqueue_minheap = priority_queue_initialize(256, false, sandbox_get_priority);
software_interrupt_enable();
/* Register Function Pointers for Abstract Scheduling API */
struct local_runqueue_config config = { .add_fn = local_runqueue_minheap_add,

@ -41,6 +41,7 @@ runtime_cleanup()
{
if (runtime_sandbox_perf_log != NULL) fflush(runtime_sandbox_perf_log);
software_interrupt_deferred_sigalrm_max_print();
exit(EXIT_SUCCESS);
}

@ -254,33 +254,26 @@ sandbox_switch_to(struct sandbox *next_sandbox)
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();
/* Get the old sandbox we're switching from.
* This is null if switching from base context
*/
struct sandbox * current_sandbox = current_sandbox_get();
struct arch_context *current_context = NULL;
if (current_sandbox != NULL) current_context = &current_sandbox->ctxt;
assert(next_sandbox != current_sandbox);
/* 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),
@ -288,17 +281,8 @@ sandbox_switch_to(struct sandbox *next_sandbox)
#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();
sandbox_set_as_running(next_sandbox, next_sandbox->state);
arch_context_switch(current_context, next_context);
}

@ -31,10 +31,24 @@ static uint64_t software_interrupt_interval_duration_in_cycles;
* Thread Globals *
*****************/
__thread static volatile sig_atomic_t software_interrupt_SIGALRM_kernel_count = 0;
__thread static volatile sig_atomic_t software_interrupt_SIGALRM_thread_count = 0;
__thread static volatile sig_atomic_t software_interrupt_SIGUSR_count = 0;
__thread volatile sig_atomic_t software_interrupt_is_disabled = 0;
__thread _Atomic static volatile sig_atomic_t software_interrupt_SIGALRM_kernel_count = 0;
__thread _Atomic static volatile sig_atomic_t software_interrupt_SIGALRM_thread_count = 0;
__thread _Atomic static volatile sig_atomic_t software_interrupt_SIGUSR_count = 0;
__thread volatile sig_atomic_t software_interrupt_is_disabled = 1;
__thread _Atomic volatile sig_atomic_t software_interrupt_deferred_sigalrm = 0;
__thread _Atomic volatile sig_atomic_t software_interrupt_signal_depth = 0;
_Atomic volatile sig_atomic_t software_interrupt_deferred_sigalrm_max[RUNTIME_WORKER_THREAD_CORE_COUNT] = { 0 };
void
software_interrupt_deferred_sigalrm_max_print()
{
printf("Max Deferred Sigalrms\n");
for (int i = 0; i < runtime_worker_threads_count; i++) {
printf("Worker %d: %d\n", i, software_interrupt_deferred_sigalrm_max[i]);
}
fflush(stdout);
}
/***************************************
* Externs
@ -55,10 +69,7 @@ sigalrm_propagate_workers(siginfo_t *signal_info)
{
/* Signal was sent directly by the kernel, so forward to other threads */
if (signal_info->si_code == SI_KERNEL) {
software_interrupt_SIGALRM_kernel_count++;
#ifdef LOG_PREEMPTION
debuglog("Kernel SIGALRM: %d!\n", software_interrupt_SIGALRM_kernel_count);
#endif
atomic_fetch_add(&software_interrupt_SIGALRM_kernel_count, 1);
for (int i = 0; i < runtime_worker_threads_count; i++) {
if (pthread_self() == runtime_worker_threads[i]) continue;
@ -83,10 +94,7 @@ sigalrm_propagate_workers(siginfo_t *signal_info)
}
}
} else {
software_interrupt_SIGALRM_thread_count++;
#ifdef LOG_PREEMPTION
debuglog("Thread SIGALRM: %d!\n", software_interrupt_SIGALRM_thread_count);
#endif
atomic_fetch_add(&software_interrupt_SIGALRM_thread_count, 1);
/* Signal forwarded from another thread. Just confirm it resulted from pthread_kill */
assert(signal_info->si_code == SI_TKILL);
}
@ -101,34 +109,27 @@ sigalrm_propagate_workers(siginfo_t *signal_info)
static inline void
sigalrm_handler(siginfo_t *signal_info, ucontext_t *user_context, struct sandbox *current_sandbox)
{
sigalrm_propagate_workers(signal_info);
/* NOOP if software interrupts not enabled */
if (!software_interrupt_is_enabled()) return;
/*
* 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
*/
if (!current_sandbox) return;
/*
* if a SIGALRM fires while the worker thread executing cleanup of a sandbox, it might be in a RETURNED
* state. In this case, we should just allow return to allow the sandbox to complete cleanup, as it is
* about to switch to 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 with above
*/
if (current_sandbox->state == SANDBOX_RETURNED) return;
/* A worker thread received a SIGALRM when interrupts were disabled, so defer until they are reenabled */
if (!software_interrupt_is_enabled()) {
// Don't increment if kernel? The first worker gets tons of these...
atomic_fetch_add(&software_interrupt_deferred_sigalrm, 1);
return;
}
/* A worker thread received a SIGALRM while running a preemptable sandbox, so preempt */
assert(current_sandbox->ctxt.preemptable);
software_interrupt_disable();
assert(current_sandbox != NULL);
assert(current_sandbox->state != SANDBOX_RETURNED);
/* Preempt */
local_runqueue_preempt(user_context);
/* We have to call current_sandbox_get because the argument potentially points to what
* was just preempted */
if (current_sandbox_get()->ctxt.preemptable) software_interrupt_enable();
return;
}
@ -150,7 +151,7 @@ sigusr1_handler(siginfo_t *signal_info, ucontext_t *user_context, struct sandbox
/* Extra checks to verify that preemption properly set context state */
assert(current_sandbox->ctxt.variant == ARCH_CONTEXT_VARIANT_SLOW);
software_interrupt_SIGUSR_count++;
atomic_fetch_add(&software_interrupt_SIGUSR_count, 1);
#ifdef LOG_PREEMPTION
debuglog("Total SIGUSR1 Received: %d\n", software_interrupt_SIGUSR_count);
@ -160,8 +161,6 @@ sigusr1_handler(siginfo_t *signal_info, ucontext_t *user_context, struct sandbox
arch_mcontext_restore(&user_context->uc_mcontext, &current_sandbox->ctxt);
software_interrupt_enable();
return;
}
@ -187,10 +186,13 @@ software_interrupt_validate_worker()
static inline void
software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void *user_context_raw)
{
/* If the runtime has preemption disabled, and we receive a signal, panic */
if (unlikely(!runtime_preemption_enabled)) {
panic("Unexpectedly invoked signal handlers with preemption disabled\n");
}
assert(software_interrupt_signal_depth < 2);
atomic_fetch_add(&software_interrupt_signal_depth, 1);
software_interrupt_validate_worker();
ucontext_t * user_context = (ucontext_t *)user_context_raw;
@ -198,20 +200,26 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void
switch (signal_type) {
case SIGALRM: {
return sigalrm_handler(signal_info, user_context, current_sandbox);
sigalrm_handler(signal_info, user_context, current_sandbox);
break;
}
case SIGUSR1: {
return sigusr1_handler(signal_info, user_context, current_sandbox);
sigusr1_handler(signal_info, user_context, current_sandbox);
break;
}
default: {
if (signal_info->si_code == SI_TKILL) {
switch (signal_info->si_code) {
case SI_TKILL:
panic("Unexpectedly received signal %d from a thread kill, but we have no handler\n",
signal_type);
} else if (signal_info->si_code == SI_KERNEL) {
case SI_KERNEL:
panic("Unexpectedly received signal %d from the kernel, but we have no handler\n", signal_type);
default:
panic("Anomolous Signal\n");
}
}
}
atomic_fetch_sub(&software_interrupt_signal_depth, 1);
}
/********************
@ -271,9 +279,17 @@ software_interrupt_initialize(void)
signal_action.sa_sigaction = software_interrupt_handle_signals;
signal_action.sa_flags = SA_SIGINFO | SA_RESTART;
/* all threads created by the calling thread will have signal blocked */
// TODO: Unclear about this...
sigemptyset(&signal_action.sa_mask);
// sigaddset(&signal_action.sa_mask, SIGALRM);
// sigaddset(&signal_action.sa_mask, SIGUSR1);
for (int i = 0;
i < (sizeof(software_interrupt_supported_signals) / sizeof(software_interrupt_supported_signals[0]));
i++) {
// TODO: Setup masks
int return_code = sigaction(software_interrupt_supported_signals[i], &signal_action, NULL);
if (return_code) {
perror("sigaction");

@ -43,6 +43,7 @@ __thread int worker_thread_idx;
static inline void
worker_thread_execute_epoll_loop(void)
{
assert(software_interrupt_is_disabled);
while (true) {
struct epoll_event epoll_events[RUNTIME_MAX_EPOLL_EVENTS];
int descriptor_count = epoll_wait(worker_thread_epoll_file_descriptor, epoll_events,
@ -63,9 +64,7 @@ worker_thread_execute_epoll_loop(void)
assert(sandbox);
if (sandbox->state == SANDBOX_BLOCKED) {
software_interrupt_disable();
sandbox_set_as_runnable(sandbox, SANDBOX_BLOCKED);
software_interrupt_enable();
}
} else if (epoll_events[i].events & (EPOLLERR | EPOLLHUP)) {
/* Mystery: This seems to never fire. Why? Issue #130 */
@ -112,13 +111,17 @@ worker_thread_execute_epoll_loop(void)
void *
worker_thread_main(void *argument)
{
/* The base worker thread should start with software interrupts disabled */
assert(software_interrupt_is_disabled);
/* Set base context as running */
worker_thread_base_context.variant = ARCH_CONTEXT_VARIANT_RUNNING;
/* Index was passed via argument */
worker_thread_idx = *(int *)argument;
/* Initialize Base Context as unused
* The SP and IP are populated during the first FAST switch away
*/
arch_context_init(&worker_thread_base_context, 0, 0);
/* Set my priority */
// runtime_set_pthread_prio(pthread_self(), 0);
/* Initialize Runqueue Variant */
switch (runtime_scheduler) {
@ -135,35 +138,30 @@ worker_thread_main(void *argument)
/* Initialize Completion Queue */
local_completion_queue_initialize();
/* Initialize Flags */
software_interrupt_is_disabled = false;
/* Initialize epoll */
worker_thread_epoll_file_descriptor = epoll_create1(0);
if (unlikely(worker_thread_epoll_file_descriptor < 0)) panic_err();
/* Unmask signals */
/* Unmask signals, unless the runtime has disabled preemption */
if (runtime_preemption_enabled) {
software_interrupt_unmask_signal(SIGALRM);
software_interrupt_unmask_signal(SIGUSR1);
}
/* 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;
while (true) {
assert(!software_interrupt_is_enabled());
/* Assumption: current_sandbox should be unset at start of loop */
assert(current_sandbox_get() == NULL);
worker_thread_execute_epoll_loop();
/* Switch to a sandbox if one is ready to run */
software_interrupt_disable();
next_sandbox = local_runqueue_get_next();
if (next_sandbox != NULL) {
sandbox_switch_to(next_sandbox);
} else {
software_interrupt_enable();
};
if (next_sandbox != NULL) { sandbox_switch_to(next_sandbox); }
assert(!software_interrupt_is_enabled());
/* Clear the completion queue */
local_completion_queue_free();

Loading…
Cancel
Save