El Cortex M3 admite un par útil de operaciones de operaciones (también comunes en muchas otras máquinas) llamadas "Load-Exclusive" (LDREX) y "Store-Exclusive" (STREX). Conceptualmente, la operación LDREX realiza una carga, también establece algún hardware especial para observar si la ubicación que se cargó podría estar escrita por otra cosa. La ejecución de un STREX a la dirección utilizada por el último LDREX hará que esa dirección se escriba solo si nada más lo escribió primero . La instrucción STREX cargará un registro con 0 si la tienda tuvo lugar, o 1 si fue abortada.
Tenga en cuenta que STREX es a menudo pesimista. Hay una variedad de situaciones en las que puede decidir no realizar la tienda, incluso si la ubicación en cuestión no hubiera sido tocada. Por ejemplo, una interrupción entre un LDREX y STREX hará que STREX suponga que la ubicación que se está observando podría haber sido afectada. Por esta razón, generalmente es una buena idea minimizar la cantidad de código entre LDREX y STREX. Por ejemplo, considere algo como lo siguiente:
inline void safe_increment(uint32_t *addr)
{
uint32_t new_value;
do
{
new_value = __ldrex(addr) + 1;
} while(__strex(new_value, addr));
}
que compila a algo como:
; Assume R0 holds the address in question; r1 trashed
lp:
ldrex r1,[r0]
add r1,r1,#1
strex r1,r1,[r0]
cmp r1,#0 ; Test if non-zero
bne lp
.. code continues
La gran mayoría de las veces que se ejecuta el código, no ocurrirá nada entre el LDREX y el STREX para "molestarlos", por lo que el STREX tendrá éxito sin más dilación. Sin embargo, si ocurre una interrupción inmediatamente después de la instrucción LDREX o ADD, el STREX no realizará el almacenamiento, sino que el código volverá a leer el valor (posiblemente actualizado) de [r0] y calculará un nuevo valor incrementado basado en eso.
El uso de LDREX / STREX para realizar operaciones como safe_increment hace posible no solo administrar las secciones críticas, sino también en muchos casos para evitar la necesidad de ellas.