vwait - Process events until a variable is written http://www.purl.org/tcl/home/man/tcl8.4/TclCmd/vwait.htm '''vwait''' ''varName'' This command enters the Tcl event loop to process events, blocking the application if no events are ready. It continues processing events until some event handler sets the value of variable ''varName''. Once ''varName'' has been set, the '''vwait''' command will return as soon as the event handler that modified ''varName'' completes. ''VarName'' must globally scoped (either with a call to '''[global]''' for the ''varName'', or with the full namespace path specification). [http://www.tcl.tk/man/tcl/TclCmd/vwait.htm%|%official man page]: In some cases the '''vwait''' command may not return immediately after ''varName'' is set. This can happen if the event handler that sets ''varName'' does not complete immediately. For example, if an event handler sets ''varName'' and then itself calls '''vwait''' to wait for a different variable, then it may not return for a long time. During this time the top-level '''vwait''' is blocked waiting for the event handler to complete, so it cannot return either. ---- [Chris Nelson] said these golden words on [the comp.lang.tcl newsgroup]: ''Multiple vwaits nest, they do not happen in parallel. The outermost vwait cannot complete until all others return.'' ---- '''vwait forever''': ''Wish'' has a built-in event loop. Tclsh has one too but enters that only on demand, for which the [idiom] is to write at the end of code vwait forever (''forever'' being the name of a variable that is presumably never used, but you can set ''forever'' to any value to terminate such a Tcl script). ''RS'' See the note below about namespaces - missing this note results in problems that occur quite commonly! (See the note below about namespaces - missing this note results in problems that occur quite commonly!) ---- '''Timeout for vwait''': Wai Shun Au wrote in [the comp.lang.tcl newsgroup]: after 30000 {set a $a} vwait a [Jeffrey Hobbs] commented: You found the standard way, but you have to go a [Jeffrey Hobbs] commented: You found the standard way, but you have to go a bit further to avoid weird bugs. Cache the [after] id and make sure to cancel it following the ''vwait'' (no [catch] needed - if the after id no longer exists, because it was triggered, ''after cancel'' doesn't care). That way you won't get ''a'' being reset no matter what in 30 secs. ---- [DKF]: You can '''`vwait` on several variables simultaneously''' as long as You can '''[[vwait]] on several variables simultaneously''' as long as all those variables are in the same array, and you are happy for any set of the array to cause the [[vwait]] to terminate. Do this by making the [[vwait]] be on the overall array, and not any element of it. ''[DKF]'' ---- [BBH]: Actually, you can combine the multiple variable & timeout nicely Actually, you can combine the multiple variable & timeout nicely without the variables having to be related, and the timer won't affect the actual variables, Demonstrated by this code taken from dissussion on [the comp.lang.tcl newsgroup] (most of the original work by [Donald Porter], that I tweaked a little to add timeout option). ''[BBH]'' kenstir: I have further tweaked this version of waitForAny that returns the I have further tweaked this version of waitForAny that returns the var (or vars) that got set during the vwait. This allows you to build a robust asynchronous queue. I'm submitting it as a patch to tcllib.sourceforge.net along with tests. ''kenstir'' namespace eval control { variable waitForAnyKey 0 # new "vwait" that takes multiple variables and/or optional timeout # usage: waitForAny ?timeout? variable ?variable ...? proc waitForAny {args} { variable waitForAnyArray variable waitForAnyArray variable waitForAnyKey # if first arg is a number, then that is max wait time # if first arg is a number, then that is max wait time if {[string is int [lindex $args 0]]} { set timeout [lindex $args 0] set args [lrange $args 1 end] } # create trigger script that will cause vwait to fall thru # create trigger script that will cause vwait to fall thru # (trailing comment is to eat appended args in trace command) set index "Key[incr waitForAnyKey]" set trigger "[namespace code [list set waitForAnyArray($index) 1]] ;#" # create trace to trip trigger # create trace to trip trigger foreach var $args { uplevel \#0 [list trace variable $var w $trigger] } # set timer is user requested one # set timer is user requested one if {[info exists timeout]} { set timerId [after $timeout $trigger] } vwait [namespace which -variable waitForAnyArray]($index) # remove all traces # remove all traces foreach var $args { uplevel \#0 [list trace vdelete $var w $trigger] } # cancel timer # cancel timer if [info exists timerId] { after cancel $timerId } # cleanup unset waitForAnyArray($index) } } ---- '''vwait in namespaces:''' It is not documented, but the varName must be globally qualified as if in a binding, even if the vwait is inside a ''namespace eval'': namespace eval foo { vwait bar ;# will never fire vwait ::foo::bar ;# does the job variable bar ;# These two lines also do the job variable bar ;# These two lines also do the job vwait [namespace which -variable bar] ;# DGP } ;# RS '''[DGP]''' -- The fully qualified name requirement is documented in Tcl 8.3.3. Upgrade! ---- A majority of the coding questions received in [comp.lang.tcl] A majority of the coding questions received in [the comp.lang.tcl newsgroup] about vwait appear to result from deep misunderstandings of the command (as opposed to mere syntactic confusion, for example). [Bruce Hartweg] has rightly advised that its proper use is restricted: "IMHO vwaits shouldn't be used too much (the nesting issue creates unexpected results) because you are trying to force a synchronous approach in an event world - It is much better to keep everything event driven. Occasionally for simple things (like dialogs) to use a vwait to avoid having to break something the has a couple of file picks and/or confirmations into umpteen parts has its place." [KBK] agrees that the [Tcl event loop] is widely misunderstood and discusses related issues in [Update considered harmful] and the pages to which it links. in an event world, it is much better to keep everything event driven. Packages such as [Tk] and [tclsvc] themselves call `vwait`. To avoid nested `vwait` calls: This little program demonstrates what [Chris Nelson] stated above (vwaits nest): set ::time 0 set ::a 0 set ::b 0 proc a_vwait {} { proc a_vwait { } { puts "Waiting 15 sec for ::a" vwait ::a puts "::a set" } proc b_vwait {} { proc b_vwait { } { puts "Waiting 30 sec for ::b" vwait ::b puts "::b set" } proc timer {} { proc timer { } { incr ::time 1 puts "$::time sec" if {$::time == 35} { exit } else { after 1000 timer } } after 1 a_vwait after 1 a_vwait after 5 b_vwait after 10 timer after 15000 {set ::a 0} after 15000 {set ::a 0} after 30000 {set ::b 0} vwait forever vwait forever When you understand this code snippit, you'll be free from the dangers of haphazardly using vwait. ---- [Marty Backe] 15 August 2002 Peter Newman 2004-03-09: '''Wish Is Buggy!''' It seems to me that all these ---- ====== ''[Jeff Hobbs] spoke in the [Tcl chatroom]:'' vwait should always be protected by if {![info exists tk_version] && ![info exists tcl_service]} { ... Why? [CL] says it's to avoid the lockup of duelling vwaits--'cause [Tk] and [tclsvc] launch their own. [schlenk]: Not really, wish is ok, but there are tendencies to get rid of it [Tcl syntax help] - [Arts and Crafts of Tcl-Tk Programming] - [Category Command]