Ok, so the consensus is that my original code is not thread safe, neither in practice nor in theory.
Which of these would you use to correct my code? I know how to use a mutex lock to protect the variable, but is there a less expensive way, using "volatile" and/or atomic operations? Which atomic operation would I even use for this - I've found the definitions in OSAtomic.h, but I can't find any decent documentation.
Can anyone recommend a source that will explain all these things?? Apple's Multithreading Guide is pretty brief.
First, your example was a bit unusual; it just cannot work right in a multithreaded environment. Lets say you start two threads, one will try to set a variable x to 1 at exactly midnight tonight, and the other will try to set the same variable x to 2 at exactly the same time. In some cases (for example if x is 64 bit on a 32 bit system) you might end up with a mixture of both numbers, but even in the best case one thread will change x before the other, so you don't know whether x will be 1 or 2 one second after midnight.
Things like "atomic add" are quite useful. Lets say you want to increase x by one. Your processor reads x, adds 1 to the number, writes the result back to x. Say x = 100, and two threads try to do this at exactly the same time. If you are lucky, one thread finishes before the other starts, so x is correctly increased twice. If you are unlucky, the first thread reads x = 100. The second thread reads x = 100. The first thread calculates the new value 101, so does the second thread, then both write 101 to x. x is increased only once. An atomic add (I think it is OSAtomicAdd32) will increase x, and will not let anyone else interfere with it. So if both threads call OSAtomicAdd32 to increase x, it is guaranteed that x is increased twice.
One operation that is often used is "change x from a to b". You could for example read x, which might be 100, then call a function "change x from 100 to 101". There are two possibilities: If x was still 100 when you called that function, it will get changed to 101. If someone else changed it between reading 100 and calling the change function, it will not be changed again, and the function reports an error. In that case you just try again: Read x again (which might be 99 or 101 or something else at this point), add 1, try to change it again, until you succeed.
On a single processor system, things can go wrong, but they usually go wrong less often (which means any bugs just so much harder to find). Lets say you still have two threads running, but they cannot run simultaneously. First thread reads x = 100 and wants to change it to 101. JUST BEFORE it writes x back, the operating system could switch to another thread. And that thread could be running for many milliseconds, and could change x thousands of times! Then when the OS switches back to the first thread, that thread writes the number 101 - which is likely completely wrong!
About VOLATILE: The volatile keyword in C tells the compiler that a value can change unexpectedly (for example because it is modified by another thread), or that something could read the value (for example another thread reading it). If you change x, and x is volatile, then the compiler will use an instruction to change x _exactly_ where it should according to your code. And when you read x, the compiler will read x from memory _exactly_ where it should according to your code.
As an example: If you have int x; int y; y = x; then obviously x and y have the same value, and your compiler will assume that they are the same. If you then write z = y; the compiler might actually store x into z, because it knows x and y are the same. If another thread changes x or y in between, your code will not notice. If you make x and y volatile, then the compiler _must_ store the value of x into y when you write y = x; and not any later, and it _must_ read y again when you write z = y; . So volatile is essential to make multithreaded code work (and it makes code slower that doesn't need it).
(Hope this doesn't come up twice, MacRumors forgot my login while I was typing).