How does the compiler or OS distinguish between sig_atomic_t type and a normal int type variable, and ensures that the operation will be atomic? Programs using both have same assembler code. How extra care is taken to make the operation atomic?
6 Answers
sig_atomic_t
is not an atomic data type. It is just the data type that you are allowed to use in the context of a signal handler, that is all. So better read the name as "atomic relative to signal handling".
To guarantee communication with and from a signal handler, only one of the properties of atomic data types is needed, namely the fact that read and update will always see a consistent value. Other data types (such as perhaps long long
) could be written with several assembler instructions for the lower and higher part, e.g. sig_atomic_t
is guaranteed to be read and written in one go.
So a platform may choose any integer base type as sig_atomic_t
for which it can make the guarantee that volatile sig_atomic_t
can be safely used in signal handlers. Many platforms chose int
for this, because they know that for them int
is written with a single instruction.
The latest C standard, C11, has atomic types, but which are a completely different thing. Some of them (those that are "lockfree") may also be used in signal handlers, but that again is a completely different story.

- 3,475
- 7
- 34
- 46

- 76,821
- 6
- 102
- 177
-
If both type are same (int as in Linux) how they are treated differently, as many sites suggested, making a variable 'volatile sig_atomic_t' actually ensures atomic and thread safe operation – Chu Jul 24 '14 at 12:43
-
5@Satchit, they are not treated differently. Platforms that use plain `int` for this give you a guarantee that `volatile int` is sufficient for signal handlers. This is normally nothing you as a user should or could know, but the compiler implementor has to look that up and choose the correct base type for this. And again **`sig_atomic_t` is not an atomic type**. – Jens Gustedt Jul 24 '14 at 17:01
-
1So in multithreading application it doesn't have any significance? That is declaring a global variable as volatile int a is same as declaring volatile sig_atomic_t a ? – Chu Jul 25 '14 at 07:07
-
2@Satchit, C prior of C11 didn't have even a model of threads, so the type `sig_atomic_t` of the C standard made no assumption at all if this could be suited for multithreading. And yes, generally it is not suited for inter-thread communication, don't use it for that purpose. `volatile` has not much to do with thread safeness, either. As I said, C11 that introduces threads also introduces proper atomic datatypes. – Jens Gustedt Jul 25 '14 at 11:12
-
3*`sig_atomic_t` is not an atomic data type*. The definition of `sig_atomic_t` is: "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." The standard clearly states "`sig_atomic_t` ... is ... an object that can be accessed as an atomic entity." There are still visibility issues in a multithread context (and `volatile` won't help with those), but the access itself seems to be required to be atomic. – Andrew Henle Oct 26 '17 at 09:07
-
4@AndrewHenle, actually the term atomic is not quite well defined in that context. It can mean different things, here it just means "indivisible". This definition is different from the definition of atomic that was added later to the standard in C11. – Jens Gustedt Oct 26 '17 at 10:08
-
Nit: "`sig_atomic_t` is not an atomic data type" -- it is atomic, but atomic relative to signal delivery, not racing threads. You could start a read or write to a `sig_atomic_t`, then an async signal can get delivered -- the guarantee is that the read or write will start and complete entirely either before or after the signal handler runs. – user2259432 Jan 09 '23 at 22:53
Note that sig_atomic_t
is not thread-safe, only async-signal safe.
Atomics involve two types of barriers:
- Compiler barrier. It makes sure that the compiler does not reorder reads/writes from/to an atomic variable relative to reads and writes to other variables. This is what
volatile
keyword does. - CPU barrier and visibility. It makes sure that the CPU does not reorder reads and writes. On x86 all loads and stores to aligned 1,2,4,8-byte storage are atomic. Visibility makes sure that stores become visible to other threads. Again, on Intel CPUs, stores are visible immediately to other threads due to cache coherence and memory coherence protocol MESI. But that may change in the future. See §8.1 LOCKED ATOMIC OPERATIONS in Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3A for more details.
For comprehensive treatment of the subject watch atomic Weapons: The C++ Memory Model and Modern Hardware.

- 131,725
- 17
- 180
- 271
-
4Not relevant. Even if `sig_atomic_t` has atomic in its name, it is not an atomic data type. – Jens Gustedt Jul 24 '14 at 10:53
-
-
1@JensGustedt [The C11 standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) defines `sig_atomic_t` as an atomic object at all tmes: "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." – Andrew Henle Oct 26 '17 at 09:01
-
4@MaximEgorushkin You misunderstand the definition. `sig_atomic_t` is atomic *at all times*. The "even in the presence of asynchronous interrupts" part of the definition does not limit the atomicity to asynchronous interrupts, it merely emphasizes the requirement to be atomic includes them. Note that I'm not saying `sig_atomic_t` is thread-safe - that does require the use of barriers. The atomicity does mean, though, that the *entire* value can be accessed without something like a mutex to guarantee completeness. *Visiblity* of changes needed for thread safety still need to be addressed. – Andrew Henle Oct 26 '17 at 12:42
-
-
@MaximEgorushkin Sorry, I've accidentally posted the comment early, and past the 5 mins edit grace period. The thing is, when it comes to the CPU visibility, particularly when TSO (Total Store Ordering) is involved. Can we still ensure that the memory changed by one CPU could be seen by another CPU immediately? Say the corresponding instruction involved has the LOCK prefix. Thanks! – Feng. Ma Dec 14 '21 at 10:05
-
@Feng.Ma Use https://en.cppreference.com/w/c/atomic with a required memory order. – Maxim Egorushkin Dec 14 '21 at 22:13
sig_atomic_t
is often just a typedef
(to some system specific integral type, generally int
or long
). And it is very important to use volatile sig_atomic_t
(not just sig_atomic_t
alone).
When you add the volatile
keyword, the compiler has to avoid a lot of optimizations.
The recent C11 standard added _Atomic
and <stdatomic.h>
. You need a very recent GCC (e.g. 4.9) to have it supported.

- 223,805
- 18
- 296
- 547
-
-
1The IDE does not matter. `_Atomic` & C11 need *compiler* (not IDE) support. And Pelles C does not seem to run on Linux. – Basile Starynkevitch Jul 24 '14 at 11:22
-
As any other http://en.wikipedia.org/wiki/Integrated_development_environment, Pelles includes a compiler: http://www.smorgasbordet.com/pellesc/ – this Jul 24 '14 at 11:24
-
1No IDEs does not *include* but are just *using* and *interfacing* to compilers. The compiler is, from the point of view of the IDE, some external tool. IDE are just glorified editors (for instance Eclipse CDT use `gcc`). You can use `vim` or `emacs`; BTW your link to Pelles says that only Windows (not Linux) is supported and that they use some variant of `lcc` – Basile Starynkevitch Jul 24 '14 at 11:24
-
-
1
-
On Linux, the IDEs and the compilers have different providers. – Basile Starynkevitch Jul 24 '14 at 11:35
Programs using both have same assembler code. How extra care is taken to make the operation atomic?
Although this is an old question, I think it's still worth addressing this part of the question specifically. On Linux, sig_atomic_t
is provided by glibc. sig_atomic_t
in glibc is a typedef for int
and has no special treatment (as of this post). The glibc docs address this:
In practice, you can assume that int is atomic. You can also assume that pointer types are atomic; that is very convenient. Both of these assumptions are true on all of the machines that the GNU C Library supports and on all POSIX systems we know of.
In other words, it just so happens that regular int
already satisfies the requirements of sig_atomic_t
on all the platforms that glibc supports and no special support is needed. Nonetheless, the C and POSIX standards mandate sig_atomic_t
because there could be some exotic machine on which we want to implement C and POSIX for which int
does not fulfill the requirements of sig_atomic_t
.

- 22,455
- 16
- 75
- 126
-
There seem to be different meanins of “atomic” out there in the wild. The C11 atomic types are the thread-safe type of atomic that use memory barriers at runtime. The sig_atomic_t “atomicity” doesn't give the same guarantees. – Pavel Šimerda Aug 14 '21 at 16:38
This data type seems to be atomic.
From here:
24.4.7.2 Atomic Types To avoid uncertainty about interrupting access to a variable, you can use a particular data type for which access is always atomic: sig_atomic_t. Reading and writing this data type is guaranteed to happen in a single instruction, so there’s no way for a handler to run “in the middle” of an access.
The type sig_atomic_t is always an integer data type, but which one it is, and how many bits it contains, may vary from machine to machine.
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 is atomic. You can also assume that pointer types are atomic; that is very convenient. Both of these assumptions are true on all of the machines that the GNU C Library supports and on all POSIX systems we know of.
It pays off to have studied some kernel-development-level memory models...
Anyway, sig_atomic_t is atomic. The normal definition of atomic is that you can't get a "partial" result, e.g. due to concurrent writes, or concurrent read and write. Attaching any other properties to "atomic" is dangerous, and causes the type of confusion seen here.
So, when you do any sort of sig_atomic_t store, you are guaranteed to either get the old value, or the new value when something reads it back -- be it before, during, or after that store.
Answering your direct question about "how that works": the compiler will use an underlying type size and issue extra machine instructions where required, to signal the CPU that it must do an atomic store and atomic read.
All that said, it is important to note that you really can't say much about whether you will get the old or the new value when you try to read an atomic variable like sig_atomic_t. All you know is that you will not get a mix of two different stores that raced each other, nor a mix of the old and the new value while a store is happening concurrently with your read.
In C, you also normally need to declare variables as "volatile sig_atomic_t" because otherwise the compiler has no reason to not cache it, and you could be using an older value for longer than expected: the compiler has no reason to force a fresh memory read if it already has an old value in a register from a previous read. "volatile" tells the compiler to always do a fresh memory read when it needs to get the value of the variable.
Note that neither "volatile" nor "sig_atomic_t" are strong enough "compiler barriers" to ensure it is not reordered around by the compiler optimizer, let alone by the CPU itself (which would require a memory barrier, not just a compiler barrier). If you need any visibility constraints re. other threads, processors, and even hardware when doing MMIO, you need "extra stuff" (compiler barriers, and memory barriers).
And that's where C11 _Atomic and the C11 memory models come into play. They're not about "atomic" reads and stores only, they also include a lot of visibility rules and constraints re. other entities (MMIO devices, other execution threads, other processors).

- 81
- 1
- 3