9

According to Are C++ Reads and Writes of an int Atomic?, due to issues of processor caching, reads of ints (and thusly pointers--or so I assume) are not atomic in C. So, my question is is there some assembly that I could use to make the read atomic, or do I need to use a lock? I looked at several sets of libraries of atomic operations, and, as of yet, I am unable to find a function for an atomic read.

EDIT: Compiler: Clang 2.9 EDIT: Platform: x86 (64-bit)

Thanks.

Community
  • 1
  • 1
Maz
  • 3,375
  • 1
  • 22
  • 27
  • If GCC, there [are some features](http://www.alexonlinux.com/multithreaded-simple-data-type-access-and-atomic-variables) you could use. I'm not making that an answer, as I'd basically be regurgitating someone else's blog post. – Tim Post Jul 13 '11 at 00:33
  • I updated your tags based on your last edit. – Tim Post Jul 13 '11 at 00:38
  • Take care about the terminology. Reads and writes of an 32 bit structure on correctly aligned memory _are_ atomic on a 32+ bits platform; that is, the value cannot change halfway through the read or write. (Most compilers align memory correctly to make this true.) What Clang does not ensure is that the value you get is consistent with the "latest" value, nor that the read and write operations happen in exact order shown in your code. – zneak Jul 13 '11 at 00:40
  • also what platform? ARM? – Spudd86 Jul 13 '11 at 00:48
  • Is it really possible to create a situation where it actually shows a problem? What would that look like? – Captain Giraffe Jul 13 '11 at 00:56
  • @Captain Giraffe you just keep seeing the same value all the time on one thread (at least until a context switch or something causes a cache flush) IIRC ARM is like this, even if you use atomic writes they are not coherent with normal reads – Spudd86 Jul 13 '11 at 01:02
  • @Spudd86 So it's not an atomicity issue its a caching/multicore issue? – Captain Giraffe Jul 13 '11 at 01:07
  • @Captain Giraffe that's how I read the question anyway. – Spudd86 Jul 13 '11 at 01:22

2 Answers2

6

In general, a simple atomic fetch isn't provided by atomic operations libraries because it's rarely used; you read the value and then do something with it, and the lock needs to be held during that something so that you know that the value you read hasn't changed. So instead of an atomic read, there is an atomic test-and-set of some kind (e.g. gcc's __sync_fetch_and_add()) which performs the lock, then you perform normal unsynchronized reads while you hold the lock.

The exception is device drivers where you may have to actually lock the system bus to get atomicity with respect to other devices on the bus, or when implementing the locking primitives for atomic operations libraries; these are inherently machine-specific, and you'll have to delve into assembly language. On x86 processors, there are various atomic instructions, plus a lock prefix that can be applied to most operations that access memory and hold a bus lock for the duration of the operation; other platforms (SPARC, MIPS, etc.) have similar mechanisms, but often the fine details differ. You will have to know the CPU you're programming for and quite probably have to know something about the machine's bus architecture in this case. And libraries for this rarely make sense, because you can't hold bus or memory locks across function entry/exit, and even with a macro library one has to be careful because of the implication that one could intersperse normal operations between macro calls when in fact that could break locking. It's almost always better to just code the entire critical section in assembly language.

geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • the problem is that on some systems you cannot get correct results unless you do something special on the read as well as the write, ie if you're doing a short calculation then you do a CAS so your changes only happen if the value did not change while you were working. – Spudd86 Jul 13 '11 at 00:45
  • @Spudd86: I would class those in the second group, not the first. – geekosaur Jul 13 '11 at 00:53
  • @geeksaur I'm pretty sure that's what the question was about though – Spudd86 Jul 13 '11 at 00:58
  • @Spudd86: and that's why I included a discussion of them, which is longer than the other. – geekosaur Jul 13 '11 at 01:00
  • @Spudd86: slightly less snippily: there were two questions there, about the low level/asm stuff and about why libraries don't have atomic reads, so I addressed both. – geekosaur Jul 13 '11 at 01:08
5

gcc has a set of atomic builtin functions, but it does not have a plain atomic fetch, however you could do something like __sync_fetch_and_add(&<your variable here>, 0); to work around that

GCC docs are here and there's that blog post above

EDIT: Ah, clang, I know LLVM IR has atomics in it, but I don't know if clang exposes them in any way, but it might be worth a shot to see if it complains about using the gcc ones, it might support them. EDIT: hmm it seems to have something... clang docs doesn't do as much as gcc though, and the docs seem to suggest it may also do the gcc ones.

Spudd86
  • 2,986
  • 22
  • 20
  • Worth noting that the compiler does automatic type conversion when using these. +1, but alas .. it's Clang :) – Tim Post Jul 13 '11 at 00:36
  • "Clang supports a number of builtin library functions with the same syntax as GCC, including things like ..., `__sync_fetch_and_add`, etc." http://clang.llvm.org/docs/LanguageExtensions.html#builtins – mbauman Jul 13 '11 at 01:01