Interrupt Critical Section¶
There are situations where no interrupts should be allowed. These code segments are usually called critical sections.
: bar ." bar" int? . ;
: baz ." baz" int? . ;
: qux ." qux" int? . ;
: foo \ prints bar-1 baz0 qux-1.
bar
critical[
\ nothing will disturb us here
baz
]critical \ now interrupts or other things may happen again
qux ;
If the standard interrupt enabled system setup is used, calling foo
should print bar-1 baz0 qux-1
. baz can call words that use
the critical[] word pair itself.
To temporarily turn off interrupts, the current state has to be stored. Since the critical section could be nested, a global variable is not the best solution. The following code example stores the information on the return stack. This requires some stack shuffling since a colon word is usually not allowed to manipulate the return stack outside of its own scope. This is the reason, why the critical section must be paired within one definition afterwards. Otherwise the return stack will have data that crashes the system.
\ global interrupt enable state as forth flag, AVR8
: int? ( -- f )
SREG c@ SREG_I and 0> \ use the amforth-shell for the constants
;
\ the MSP 430 works similar
\ : int? sr@ 8 and 8 = ; \ sr@ : status register fetch
: critical[ \ ( -- ) R( XT -- f XT )
r> int? >r >r \ keep the current state
-int
;
: ]critical \ ( -- ) R( f XT -- XT )
r> r> if +int then >r \ will crash if not matched
;
A possible modification is to add the PAUSE vector as well and turn off the cooperative multitasker during the critical section.
: critical[ \ ( -- ) R( XT -- n*f XT )
r> int? >r \ save current state of interrupt and multitasker
['] pause defer@ >r >r
-int single \ no interrupts, no task switches
;
: ]critical \ ( -- ) R( n*f XT -- XT )
r>
r> ['] pause defer! \ restore multitasker
r> if +int then \ restore global interrupt flag
>r \ will crash if not matched
;