diff --git a/runtime/include/arch/aarch64/context.h b/runtime/include/arch/aarch64/context.h index 7bb99dc..53933dd 100644 --- a/runtime/include/arch/aarch64/context.h +++ b/runtime/include/arch/aarch64/context.h @@ -2,8 +2,11 @@ #if defined(AARCH64) || defined(aarch64) #include +#include + #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" diff --git a/runtime/include/arch/context.h b/runtime/include/arch/context.h index 67e7b0f..72443aa 100644 --- a/runtime/include/arch/context.h +++ b/runtime/include/arch/context.h @@ -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 */ diff --git a/runtime/include/arch/x86_64/context.h b/runtime/include/arch/x86_64/context.h index 67afedc..5f90818 100644 --- a/runtime/include/arch/x86_64/context.h +++ b/runtime/include/arch/x86_64/context.h @@ -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) diff --git a/runtime/include/current_sandbox_block.h b/runtime/include/current_sandbox_block.h index 6ccd510..6904088 100644 --- a/runtime/include/current_sandbox_block.h +++ b/runtime/include/current_sandbox_block.h @@ -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(); diff --git a/runtime/include/current_sandbox_yield.h b/runtime/include/current_sandbox_yield.h index f3354f3..40d3730 100644 --- a/runtime/include/current_sandbox_yield.h +++ b/runtime/include/current_sandbox_yield.h @@ -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(); } diff --git a/runtime/include/software_interrupt.h b/runtime/include/software_interrupt.h index 1a6325c..19be6f4 100644 --- a/runtime/include/software_interrupt.h +++ b/runtime/include/software_interrupt.h @@ -10,12 +10,16 @@ #include #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); diff --git a/runtime/src/arch_context.c b/runtime/src/arch_context.c index c20ff12..4510a1b 100644 --- a/runtime/src/arch_context.c +++ b/runtime/src/arch_context.c @@ -4,6 +4,7 @@ #include #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"); } diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index aedb923..f88bd5e 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -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; diff --git a/runtime/src/local_runqueue_minheap.c b/runtime/src/local_runqueue_minheap.c index ff330ee..f01c6cb 100644 --- a/runtime/src/local_runqueue_minheap.c +++ b/runtime/src/local_runqueue_minheap.c @@ -1,5 +1,6 @@ #include +#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, diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index ee20437..09ba5b7 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -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); } diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index dea01a5..b11a948 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -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 = ¤t_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 - || ¤t_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 = ¤t_sandbox->ctxt; - #ifdef LOG_CONTEXT_SWITCHES debuglog("Sandbox %lu (@%p) (%s) > Sandbox %lu (@%p) (%s)\n", current_sandbox->id, ¤t_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 - || ¤t_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); } diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index f23d6bb..d3c962b 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -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, ¤t_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"); diff --git a/runtime/src/worker_thread.c b/runtime/src/worker_thread.c index e727cbf..7c54945 100644 --- a/runtime/src/worker_thread.c +++ b/runtime/src/worker_thread.c @@ -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();