The standard says it's necessary. If you have a `volatile long long` on a 32-bit architecture, the compiler will have to compile it to some bignum code (meaning multiple instructions), and the signal can come in between them. I can quickly make it happen on a Nintendo Wii (32-bit powerpc) as well as an i386 laptop using this test program: ``` #define _POSIX_C_SOURCE 200809L #include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> static void catch_sigalrm(int); volatile unsigned long long longword; volatile sig_atomic_t partial_update; int main(void) { const struct sigaction act = { .sa_handler = catch_sigalrm }; const struct itimerval timer = { .it_value.tv_usec = 1, .it_interval.tv_usec = 1, }; unsigned long long n; sigaction(SIGALRM, &act, NULL); setitimer(ITIMER_REAL, &timer, NULL); for (unsigned char i = 0;; i++) { memset(&n, i, sizeof(n)); longword = n; if (partial_update) { fprintf(stderr, "longword partially updated\n"); exit(1); } } } static void catch_sigalrm(int unused) { union multibyte { long long n; unsigned char bytes[sizeof(long long)]; } window; (void)unused; window.n = longword; for (size_t i = 1; i < sizeof(window.bytes); i++) { if (window.bytes[i] != window.bytes[0]) partial_update = 1; } } ``` Output: ``` $ cc -O2 test.c && ./a.out longword partially updated ``` The program runs (apparently) forever on my amd64 desktop. If you look at the powerpc assembly version of the program in Godbolt: https://godbolt.org/z/Pc8q7E5ej Lines 69 and 70 of the assembly use 2 STW instructions to store each 32-bit half of the bignum. On Wed, Mar 20, 2024 at 04:44:33PM -0400, Elad Lahav wrote: > Do you really need volatile? > There are two cases to consider. Either your code synchronizes updates > to the shared value with the signal handler (e.g., by blocking and > then unblocking the signal), in which case I believe the compiler > cannot ignore updates to the value; or you don't, and you can't depend > on the variable having any specific value in the signal handler. The > only thing you want to prevent in the latter case is the handler > observing a partial update to the variable, which I presume is where > the other requirements originate. (In practice, there should be little > or no concern with any primitive type on modern hardware). > > --Elad > > On Wed, Mar 20, 2024 at 4:32 PM Guilherme Janczak > <guilherme.janczak@xxxxxxxxxx> wrote: > > > > Variables shared with signal handlers must be of type `volatile > > sigatomic_t`, not `volatile` or `sigatomic_t` as the current text says, > > according to a C11 draft: > > > > When ... interrupted by ... a signal, values of objects that are > > neither lock-free atomic objects nor of type volatile sig_atomic_t > > are unspecified. > > > > Ref: https://www.iso-9899.info/n1570.html#5.1.2.3p5 > > Signed-off-by: Guilherme Janczak <guilherme.janczak@xxxxxxxxxx> > > --- > > memorder/memorder.tex | 4 ++-- > > 1 file changed, 2 insertions(+), 2 deletions(-) > > > > diff --git a/memorder/memorder.tex b/memorder/memorder.tex > > index 5c50d42d..873c3424 100644 > > --- a/memorder/memorder.tex > > +++ b/memorder/memorder.tex > > @@ -1317,8 +1317,8 @@ from the viewpoint of the interrupted thread, at least at the > > assembly-language level. > > However, the C and C++ languages do not define the results of handlers > > and interrupted threads sharing plain variables. > > -Instead, such shared variables must be \co{sig_atomic_t}, lock-free > > -atomics, or \co{volatile}. > > +Instead, such shared variables must be \co{volatile sig_atomic_t} or > > +lock-free atomics. > > > > On the other hand, because the handler executes within the interrupted > > thread's context, the memory ordering used to synchronize communication > > -- > > 2.42.0 > > > >