30

On my Linux box, sig_atomic_t is a plain old int. Do ints posses a special atomic quality?

$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
...
Thread model: posix
gcc version 4.3.2 (Debian 4.3.2-1.1) 

$ echo '#include <signal.h>' | gcc -E - | grep atomic
typedef int __sig_atomic_t;
typedef __sig_atomic_t sig_atomic_t;
chrisaycock
  • 36,470
  • 14
  • 88
  • 125
smcdow
  • 699
  • 1
  • 7
  • 12
  • @chrisaycock: C? Wouldn't this apply any any language with access to `sig_atomic_t` variables? I could have just as easily used `g++`. – smcdow Mar 07 '12 at 18:55
  • 4
    The `sig_atomic_t` type is actually part of the C specification. It's also found in the C++ specification (as most things are), but you are most likely to get good answers with a C tag. – Dietrich Epp Mar 07 '12 at 19:31
  • 2
    C doesn't guarantee that. Specific CPUs have specific guarantees for atomicity of certain types. In particular, modern CPUs used in desktop systems tend to guarantee atomicity at least for aligned `int`s (i.e. platform-word-sized data). This is not necesarily the case for older CPUs, or for CPUs used in embedded systems. – ninjalj Mar 07 '12 at 19:59

2 Answers2

38

C99 sig_atomic_t conforms only to a very weak definition of "atomicity", because C99 has no concept of concurrency, only interruptibility. (C2011 adds a concurrency model, and with it the _Atomic types that make stronger guarantees; however, AFAIK sig_atomic_t is unchanged, since its raison d'être is still communication with signal handlers, not across threads.)

This is everything C99 says about sig_atomic_t:

(§7.14 <signal.h>, paragraph 2) The type defined is sig_atomic_t, which is the (possibly volatile-qualified) integer type of an object that can be accessed as an atomic entity, even in the presence of asynchronous interrupts. (§7.14 <signal.h>, paragraph 2)

(§7.14p5) If [a] signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t.

(§7.18.3 Limits of other integer types, paragraph 3) If sig_atomic_t (see 7.14) is defined as a signed integer type, the value of SIG_ATOMIC_MIN shall be no greater than −127 and the value of SIG_ATOMIC_MAX shall be no less than 127; otherwise, sig_atomic_t is defined as an unsigned integer type, and the value of SIG_ATOMIC_MIN shall be 0 and the value of SIG_ATOMIC_MAX shall be no less than 255.

The term "atomic entity" is not defined anywhere in the standard. Translating from standards-ese, the intent is that the CPU can completely update a variable of type sig_atomic_t in memory ("static storage duration") with one machine instruction. Thus, in the concurrency-free, precisely interruptible C99 abstract machine, it is impossible for a signal handler to observe a variable of type sig_atomic_t halfway through an update. The §7.18.3p3 language licenses this type to be as small as char if necessary. Note please the complete absence of any language relating to cross-processor consistency.

There are real CPUs which require more than one instruction to write a value larger than char to memory. There are also real CPUs which require more than one instruction to write values smaller than a machine word (often, but not necessarily, the same as int) to memory. The language in the GNU C Library manual is now inaccurate. It represents a desire on the part of the original authors to eliminate what they saw as unnecessary license for C implementations to do weird shit that made life harder for application programmers. Unfortunately, that very license is what makes it possible to have C at all on some real machines. There is at least one embedded Linux port (to the AVR) for which neither int nor pointers can be written to memory in one instruction. (People are working on making the manual more accurate, see e.g. http://sourceware.org/ml/libc-alpha/2012-02/msg00651.html -- sig_atomic_t seems to have been missed in that one, though.)

Community
  • 1
  • 1
zwol
  • 135,547
  • 38
  • 252
  • 361
  • 1
    Heh, looks like not. It would probably be better off with something smaller :) I tend to think, though, that Linux/AVR with whatever C library is more likely to be a relevant portability target nowadays than the Hurd or other boutique-but-not-embedded OSes. – zwol Mar 07 '12 at 20:12
  • Ok, then I'll assume the glibc manual isn't inaccurate. (Notice that it says: _In practice_ and _these are true on all of the machines that the GNU C library supports_) – ninjalj Mar 07 '12 at 20:18
  • Note that `sig_atomic_t` doesn't do anything to make read-modify-write operations atomic with respect to signals. On x86, interrupts can't split up `dec dword [mem]`, so it's atomic in a Uniprocessor kernel with respect to interrupts, or a single-threaded process with respect to signals [2nd half of this answer](http://stackoverflow.com/a/39358907/224132). But (AFAIK) there's no ISO or GNU C11 way to request that `num--` compiles to that instead of separate load/store insns, without using an atomic that will compile to `lock dec dword [num]`. Not counting inline asm of course. – Peter Cordes Sep 09 '16 at 17:14
  • **Just to be specific about the limitations**: You also don't get control over compile-time reordering, and the compiler assumes the object isn't asynchronously modified. (So `while(updated_by_sig_handler == 0){}` can compile to `if(update_by_sig_handler == 0){ while(true); }` instead of a loop that re-reads memory inside the loop, until it sees a value written by a signal handler. Or hoist the load out of a loop that does do something while waiting. Writing spin-loops yourself is almost always bad, even if you do use C11 atomics, but it's an easy example.) – Peter Cordes Sep 09 '16 at 17:22
  • @PeterCordes Both of your points are covered by C99 7.14.1.1p5: "If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static storage duration other than by *assigning a value* to an object declared as `volatile sig_atomic_t`." It has to be `volatile` so that the compiler does *not* assume the object isn't asynchronously modified (this is one of the very few things that `volatile` is actually designed to do) and "assigning a value" limits it to `var = (expression not involving var)`. – zwol Sep 09 '16 at 18:59
  • @PeterCordes (In case it isn't clear, if the expression on the RHS of the assignment *did* involve `var`, that would be referring to `var` _other than by assigning a value_.) – zwol Sep 09 '16 at 19:01
  • @PeterCordes (In C11 that sentence is amended to "...refers to any object with static or thread storage duration that is not a lock-free atomic object other than by..." which I think means you can freely use any `_Atomic` operation inside a signal handler, as long as the implementation supports that particular operation.) – zwol Sep 09 '16 at 19:06
  • 1
    Oh right, I saw this language in the standard, but I didn't stop to wrap my head around it. And of course it has to be `volatile`, derp, that does trigger the async-modification assumption. Thanks for explanation, I see what that passage of legalese is saying now. Interesting point that the `_Atomic` has to be lock-free. Since it's a signal-handler in the same thread, it would create a potential deadlock to have the signal handler try to acquire a lock that the thread sometimes holds. – Peter Cordes Sep 09 '16 at 19:19
6

Certain types may require multiple instruction to read/write. int type is always read/written atomically.

Data Type: sig_atomic_t

This is an integer data type. Objects of this type are always accessed atomically.

In practice, you can assume that int and other integer types no longer than int are atomic. You can also assume that pointer types are atomic; that is very convenient. Both of these are true on all of the machines that the GNU C library supports, and on all POSIX systems we know of.

Reference

perreal
  • 94,503
  • 21
  • 155
  • 181
  • 10
    It's *usually* the case that `int` is always read and written atomically. The C standard doesn't specifically guarantee this. The implementation, by defining `sig_atomic_t` as `int`, promises that that's the case *for that implementation*. You shouldn't assume that it's true for all implementations. – Keith Thompson Mar 07 '12 at 18:40
  • A more current [reference](http://www.gnu.org/software/libc/manual/html_mono/libc.html#Atomic-Data-Access). Just to clarify, this only applies to the GNU C library? With or without GCC? I've never heard of a simple type having atomic access in standard C (excluding normal atomic methods and locking mechanisms). – Ioan Mar 07 '12 at 18:42
  • @Ioan C99 has no concurrency model, so what it means by "atomic" is something very weak compared to what you're probably thinking of. See my answer. – zwol Mar 07 '12 at 19:28