Is there a race condition when cancelling the delayed command in this coundown timer?

Viewed 30

I have implemented a countdown timer that has a "Reset" button for cancelling a countdown:

set LAUNCHTIME 5
set message $LAUNCHTIME
set afterId {}

proc countdown {remainingSeconds} {
    global afterId message
# **
    if {$remainingSeconds > 0} {
        set message $remainingSeconds
# **
        set afterId [after 1000 [list countdown [expr {$remainingSeconds - 1}]]]
    } else {
        set message "Takeoff!"
        .start configure -state normal
    }
}

label .message -textvariable message
button .start -text Start -command {
    .start configure -state disabled
    countdown $LAUNCHTIME
}
button .reset -text Reset -command {
    # Reset timer, message, and button.
    after cancel $afterId  ;# <- XXX: Could this cause a race condition?
    set message $LAUNCHTIME
    .start configure -state normal
}

grid .message
grid .start
grid .reset
grid anchor . center

This is what it looks like when the "Start" button is pressed:

GIF of countdown timer

Is there a possible race condition when the "Reset" button is pressed while a countdown is occurring? I am worried about the possibility that the "Reset" button is pressed while the program is executing the parts between the double asterisks (**) in the code above. At that point, there is no delayed command that is scheduled to execute, so the "Reset" does not cancel any delayed command. However, right after, a new delayed command will be scheduled to execute (set afterId [after 1000 ...]), even though the user has intended to reset the countdown.

1 Answers

Tcl runs single threaded (at least within a single interpreter). Events are only handled in the event loop. That means that you don't have to worry that something happens that could change things while a sequential block of code is executed.

If the user clicks the Reset button while the countdown proc runs, the proc continues all the way to the end. At that point the event loop resumes and handles the button-click by executing the code attached to the -command option of the button. That cancels the after event, so countdown will not be called again.

Similarly, if the after event expires while the code attached to the Reset button runs, the code completes, canceling the after event before the code of the after event starts.

Related