The read, change and write race
The idiom reg |= mask is three operations: a read, a change and a write.
An interrupt between the read and the write loses whatever it wrote.
A write-one-to-set register removes the read, so it loses nothing.
Try the read and write race in the bit editor
Bit 2 cleared, and the other seven bits kept. Edit the expression or select a bit to flip it.
7
6
5
4
3
2
1
0
0xB2
hex · 8 bits
- binary
- 0b10110010
- value
- 178
Shift
The read and write race step by step
Follow a lost change through the three operations:
- The main loop reads the register, which holds 0x01.
- An interrupt fires and sets bit 3, so the register holds 0x09.
- The main loop changes its own copy, 0x01, to 0x03.
- The main loop writes 0x03, and bit 3 is gone.
Ways to change a register, and what each costs
| Approach | Reads first | Safe against an interrupt |
|---|---|---|
| reg |= mask | Yes | No |
| reg &= ~mask | Yes | No |
| W1TS register | No | Yes |
| W1TC register | No | Yes |
| Bit-band alias | No | Yes on Cortex-M3 and M4 |
| Disable interrupts around it | Yes | Yes, at the cost of latency |
Where the read and write race is used
An ESP32 and many ARM parts hold a pair of set and clear registers for this reason.
A Cortex-M3 maps each bit of some regions to its own address, which the name bit-band covers.
A driver that runs in an interrupt and in the main loop needs one of these approaches.
The read and write race: points to note
- The problem is rare and depends on timing, so it survives every test and appears in the field.
- A volatile pointer keeps the read in the code. It does not make the sequence atomic.
- A write of 0 to a W1TS register does nothing at all. Use the W1TC register of the pair to clear.
- Disabled interrupts around a register write add latency, so keep the window short.
The full bit editor
The full editor holds many values, evaluates expressions across them, and reads live values from a serial port.
Open this value in the full editorQuestions about the read and write race
- Why did my interrupt lose a pin change?
- The main loop read the register before the interrupt and wrote its old copy back afterwards.
- Does volatile fix the race?
- No. It keeps the read in the code. The read, the change and the write are still three steps.
- What is a W1TS register?
- A register where a 1 sets that bit and a 0 changes nothing. It needs no read, so it cannot lose one.