Example #1
0
static BOOL WINAPI sigint_handler(DWORD wsig) //This needs winapi types to guarantee __stdcall
{
    int sig;
    //windows signals use different numbers from unix (raise)
    switch(wsig) {
        case CTRL_C_EVENT: sig = SIGINT; break;
        //case CTRL_BREAK_EVENT: sig = SIGTERM; break;
        // etc.
        default: sig = SIGTERM; break;
    }
    if (exit_on_sigint)
        jl_exit(128 + sig); // 128 + SIGINT
    jl_try_deliver_sigint();
    return 1;
}
Example #2
0
static void *signal_listener(void *arg)
{
    static uintptr_t bt_data[JL_MAX_BT_SIZE + 1];
    static size_t bt_size = 0;
    sigset_t sset;
    unw_context_t *signal_context;
    int sig, critical, profile;
    int i;
    jl_sigsetset(&sset);
    while (1) {
        profile = 0;
        sigwait(&sset, &sig);
#ifndef HAVE_MACH
#  ifdef HAVE_ITIMER
        profile = (sig == SIGPROF);
#  else
        profile = (sig == SIGUSR1);
#  endif
#endif
        if (sig == SIGINT) {
            if (exit_on_sigint) {
                critical = 1;
            }
            else {
                jl_try_deliver_sigint();
                continue;
            }
        }
        else {
            critical = 0;
        }

        critical |= (sig == SIGTERM);
        critical |= (sig == SIGABRT);
        critical |= (sig == SIGQUIT);
#ifdef SIGINFO
        critical |= (sig == SIGINFO);
#else
        critical |= (sig == SIGUSR1 && !profile);
#endif

        bt_size = 0;
        // sample each thread, round-robin style in reverse order
        // (so that thread zero gets notified last)
        for (i = jl_n_threads; i-- > 0; ) {
            // notify thread to stop
            jl_thread_suspend_and_get_state(i, &signal_context);

            // do backtrace on thread contexts for critical signals
            // this part must be signal-handler safe
            if (critical) {
                bt_size += rec_backtrace_ctx(bt_data + bt_size,
                        JL_MAX_BT_SIZE / jl_n_threads - 1,
                        signal_context);
                bt_data[bt_size++] = 0;
            }

            // do backtrace for profiler
            if (profile && running) {
                if (bt_size_cur < bt_size_max - 1) {
                    // Get backtrace data
                    bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur,
                            bt_size_max - bt_size_cur - 1, signal_context);
                    // Mark the end of this block with 0
                    bt_data_prof[bt_size_cur++] = 0;
                }
                if (bt_size_cur >= bt_size_max - 1) {
                    // Buffer full: Delete the timer
                    jl_profile_stop_timer();
                }
            }

            // notify thread to resume
            jl_thread_resume(i, sig);
        }

        // this part is async with the running of the rest of the program
        // and must be thread-safe, but not necessarily signal-handler safe
        if (critical) {
            jl_critical_error(sig, NULL, bt_data, &bt_size);
            // FIXME
            // It is unsafe to run the exit handler on this thread
            // (this thread is not managed and has a rather limited stack space)
            // try harder to run this on a managed thread.
#ifdef SIGINFO
            if (sig != SIGINFO)
#else
            if (sig != SIGUSR1)
#endif
                jl_exit(128 + sig);
        }
    }
}
Example #3
0
static void *signal_listener(void *arg)
{
    static uintptr_t bt_data[JL_MAX_BT_SIZE + 1];
    static size_t bt_size = 0;
    sigset_t sset;
    int sig, critical, profile;
    jl_sigsetset(&sset);
#ifdef HAVE_KEVENT
    struct kevent ev;
    int sigqueue = kqueue();
    if (sigqueue == -1) {
        perror("signal kqueue");
    }
    else {
        kqueue_signal(&sigqueue, &ev, SIGINT);
        kqueue_signal(&sigqueue, &ev, SIGTERM);
        kqueue_signal(&sigqueue, &ev, SIGABRT);
        kqueue_signal(&sigqueue, &ev, SIGQUIT);
#ifdef SIGINFO
        kqueue_signal(&sigqueue, &ev, SIGINFO);
#else
        kqueue_signal(&sigqueue, &ev, SIGUSR1);
#endif
#ifdef HAVE_ITIMER
        kqueue_signal(&sigqueue, &ev, SIGPROF);
#endif
    }
#endif
    while (1) {
        profile = 0;
        sig = 0;
        errno = 0;
#ifdef HAVE_KEVENT
        if (sigqueue != -1) {
            int nevents = kevent(sigqueue, NULL, 0, &ev, 1, NULL);
            if (nevents == -1) {
                if (errno == EINTR)
                    continue;
                perror("signal kevent");
            }
            if (nevents != 1) {
                close(sigqueue);
                sigqueue = -1;
                continue;
            }
            sig = ev.ident;
        }
        else
#endif
        if (sigwait(&sset, &sig)) {
            sig = SIGABRT; // this branch can't occur, unless we had stack memory corruption of sset
        }
        else if (!sig || errno == EINTR) {
            // This should never happen, but it has been observed to occur
            // when this thread gets used to handle run a signal handler (without SA_RESTART).
            // It would be nice to prohibit the kernel from doing that, by blocking signals on this thread,
            // (so that we aren't temporarily unable to handle the signals that this thread exists to handle)
            // but that sometimes results in the signals never getting delivered at all.
            // Apparently the only consistent way to handle signals with sigwait is all-or-nothing :(
            // And while sigwait handles per-process signals more sanely,
            // it can't really handle thread-targeted signals at all.
            // So signals really do seem to always just be lose-lose.
            continue;
        }
#ifndef HAVE_MACH
#  ifdef HAVE_ITIMER
        profile = (sig == SIGPROF);
#  else
        profile = (sig == SIGUSR1);
#  endif
#endif

        if (sig == SIGINT) {
            if (jl_ignore_sigint()) {
                continue;
            }
            else if (exit_on_sigint) {
                critical = 1;
            }
            else {
                jl_try_deliver_sigint();
                continue;
            }
        }
        else {
            critical = 0;
        }

        critical |= (sig == SIGTERM);
        critical |= (sig == SIGABRT);
        critical |= (sig == SIGQUIT);
#ifdef SIGINFO
        critical |= (sig == SIGINFO);
#else
        critical |= (sig == SIGUSR1 && !profile);
#endif

        int doexit = critical;
#ifdef SIGINFO
        if (sig == SIGINFO)
            doexit = 0;
#else
        if (sig == SIGUSR1)
            doexit = 0;
#endif

        bt_size = 0;
#if !defined(JL_DISABLE_LIBUNWIND)
        unw_context_t *signal_context;
        // sample each thread, round-robin style in reverse order
        // (so that thread zero gets notified last)
        for (int i = jl_n_threads; i-- > 0; ) {
            // notify thread to stop
            jl_thread_suspend_and_get_state(i, &signal_context);

            // do backtrace on thread contexts for critical signals
            // this part must be signal-handler safe
            if (critical) {
                bt_size += rec_backtrace_ctx(bt_data + bt_size,
                        JL_MAX_BT_SIZE / jl_n_threads - 1,
                        signal_context);
                bt_data[bt_size++] = 0;
            }

            // do backtrace for profiler
            if (profile && running) {
                if (bt_size_cur < bt_size_max - 1) {
                    // unwinding can fail, so keep track of the current state
                    // and restore from the SEGV handler if anything happens.
                    jl_ptls_t ptls = jl_get_ptls_states();
                    jl_jmp_buf *old_buf = ptls->safe_restore;
                    jl_jmp_buf buf;

                    ptls->safe_restore = &buf;
                    if (jl_setjmp(buf, 0)) {
                        jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n");
                    } else {
                        // Get backtrace data
                        bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur,
                                bt_size_max - bt_size_cur - 1, signal_context);
                    }
                    ptls->safe_restore = old_buf;

                    // Mark the end of this block with 0
                    bt_data_prof[bt_size_cur++] = 0;
                }
                if (bt_size_cur >= bt_size_max - 1) {
                    // Buffer full: Delete the timer
                    jl_profile_stop_timer();
                }
            }

            // notify thread to resume
            jl_thread_resume(i, sig);
        }
#endif

        // this part is async with the running of the rest of the program
        // and must be thread-safe, but not necessarily signal-handler safe
        if (critical) {
            jl_critical_error(sig, NULL, bt_data, &bt_size);
            if (doexit) {
                thread0_exit_count++;
                jl_exit_thread0(128 + sig);
            }
        }
    }
    return NULL;
}