Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
signals.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <sysio/vm/exceptions.hpp>
4#include <sysio/vm/utils.hpp>
5
6#include <atomic>
7#include <cstdlib>
8#include <exception>
9#include <utility>
10#include <signal.h>
11#include <setjmp.h>
12
13namespace sysio { namespace vm {
14
15 // Fixes a duplicate symbol build issue when building with `-fvisibility=hidden`
16 __attribute__((visibility("default")))
17 inline thread_local std::atomic<sigjmp_buf*> signal_dest = ATOMIC_VAR_INIT(nullptr);
18
19 // Fixes a duplicate symbol build issue when building with `-fvisibility=hidden`
20 __attribute__((visibility("default")))
21 inline thread_local std::exception_ptr saved_exception{nullptr};
22
23 template<int Sig>
24 inline struct sigaction prev_signal_handler;
25
26 inline void signal_handler(int sig, siginfo_t* info, void* uap) {
27 sigjmp_buf* dest = std::atomic_load(&signal_dest);
28 if (dest) {
29 siglongjmp(*dest, sig);
30 } else {
31 struct sigaction* prev_action;
32 switch(sig) {
33 case SIGSEGV: prev_action = &prev_signal_handler<SIGSEGV>; break;
34 case SIGBUS: prev_action = &prev_signal_handler<SIGBUS>; break;
35 case SIGFPE: prev_action = &prev_signal_handler<SIGFPE>; break;
36 default: std::abort();
37 }
38 if (!prev_action) std::abort();
39 if (prev_action->sa_flags & SA_SIGINFO) {
40 // FIXME: We need to be at least as strict as the original
41 // flags and relax the mask as needed.
42 prev_action->sa_sigaction(sig, info, uap);
43 } else {
44 if(prev_action->sa_handler == SIG_DFL) {
45 // The default for all three signals is to terminate the process.
46 sigaction(sig, prev_action, nullptr);
47 raise(sig);
48 } else if(prev_action->sa_handler == SIG_IGN) {
49 // Do nothing
50 } else {
51 prev_action->sa_handler(sig);
52 }
53 }
54 }
55 }
56
57 // only valid inside invoke_with_signal_handler.
58 // This is a workaround for the fact that it
59 // is currently unsafe to throw an exception through
60 // a jit frame.
61 template<typename F>
62 inline void longjmp_on_exception(F&& f) {
63 static_assert(std::is_trivially_destructible_v<std::decay_t<F>>, "longjmp has undefined behavior when it bypasses destructors.");
64 bool caught_exception = false;
65 try {
66 f();
67 } catch(...) {
68 saved_exception = std::current_exception();
69 // Cannot safely longjmp from inside the catch,
70 // as that will leak the exception.
71 caught_exception = true;
72 }
73 if (caught_exception) {
74 sigset_t block_mask;
75 sigemptyset(&block_mask);
76 sigaddset(&block_mask, SIGPROF);
77 pthread_sigmask(SIG_BLOCK, &block_mask, nullptr);
78 sigjmp_buf* dest = std::atomic_load(&signal_dest);
79 siglongjmp(*dest, -1);
80 }
81 }
82
83 template<typename E>
84 [[noreturn]] inline void throw_(const char* msg) {
85 saved_exception = std::make_exception_ptr(E{msg});
86 sigset_t block_mask;
87 sigemptyset(&block_mask);
88 sigaddset(&block_mask, SIGPROF);
89 pthread_sigmask(SIG_BLOCK, &block_mask, nullptr);
90 sigjmp_buf* dest = std::atomic_load(&signal_dest);
91 siglongjmp(*dest, -1);
92 }
93
95 struct sigaction sa;
96 sa.sa_sigaction = &signal_handler;
97 sigemptyset(&sa.sa_mask);
98 sigaddset(&sa.sa_mask, SIGPROF);
99 sa.sa_flags = SA_NODEFER | SA_SIGINFO;
100 sigaction(SIGSEGV, &sa, &prev_signal_handler<SIGSEGV>);
101 sigaction(SIGBUS, &sa, &prev_signal_handler<SIGBUS>);
102 sigaction(SIGFPE, &sa, &prev_signal_handler<SIGFPE>);
103 }
104
105 inline void setup_signal_handler() {
106 static int init_helper = (setup_signal_handler_impl(), 0);
108 static_assert(std::atomic<sigjmp_buf*>::is_always_lock_free, "Atomic pointers must be lock-free to be async signal safe.");
109 }
110
119 // Make this noinline to prevent possible corruption of the caller's local variables.
120 // It's unlikely, but I'm not sure that it can definitely be ruled out if both
121 // this and f are inlined and f modifies locals from the caller.
122 template<typename F, typename E>
123 [[gnu::noinline]] auto invoke_with_signal_handler(F&& f, E&& e) {
125 sigjmp_buf dest;
126 sigjmp_buf* volatile old_signal_handler = nullptr;
127 int sig;
128 if((sig = sigsetjmp(dest, 1)) == 0) {
129 // Note: Cannot use RAII, as non-trivial destructors w/ longjmp
130 // have undefined behavior. [csetjmp.syn]
131 //
132 // Warning: The order of operations is critical here.
133 // We also have to register signal_dest before unblocking
134 // signals to make sure that only our signal handler is executed
135 // if the caller has previously blocked signals.
136 old_signal_handler = std::atomic_exchange(&signal_dest, &dest);
137 sigset_t unblock_mask, old_sigmask; // Might not be preserved across longjmp
138 sigemptyset(&unblock_mask);
139 sigaddset(&unblock_mask, SIGSEGV);
140 sigaddset(&unblock_mask, SIGBUS);
141 sigaddset(&unblock_mask, SIGFPE);
142 sigaddset(&unblock_mask, SIGPROF);
143 pthread_sigmask(SIG_UNBLOCK, &unblock_mask, &old_sigmask);
144 try {
145 f();
146 pthread_sigmask(SIG_SETMASK, &old_sigmask, nullptr);
147 std::atomic_store(&signal_dest, old_signal_handler);
148 } catch(...) {
149 pthread_sigmask(SIG_SETMASK, &old_sigmask, nullptr);
150 std::atomic_store(&signal_dest, old_signal_handler);
151 throw;
152 }
153 } else {
154 std::atomic_store(&signal_dest, old_signal_handler);
155 if (sig == -1) {
156 std::exception_ptr exception = std::move(saved_exception);
157 saved_exception = nullptr;
158 std::rethrow_exception(exception);
159 } else {
160 e(sig);
161 }
162 }
163 }
164
165}} // namespace sysio::vm
std::shared_ptr< exception > exception_ptr
__attribute__((always_inline)) inline uint64_t rotl64(uint64_t x
Definition name.hpp:106
auto invoke_with_signal_handler(F &&f, E &&e)
Definition signals.hpp:123
__attribute__((visibility("default"))) inline thread_local std __attribute__((visibility("default"))) inline thread_local std struct sigaction prev_signal_handler
Definition signals.hpp:24
void setup_signal_handler()
Definition signals.hpp:105
void setup_signal_handler_impl()
Definition signals.hpp:94
void ignore_unused_variable_warning(T &...)
Definition utils.hpp:101
void longjmp_on_exception(F &&f)
Definition signals.hpp:62
void throw_(const char *msg)
Definition signals.hpp:84
void signal_handler(int sig, siginfo_t *info, void *uap)
Definition signals.hpp:26