The most difficult part of handling a critical section without an OS is not actually creating the mutex, but rather figuring out what should happen if code wants to use a resource which is not presently available. The load-exclusive and conditional-store-exclusive instructions make it fairly easy to create an "swap" function which, given a pointer to an integer, will atomically store a new value but return what the pointed-to integer had contained:
int32_t atomic_swap(int32_t *dest, int32_t new_value)
{
int32_t old_value;
do
{
old_value = __LDREXW(&dest);
} while(__STREXW(new_value,&dest);
return old_value;
}
Given a function like the above, one can easily enter a mutex via something like
if (atomic_swap(&mutex, 1)==0)
{
... do stuff in mutex ... ;
mutex = 0; // Leave mutex
}
else
{
... couldn't get mutex...
}
In the absence of an OS, the main difficulty often lies with the "couldn't get mutex" code. If an interrupt occurs when a mutex-guarded resource is busy, it may be necessary to have the interrupt-handling code set a flag and save some information to indicate what it wanted to do, and then have any main-like code which acquires the mutex check whenever it's going to release the mutex to see whether an interrupt wanted to do something while the mutex was held and, if so, perform the action on behalf of the interrupt.
Although it's possible to avoid problems with interrupts wanting to use mutex-guarded resources by simply disabling interrupts (and indeed, disabling interrupts can eliminate the need for any other kind of mutex), in general it's desirable to avoid disabling interrupts any longer than necessary.
A useful compromise can be to use a flag as described above, but have the main-line code which is going to release the mutex disable interrupts and check the aforementioned flag just before doing so (re-enable interrupts after releasing the mutex). Such an approach doesn't require leaving interrupts disabled very long, but will guard against the possibility that if the main-line code tests the interrupt's flag after releasing the mutex, there's a danger that between the time it sees the flag and the time it acts upon it, it might get preempted by other code which acquires and releases the mutex and and acts upon the interrupt flag; if the main-line code doesn't test the interrupt's flag after releasing the mutex, an interrupt which occurs just before the main-line code releases the mutex might get blocked by the mutex but not noticed by the main-line.
In any case, what's most important will be to have a means by which code that tries to use a mutex-guarded resource when it's unavailable will have a means of repeating its attempt once the resource is released.
Best Answer
The Cortex M3 supports a useful pair of operations 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:
which compiles to something like:
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 sectionsm, but also in many cases to avoid the need for them.