Patch that implements a forthcoming TIP, entitled
"Script cancellation with Tcl_CancelEval" or something
similar.
Please test. I would like to get some feedback on this
patch so that any necessary changes can be made prior
to writing the formal TIP.
v2
Logged In: YES
user_id=113501
Fixed interp deletion issue, slave interp support, and some
issues with commands that block (after, vwait). Tweaked docs.
Logged In: YES
user_id=113501
NOTE: This patch needs to be reviewed by kennykb and msofer.
Logged In: YES
user_id=113501
Attaching updated (v3) patch that works around the
Tcl_GetMaster (bug #1502071) issue.
The previous (v2) patch demonstrates the issue.
Logged In: YES
user_id=113501
The v4 patch is basically code complete. However, I'm still
working on the documentation and test cases for [cancel].
Quite a lot of fine tuning has been done to both the new
code and the comments. The Tcl_CancelEval function now
accepts a new flag TCL_CANCEL_UNWIND that allows the
evaluation stack for the target interpreter to be completely
unwound in the event of the pathological test cases like:
while {1} { catch { while {1} {incr x} } }
Here are some other test cases that work correctly with the
v4 patch:
# Tcl_CancelEval after delay test
# returns TCL_ERROR "eval cancelled" or "eval unwound"
# based on flags passed to Tcl_CancelEval
after 30000
----------------------------------------------------------
# Tcl_CancelEval vwait test
# returns TCL_ERROR "eval cancelled" or "eval unwound"
# based on flags passed to Tcl_CancelEval
vwait forever
----------------------------------------------------------
# Tcl_CancelEval very tight loop test
# returns TCL_ERROR "eval cancelled" or "eval unwound"
# based on flags passed to Tcl_CancelEval
while {1} {}
----------------------------------------------------------
# Tcl_CancelEval tight loop with body test
# returns TCL_ERROR "eval cancelled" or "eval unwound"
# based on flags passed to Tcl_CancelEval
while {1} {incr x}
----------------------------------------------------------
# Tcl_CancelEval with TCL_CANCEL_UNWIND test.
# This can only be cancelled if the TCL_CANCEL_UNWIND
# flag is used with Tcl_CancelEval. It will return
# TCL_ERROR "eval unwound" in that case.
while {1} { catch { while {1} {incr x} } }
----------------------------------------------------------
# Tcl_CancelEval with TCL_CANCEL_UNWIND slightly more
# pathological unwind test
# returns TCL_ERROR "eval unwound"
while {1} {
catch {
while {1} {
catch {
while {1} {incr x}
}
}
}
}
----------------------------------------------------------
# Tcl_CancelEval slave interp test
# returns TCL_ERROR "eval cancelled" or "eval unwound"
# based on flags passed to Tcl_CancelEval
interp create foo; foo eval { while {1} {incr x} }
----------------------------------------------------------
# Tcl_CancelEval [subst] test
# returns TCL_ERROR "eval cancelled" or "eval unwound"
# based on flags passed to Tcl_CancelEval
subst {[while {1} {incr x}]}
----------------------------------------------------------
# Tcl_CancelEval with TCL_CANCEL_UNWIND pathological slave
# interp test
# returns TCL_ERROR "eval unwound"
interp create foo; foo eval {
while {1} {
catch {
while {1} {
catch {
while {1} {incr x}
}
}
}
}
}
----------------------------------------------------------
# basic [cancel] test
# returns TCL_ERROR "eval cancelled"
cancel
----------------------------------------------------------
# basic [cancel] test with "--"
# returns TCL_ERROR "eval cancelled"
cancel --
----------------------------------------------------------
# basic [cancel -unwind] test
# returns TCL_ERROR "eval unwound"
cancel -unwind --
----------------------------------------------------------
# [cancel] test
# returns TCL_ERROR "eval cancelled"
catch {unset foo i}
while {1} {
incr i; lappend foo $i
# pretend this is some complex
# logic or Tk button command.
if {$i > 3} then {cancel}
}
----------------------------------------------------------
# [cancel -unwind] test
# returns TCL_ERROR "eval unwound"
catch {unset foo i}
while {1} {
catch {
while {1} {
incr i; lappend foo $i
# pretend this is some complex
# logic or Tk button command.
if {$i > 3} then {cancel -unwind}
}
}
}
----------------------------------------------------------
# recursion test
# returns TCL_ERROR "eval cancelled"
proc foo { i } {
if {$i > 10} then {
cancel
} else {
return [foo [expr {$i + 1}]]
}
}; foo 0
Logged In: YES
user_id=113501
Updated (v5) patch supports a custom error message for
script cancellation via Tcl_CancelEval and [cancel]. Also
corrected mutex locking issue with CancelEvalProc.
Here is a quick Tk example of [cancel] usage:
----------------------------------------------------
package require Tk
pack [text .clock] -side top -expand true \ -fill both -padx 10 -pady 10
pack [button .go -padx 10 -pady 10 -text "Go" \ -command [list goButton]] \ -side left -padx 10 -pady 10
pack [button .cancel -padx 10 -pady 10 -text "Cancel" \ -command [list cancelButton "clock stopped."]] \ -side right -padx 10 -pady 10
proc goButton {args} {
while {1} {
catch {.clock delete 1.0 end}
.clock insert end [clock format [clock seconds]]
update
}
}
proc cancelButton {msg} {
cancel -unwind {} $msg
}
Logged In: YES
user_id=113501
Refactored error message handling to be simpler and more
robust (v6).
Logged In: YES
user_id=113501
Refreshed v6 patch against current HEAD (now v7).
Logged In: YES
user_id=113501
A new patch revision is forthcoming...
Logged In: YES
user_id=113501
Latest patch is against HEAD and cleans up a few things.
Logged In: YES
user_id=148712
An almost 1500 loc patch without a spec, man page or tests.
I'm not up to guessing what it is supposed to do, sorry.
When the forthcoming tip does come forth, I'll take a look.
Logged In: YES
user_id=113501
Sorry, I'm still working on the TIP, man pages, and tests.
In the meantime, the code fully works. The basic
functionality provided is the ability to cancel a script
running in an interpreter from any thread in the process via
Tcl_CancelEval or from within the interpreter itself via the
[cancel] command. Some examples are provided below.
Logged In: YES
user_id=113501
New v9 patch. More cleanup and consistency fixes, now
complete with tests. Docs for Tcl_Canceled and
Tcl_CancelEval complete. Still working on [cancel] docs.
TIP has been submitted and should be forthcoming.
Logged In: YES
user_id=113501
New v10 patch, various adjustments, corrected whitespace.
Logged In: YES
user_id=113501
Originator: YES
Updated (v11) patch to latest HEAD.
File Added: script_cancellation_11.zip
Logged In: YES
user_id=113501
Originator: YES
Patch needs to be updated for latest HEAD. Also, the plan is to change the script visible command to [interp cancel] instead of [cancel].
Logged In: YES
user_id=113501
Originator: YES
Patch updated to latest HEAD.
Still need to change [cancel] to [interp cancel] and add docs for it.
File Added: script_cancellation_12.zip
Logged In: YES
user_id=113501
Originator: YES
Patch updated.
Changed [cancel] to [interp cancel] and added appropriate docs.
File Added: script_cancellation_13.zip
Logged In: YES
user_id=113501
Originator: YES
Minor corrections.
File Added: script_cancellation_14.zip
Logged In: YES
user_id=113501
Originator: YES
More minor corrections.
File Added: script_cancellation_15.zip
Logged In: YES
user_id=113501
Originator: YES
Adding preliminary patch for Tk changes.
File Added: tk_cancellation_1.zip