Loop With Timeout¶

Many low level routines require to wait for a specific condition come true: A transmission is finished, a flag is set etc. Most of the time these action do work fine. But sometimes, the check loop does not terminate for some (usually stupid) reason and the program essentially crashed.

\ wait for twi finish
: twi.wait ( -- )
begin
TWCR c@ 80 and
until
;


To circumvent such unwanted endless loops, a timeout is often a solution. This ensures that the loop will be left, regardless what happens. This recipe is based upon the timer module from the lib/hardware directory, that provides a millisecond tick that can be used for timeouts as well.

A timeout loop is basically a modified begin that takes a runtime parameter: the maximum allowed time for a particular loop. The loop terminater (again, until, etc) is left unchanged. If the loop terminates properly, the timeout is ignored, otherwise an exception is thrown. It is up to the programmer to catch that exception. If it is not catched, the forth interpreter will do it and returns to the command prompt.

\ timeout-begin is a potentially endless loop
\ that terminates after a predefined timeout

\ in the case of a timeout an exception is thrown
variable alarmtime
: (init-alarm)
@tick + alarmtime !
;

: (check-alarm)
alarmtime @ expired? if -512 throw then
;

: timeout-begin
postpone (init-alarm)
postpone begin
postpone (check-alarm)
; immediate


Since the alarm checks are simple, some precautions should be obeyed:

• The timer gives a millisecond resolution.

• The longest timeout period is 65.535 seconds (slightly more than a minute).

• The timeout-loop cannot be nested. If you want to use it in a multitasking

environment, change the variable to a user.

• Don’t forget to initialize and start the timer.

\ testcase. timeout after 100ms
: foo
100 timeout-begin
noop
again
;