WG14 threadsafe signals
Loading...
Searching...
No Matches
thrd_signal_handle.h
Go to the documentation of this file.
1/* Proposed WG14 improved signals support
2(C) 2025 - 2026 Niall Douglas <http://www.nedproductions.biz/>
3File Created: Feb 2025
4
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License in the accompanying file
9Licence.txt or at
10
11http://www.apache.org/licenses/LICENSE-2.0
12
13Unless required by applicable law or agreed to in writing, software
14distributed under the License is distributed on an "AS IS" BASIS,
15WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16See the License for the specific language governing permissions and
17limitations under the License.
18*/
19
20#ifndef WG14_SIGNALS_THREAD_LOCAL_SIGNAL_HANDLE_H
21#define WG14_SIGNALS_THREAD_LOCAL_SIGNAL_HANDLE_H
22
23#include "config.h"
24
25#include <signal.h>
26#include <stdbool.h>
27#include <stdint.h>
28
29#if __GLIBC__
30// glibc defines a userspace siginfo_t as an unnamed struct. No
31// choice here but to drag in the relevant header.
32#include <bits/types/siginfo_t.h>
33#endif
34
35
36#ifdef __cplusplus
37extern "C"
38{
39#endif
40
41#ifdef _WIN32
42 // MSVC may be missing necessary signal support
43 typedef uint32_t sigset_t;
44 static inline void sigemptyset(sigset_t *ss)
45 {
46 *ss = 0;
47 }
48 static inline void sigfillset(sigset_t *ss)
49 {
50 *ss = UINT32_MAX;
51 }
52 static inline void sigaddset(sigset_t *ss, const int signo)
53 {
54 *ss |= (1u << (signo - 1));
55 }
56 static inline void sigdelset(sigset_t *ss, const int signo)
57 {
58 *ss &= ~(1u << (signo - 1));
59 }
60 static inline bool sigismember(const sigset_t *ss, const int signo)
61 {
62 return (*ss & (1u << (signo - 1))) != 0;
63 }
64
65// MSVC appears to follow the Linux signal numbering
66#ifndef SIGBUS
67#define SIGBUS (7)
68#endif
69#ifndef SIGKILL
70#define SIGKILL (9)
71#endif
72#ifndef SIGSTOP
73#define SIGSTOP (19)
74#endif
75#endif
76
77#ifndef WG14_SIGNALS_DISABLE_SIGFENCE_MACRO
78 WG14_SIGNALS_EXTERN void WG14_SIGNALS_PREFIX(sigfence_force_escaped)(int,
79 ...);
80#define SIGFENCE_GLUE(x, y) x y
81#define SIGFENCE_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, \
82 count, ...) \
83 count
84#define SIGFENCE_EXPAND_ARGS(args) SIGFENCE_RETURN_ARG_COUNT args
85#define SIGFENCE_COUNT_ARGS_MAX8(...) \
86 SIGFENCE_EXPAND_ARGS((__VA_ARGS__ __VA_OPT__(, ) 8, 7, 6, 5, 4, 3, 2, 1, 0))
87#define SIGFENCE_OVERLOAD_MACRO2(name, count) name##count
88#define SIGFENCE_OVERLOAD_MACRO1(name, count) \
89 SIGFENCE_OVERLOAD_MACRO2(name, count)
90#define SIGFENCE_OVERLOAD_MACRO(name, count) \
91 SIGFENCE_OVERLOAD_MACRO1(name, count)
92#define SIGFENCE_CALL_OVERLOAD(name, ...) \
93 SIGFENCE_GLUE( \
94 SIGFENCE_OVERLOAD_MACRO(name, SIGFENCE_COUNT_ARGS_MAX8(__VA_ARGS__)), \
95 (__VA_ARGS__))
96
97#if defined(__GNUC__) || defined(__clang__)
98// On compilers with extended inline asm, we can tell the compiler that a
99// specific list of variables must be specifically written out and reloaded
100// around the fence. You may find https://godbolt.org/z/chh8ee6Mj useful to
101// review.
102#define SIGFENCE_IMPL_0() __asm__ volatile(";" : : : "memory")
103#define SIGFENCE_IMPL_1(a) __asm__ volatile(";" : "+m"(a) : : "memory")
104#define SIGFENCE_IMPL_2(a, b) \
105 __asm__ volatile(";" : "+m"(a), "+m"(b) : : "memory")
106#define SIGFENCE_IMPL_3(a, b, c) \
107 __asm__ volatile(";" : "+m"(a), "+m"(b), "+m"(c) : : "memory")
108#define SIGFENCE_IMPL_4(a, b, c, d) \
109 __asm__ volatile(";" : "+m"(a), "+m"(b), "+m"(c), "+m"(d) : : "memory")
110#define SIGFENCE_IMPL_5(a, b, c, d, e) \
111 __asm__ volatile(";" \
112 : "+m"(a), "+m"(b), "+m"(c), "+m"(d), "+m"(e) \
113 : \
114 : "memory")
115#define SIGFENCE_IMPL_6(a, b, c, d, e, f) \
116 __asm__ volatile(";" \
117 : "+m"(a), "+m"(b), "+m"(c), "+m"(d), "+m"(e), "+m"(f) \
118 : \
119 : "memory")
120#define SIGFENCE_IMPL_7(a, b, c, d, e, f, g) \
121 __asm__ volatile(";" \
122 : "+m"(a), "+m"(b), "+m"(c), "+m"(d), "+m"(e), "+m"(f), \
123 "+m"(g) \
124 : \
125 : "memory")
126#define SIGFENCE_IMPL_8(a, b, c, d, e, f, g, h) \
127 __asm__ volatile(";" \
128 : "+m"(a), "+m"(b), "+m"(c), "+m"(d), "+m"(e), "+m"(f), \
129 "+m"(g), "+m"(h) \
130 : \
131 : "memory")
132#else
133 // This is a much less efficient way of forcing the compiler to treat
134 // variables as having escaped, but it's portable.
135#define SIGFENCE_IMPL_0() WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0)
136#define SIGFENCE_IMPL_1(a) WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a))
137#define SIGFENCE_IMPL_2(a, b) \
138 WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a), &(b))
139#define SIGFENCE_IMPL_3(a, b, c) \
140 WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a), &(b), &(c))
141#define SIGFENCE_IMPL_4(a, b, c, d) \
142 WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a), &(b), &(c), &(d))
143#define SIGFENCE_IMPL_5(a, b, c, d, e) \
144 WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a), &(b), &(c), &(d), &(e))
145#define SIGFENCE_IMPL_6(a, b, c, d, e, f) \
146 WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a), &(b), &(c), &(d), &(e), \
147 &(f))
148#define SIGFENCE_IMPL_7(a, b, c, d, e, f, g) \
149 WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a), &(b), &(c), &(d), &(e), \
150 &(f), &(g))
151#define SIGFENCE_IMPL_8(a, b, c, d, e, f, g, h) \
152 WG14_SIGNALS_PREFIX(sigfence_force_escaped)(0, &(a), &(b), &(c), &(d), &(e), \
153 &(f), &(g), &(h))
154#endif
157#define sigfence(...) SIGFENCE_CALL_OVERLOAD(SIGFENCE_IMPL_, __VA_ARGS__)
158#endif
159
160#ifdef _MSC_VER
161#pragma warning(push)
162#pragma warning(disable : 4190) // C-linkage with UDTs
163#endif
164#if defined(__clang__) && defined(__cplusplus)
165#pragma clang diagnostic push
166#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
167#endif
168
172 union WG14_SIGNALS_PREFIX(stdc_siginfo_value)
173 {
174 intptr_t int_value;
176#if defined(__cplusplus)
177 constexpr WG14_SIGNALS_PREFIX(stdc_siginfo_value)()
178 : int_value(0)
179 {
180 }
181 constexpr WG14_SIGNALS_PREFIX(stdc_siginfo_value)(int v)
182 : int_value(v)
183 {
184 }
185 constexpr WG14_SIGNALS_PREFIX(stdc_siginfo_value)(void *v)
186 : ptr_value(v)
187 {
188 }
189#endif
190 };
191
192#ifdef _WIN32
193 typedef long WG14_SIGNALS_PREFIX(thrd_raised_signal_error_code_t);
194#else
195typedef int WG14_SIGNALS_PREFIX(thrd_raised_signal_error_code_t);
196#endif
197
200#ifdef _WIN32
201 typedef struct _EXCEPTION_RECORD WG14_SIGNALS_PREFIX(stdc_siginfo_siginfo_t);
202#elif __GLIBC__
203typedef siginfo_t WG14_SIGNALS_PREFIX(stdc_siginfo_siginfo_t);
204#elif __ANDROID__
205typedef struct siginfo WG14_SIGNALS_PREFIX(stdc_siginfo_siginfo_t);
206#else
207typedef struct __siginfo WG14_SIGNALS_PREFIX(stdc_siginfo_siginfo_t);
208#endif
209
212#ifdef _WIN32
213 typedef struct _CONTEXT WG14_SIGNALS_PREFIX(stdc_siginfo_context_t);
214#else
215typedef ucontext_t WG14_SIGNALS_PREFIX(stdc_siginfo_context_t);
216#endif
217
221 struct WG14_SIGNALS_PREFIX(stdc_siginfo)
222 {
223 int signo;
224
228 void *addr;
229 union WG14_SIGNALS_PREFIX(
231
233 WG14_SIGNALS_PREFIX(stdc_siginfo_siginfo_t) * raw_info;
235 WG14_SIGNALS_PREFIX(stdc_siginfo_context_t) * raw_context;
236 };
237
239 typedef union WG14_SIGNALS_PREFIX(stdc_siginfo_value)(WG14_SIGNALS_PREFIX(
240 sig_func_t))(union WG14_SIGNALS_PREFIX(stdc_siginfo_value));
241
244 typedef union WG14_SIGNALS_PREFIX(stdc_siginfo_value)(WG14_SIGNALS_PREFIX(
245 sig_recover_t))(const struct WG14_SIGNALS_PREFIX(stdc_siginfo) *);
246
248 enum WG14_SIGNALS_PREFIX(sig_decision_t)
249 {
251 WG14_SIGNALS_PREFIX(sig_decision_next_decider),
253 WG14_SIGNALS_PREFIX(sig_decision_resume_execution),
257 WG14_SIGNALS_PREFIX(sig_decision_invoke_recovery)
258 };
259
262 typedef enum WG14_SIGNALS_PREFIX(sig_decision_t)(WG14_SIGNALS_PREFIX(
263 sig_decide_t))(struct WG14_SIGNALS_PREFIX(stdc_siginfo) *);
264
281 WG14_SIGNALS_PREFIX(sigfillset_synchronous)(sigset_t *set);
282
310 WG14_SIGNALS_PREFIX(sigfillset_asynchronous_nondebug)(sigset_t *set);
311
326 WG14_SIGNALS_PREFIX(sigfillset_asynchronous_debug)(sigset_t *set);
327
348 WG14_SIGNALS_EXTERN union WG14_SIGNALS_PREFIX(stdc_siginfo_value)
349 WG14_SIGNALS_PREFIX(sigguarded)(const sigset_t *signals,
350 WG14_SIGNALS_PREFIX(sig_func_t) guarded,
351 WG14_SIGNALS_PREFIX(sig_recover_t) recovery,
352 WG14_SIGNALS_PREFIX(sig_decide_t) decider,
353 union WG14_SIGNALS_PREFIX(stdc_siginfo_value)
354 value);
355#if defined(__clang__) && defined(__cplusplus)
356#pragma clang diagnostic pop
357#endif
358#ifdef _MSC_VER
359#pragma warning(pop)
360#endif
361
389 WG14_SIGNALS_EXTERN bool WG14_SIGNALS_PREFIX(stdc_raise)(
390 int signo, WG14_SIGNALS_PREFIX(stdc_siginfo_siginfo_t) * raw_info,
391 WG14_SIGNALS_PREFIX(stdc_siginfo_context_t) * raw_context);
392
419 WG14_SIGNALS_PREFIX(siginstall)(const sigset_t *guarded);
422 WG14_SIGNALS_EXTERN int WG14_SIGNALS_PREFIX(siguninstall)(void *i);
425 WG14_SIGNALS_EXTERN int WG14_SIGNALS_PREFIX(siguninstall_system)(int version);
426
446 WG14_SIGNALS_EXTERN void *WG14_SIGNALS_PREFIX(signal_decider_create)(
447 const sigset_t *guarded, bool callfirst,
448 WG14_SIGNALS_PREFIX(sig_decide_t) decider,
449 union WG14_SIGNALS_PREFIX(stdc_siginfo_value) value);
456 WG14_SIGNALS_PREFIX(signal_decider_destroy)(void *decider);
457
458
459#ifdef __cplusplus
460}
461#endif
462
463#if WG14_SIGNALS_ENABLE_HEADER_ONLY
464#ifdef _WIN32
465#include "detail/impl/thrd_signal_handle_windows.c.ipp"
466#else
467#include "detail/impl/thrd_signal_handle_posix.c.ipp"
468#endif
469#endif
470
471
472#endif
#define WG14_SIGNALS_EXTERN
Definition config.h:113
A platform independent subset of siginfo_t.
int signo
The signal raised.
thrd_raised_signal_error_code_t error_code
The system specific error code for this signal, the si_errno code (POSIX) or NTSTATUS code (Windows).
stdc_siginfo_context_t * raw_context
The OS specific ucontext_t (POSIX) or PCONTEXT (Windows).
void * addr
Memory location which caused fault, if appropriate.
union stdc_siginfo_value value
A user-defined value.
stdc_siginfo_siginfo_t * raw_info
The OS specific signal info.
int sigfillset_asynchronous_nondebug(sigset_t *set)
THREADSAFE ASYNC-SIGNAL-SAFE Fills the set of non-debug asynchronous signals for this platform.
struct __siginfo stdc_siginfo_siginfo_t
A placeholder type for an OS specific siginfo_t * (POSIX) or PEXCEPTION_RECORD (Windows).
void * siginstall(const sigset_t *guarded)
THREADSAFE Installs, and potentially enables, the global signal handlers for the signals specified by...
enum sig_decision_t sig_decide_t(struct stdc_siginfo *)
The type of the function called when a signal is raised. Returns a decision of how to handle the sign...
union stdc_siginfo_value sig_func_t(union stdc_siginfo_value)
The type of the guarded function.
int siguninstall(void *i)
THREADSAFE Uninstall a previously installed signal guard.
sig_decision_t
The decision taken by the decider function.
@ sig_decision_resume_execution
We have fixed the cause of the signal, please resume execution.
@ sig_decision_invoke_recovery
Thread local signal deciders only: reset the stack and local state to entry to sigguarded(),...
@ sig_decision_next_decider
We have decided to do nothing.
void * signal_decider_create(const sigset_t *guarded, bool callfirst, sig_decide_t decider, union stdc_siginfo_value value)
THREADSAFE NOT REENTRANT Create a global signal continuation decider. Threadsafe with respect to othe...
int sigfillset_asynchronous_debug(sigset_t *set)
THREADSAFE ASYNC-SIGNAL-SAFE Fille the set of debug asynchronous signals for this platform.
bool stdc_raise(int signo, stdc_siginfo_siginfo_t *raw_info, stdc_siginfo_context_t *raw_context)
THREADSAFE USUALLY ASYNC-SIGNAL-SAFE Call OUR currently installed signal decider for a signal (POSIX)...
int signal_decider_destroy(void *decider)
THREADSAFE NOT REENTRANT Destroy a global signal continuation decider. Threadsafe with respect to oth...
int sigfillset_synchronous(sigset_t *set)
THREADSAFE ASYNC-SIGNAL-SAFE Fills the set of synchronous signals for this platform.
ucontext_t stdc_siginfo_context_t
A placeholder type for an OS specific ucontext_t (POSIX) or PCONTEXT (Windows).
void sigfence_force_escaped(int,...)
union stdc_siginfo_value sigguarded(const sigset_t *signals, sig_func_t guarded, sig_recover_t recovery, sig_decide_t decider, union stdc_siginfo_value value)
THREADSAFE USUALLY ASYNC-SIGNAL-SAFE Installs a thread-local signal guard for the calling thread,...
int thrd_raised_signal_error_code_t
Typedef to a system specific error code type.
union stdc_siginfo_value sig_recover_t(const struct stdc_siginfo *)
The type of the function called to recover from a signal being raised in a guarded section.
int siguninstall_system(int version)
THREADSAFE Uninstall a previously system installed signal guard.
User defined value.