The Cortex M3 supports a useful pair of operations (common in many other machines as well) called "Load-Exclusive" (LDREX) and "Store-Exclusive" (STREX). Conceptually, the LDREX operation performs a load, also sets some special hardware to observe whether the location that got loaded might be written by something else. Performing a STREX to the address used by the last LDREX will cause that address to be written only if nothing else wrote it first. The STREX instruction will load a register with 0 if the store took place, or 1 if it was aborted.
Note that STREX is often pessimistic. There are a variety of situations where it might decide not to perform the store even if the location in question had not in fact been touched. For example, an interrupt between an LDREX and STREX will cause the STREX to assume the location being watched might have been hit. For this reason, it's usually a good idea to minimize the amount of code between the LDREX and STREX. For example, consider something like the following:
inline void safe_increment(uint32_t *addr)
new_value = __ldrex(addr) + 1;
} while(__strex(new_value, addr));
which compiles to something like:
; Assume R0 holds the address in question; r1 trashed
cmp r1,#0 ; Test if non-zero
.. code continues
The vast majority of the time the code executes, nothing will happen between the LDREX and STREX to "disturb" them, so the STREX will succeed without further ado. If, however, an interrupt happens to occur immediately following the LDREX or ADD instruction, the STREX will not perform the store, but instead the code will go back to read the (possibly updated) value of [r0] and compute a new incremented value based upon that.
Using LDREX/STREX to form operations like safe_increment makes it possible to not only manage critical sections, but also in many cases to avoid the need for them.