#include #include #include #include #include #include #include #include #include #include #include #include #include __thread static volatile sig_atomic_t alarm_cnt = 0, usr1_cnt = 0; __thread volatile sig_atomic_t softint_off = 0; static const int softints[] = { SIGALRM, SIGUSR1 }; void softint_timer_arm(void) { #ifndef PREEMPT_DISABLE struct itimerval it; memset(&it, 0, sizeof(struct itimerval)); it.it_value.tv_usec = SOFTINT_TIMER_START_USEC; it.it_interval.tv_usec = SOFTINT_TIMER_PERIOD_USEC; int ret = setitimer(ITIMER_REAL, &it, NULL); if (ret) { perror("setitimer"); exit(1); } #endif } void softint_timer_disarm(void) { struct itimerval it; memset(&it, 0, sizeof(struct itimerval)); it.it_value.tv_sec = 0; it.it_interval.tv_usec = 0; int ret = setitimer(ITIMER_REAL, &it, NULL); if (ret) { perror("setitimer"); exit(1); } } static inline void softint_handler(int sig, siginfo_t *si, void *u) { #ifdef PREEMPT_DISABLE assert(0); #else struct sandbox *curr = sandbox_current(); ucontext_t *uc = (ucontext_t *)u; switch(sig) { case SIGALRM: { // if interrupts are disabled.. increment a per_thread counter and return if (si->si_code == SI_KERNEL) { // TODO: deliver signal to all other runtime threads.. } else { assert(si->si_code == SI_TKILL); } //debuglog("alrm:%d\n", alarm_cnt); alarm_cnt++; // softints per-core.. if (next_context) return; if (!softint_enabled()) return; // no sandboxes running..so nothing to preempt..let the "main" scheduler run its course. if (curr == NULL) return; // find a next sandbox to run.. struct sandbox *next = sandbox_schedule(); if (next == NULL) return; if (next == curr) return; // only this sandbox to schedule.. return to it! // save the current sandbox, state from uc! arch_mcontext_save(&curr->ctxt, &uc->uc_mcontext); // sandbox_current_set on it. restore through *uc.. sandbox_current_set(next); arch_mcontext_restore(&uc->uc_mcontext, &next->ctxt); // reset if SIGALRM happens before SIGUSR1 and if don't preempt..OR // perhaps switch here for SIGUSR1 and see if we can clear that signal // so it doesn't get called on SIGALRM return.. // next_context = NULL; break; } case SIGUSR1: { /* we set current before calling pthread_kill! */ assert(next_context && (&curr->ctxt == next_context)); assert(si->si_code == SI_TKILL); //debuglog("usr1:%d\n", usr1_cnt); usr1_cnt++; // sigalrm happened.. such a waste of time..!! if (next_context == NULL) return; // make sure sigalrm doesn't mess this up if nested.. assert(!softint_enabled()); // do not save current sandbox.. it is in co-operative switch.. // pick the next from "next_context".. // assert its "sp" to be zero in regs.. // memcpy from next context.. arch_mcontext_restore(&uc->uc_mcontext, &curr->ctxt); next_context = NULL; softint_enable(); break; } case SIGPIPE: case SIGILL: case SIGFPE: case SIGSEGV: { // determine if the crash was in the sandbox.. // if (pthread_self() == one_of_the_runtime_threads), a sandbox crashed.. kill it. // another check there could be if it is in linear memory or outside, if outside it could repeat with other sandboxes.. so perhaps restart that thread or start a fresh thread??. // else, shoot yourself in the head!.. break; } default: break; } #endif } void softint_init(void) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = softint_handler; sa.sa_flags = SA_SIGINFO | SA_RESTART; for (int i = 0; i < (sizeof(softints) / sizeof(softints[0])); i++) { int ret = sigaction(softints[i], &sa, NULL); if (ret) { perror("sigaction"); exit(1); } } }