** Summary ** Process events until a variable is written : '''vwait''' ''varName'' [http://www.tcl.tk/man/tcl/TclCmd/vwait.htm%|%official man page]: [event-oriented programming]: [update]: '''`vwait`''' enters the Tcl [event loop], and as events are processed, monitors the variable named by ''varName'' for changes, [return%|%returning] only once some event handler modifies ''varName''. Thus, `vwait` effectively blocks execution of the foreground script until a modification of ''varName'' occurs. 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). ====== 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 then the outer `vwait` depends on the inner `vwait`, and may not return for a long time. In general, nested calls to `vwait` should be avoided. 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. ** Avoiding Conflicting [vwait]'s ** ''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 Packages such as [Tk] and [tclsvc] themselves call [vwait] To avoid conflicting [vwait]'s: ====== vwait forever if {![info exists tk_version] && ![info exists tcl_service]} { ... ====== [PS] 2004-03-09: For example, this script, which implements a TCP server in tclsh and wish, will not exit properly when you close its main window: ====== proc accept { channel peer port } { close $channel } socket -server accept 5000 ====== [RS]: ''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). After you close the window, the wish app does not exit, but remains fully active in memory. [DGP]: This discussion is essentially another vote in favor of [http://sf.net/tracker/?func=detail&aid=456548&group_id=12997&atid=362997%|%Tk Feature Request 456548]. The flaw here is in Tk's continued assumptions about being in wish, rather than being an independent package that might be loaded into any Tcl interp. There's nothing wrong with [[vwait]] (at least nothing revealed in this discussion. :) ). ---- [Chris Nelson] said [http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/3ebbe18159a8efac?tvc=2&q=multiple+vwaits%|%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 ====== 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]: ** Timeout for `vwait` ** after 30000 {set a $a} vwait a ====== This way it would wait for ''a'' to be changed or until 30 seconds is up. [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 [DKF]: You can '''[[vwait]] on several variables simultaneously''' as long as array to cause the `vwait` to terminate. Do this by making the `vwait` be array to cause the [[vwait]] to terminate. Do this by making the [[vwait]] be ---- [BBH]: 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 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 kenstir: 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. ====== namespace eval control { namespace export waitForAny variable waitForAnyKey 0 # new "vwait" that takes multiple variables and/or optional timeout # usage: waitForAny ?timeout? variable ?variable ...? proc waitForAny {args} { variable waitForAnyArray variable waitForAnyKey # 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 # (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 foreach var $args { uplevel \#0 [list trace variable $var w $trigger] } # set timer is user requested one if {[info exists timeout]} { set timerId [after $timeout $trigger] } vwait [namespace which -variable waitForAnyArray]($index) # remove all traces foreach var $args { uplevel \#0 [list trace vdelete $var w $trigger] } # cancel timer if [info exists timerId] { after cancel $timerId } # cleanup unset waitForAnyArray($index) } } ====== ---- '''vwait in namespaces:''' The varName must be globally qualified as if in a ====== namespace eval foo { vwait bar ;# will never fire vwait ::foo::bar ;# does the job variable bar ;# These two lines also do the job vwait [namespace which -variable bar] ;# DGP } ;# RS ====== ** Avoiding Nested Calls to `vwait` ** ---- 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 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. ---- 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 { } { vwait ::a puts "::a set" } proc b_vwait {} { proc b_vwait { } { vwait ::b puts "::b set" } proc timer {} { proc timer { } { puts "$::time sec" if {$::time == 35} { exit } else { after 1000 timer } } after 1 a_vwait after 5 b_vwait after 10 timer after 15000 {set ::a 0} after 30000 {set ::b 0} vwait forever ====== Although there are two events set to trigger in 15 sec and 30 sec respectively, the 30 sec vwait blocks the 15 sec vwait, opposite of the intuitive reaction to this program. When you understand this code snippit, you'll be free from the dangers of haphazardly using vwait. ---- [Marty Backe] 2002-08-15 Christian Klugesherz 2009-12-04: To understand what really happens, just replace the above timer procedure, by adding [after info] which shows the existing replace the above timer proc, by adding [after info] which shows the existing ====== proc timer {} { proc timer { } { puts "$::time sec" puts [after info] if {$::time == 35} { exit } else { after 1000 timer #puts [after info] } } ====== ---- Peter Newman 2004-03-09: '''Wish Is Buggy!''' It seems to me that all these problems with `vwait`, `[tkwait]`, `[update]` and ''people not understanding the problems with [vwait], [tkwait], [update] and ''people not understanding the design of wish - in that it automatically appends the event loop onto the scripts it runs. Take the following "Hello World" program:- ====== pack [button .mybutton -text {Hello World!} -command exit] ; pack [button .mybutton -text "Hello World!" -command exit] ; It's buggy! I forget to call the event loop. So it does nothing. But wish will run it Ok. So wish's bug cancels out my bug - and the bug in my program gets overlooked. Almost all programs on this Wiki are like that. As written by the programmer, there's no call to the event loop. Now with "Hello World" programs it probably doesn't matter. But with complicated real world programs the event loop matters a lot. But wish permits and encourages Tcl programmers to ignore the event loop. We only worry about it when the roof caves in! Then we find out we haven't got a clue how it works. Or how to write code that handles the event loop properly. (Then it's thank God for pages like this on the Wiki, as you try and figure it out.) The solution is to get rid of the auto-appending the event loop from wish. And force Tcl programmers to learn and think about the event loop from the first, and with every subsequent, script they write. ---- [schlenk]: Not really, wish is ok, but there are tendencies to get rid of it in favor of tclsh and package require Tk. Tk starts the event loop by default, so there isn't any bug. If you use Tk, you have a running event loop. Why make things more complex by forcing people to require the event loop explicitly if it is clear they need it? ---- behaviour is the right thing, but the whole reason I moved the 'check for tk' remark to the top of the page is that I was bitten by it *again*. Currently, if you want the 'default' Tcl event loop, the recommended/standard way seems to be to call `[vwait forever]`. But there are more and more script out there that to call [[vwait forever]]. But there are more and more script out there that emerge. Is checking for tk and tclsvr enough right now? Probably. Will it be in the future? Probably not. What we really need is a function that I can call to start the event loop, which -by default- will never return. Something like: ====== interp eventloop # or just eventloop # and for the special case which needs it: { ... appInitCode } eventloop -pleaseReturnIfPossible { ... appCleanupCode } ====== Or bastardise `vwait` to do the same, when called with forever as its first Or bastardise vwait to do the same, when called with forever as its first {script}]] to provide the appropriate eventloop handler code. ---- [NEM]: It is worth pointing out here that [tk_messageBox] calls vwait internally (at least on UNIX). This has been a cause of several bugs in event driven code I have written. All the advice given on this page about not calling vwait should also apply to tk_messageBox. In particular, don't do something like the following: ====== fileevent $fd readable [list readdata $fd] proc readdata fd { proc readdata {fd} { set line [gets $fd] # Some other stuff... tk_messageBox ... # Some more stuff... } ====== The problem is that the nested `vwait` means that `readdata` will The problem is that the internal vwait means that your readdata proc will around this was to implement my own dialog box code, which took a callback: ====== messagebox ... -command [list dosomething ...] ====== But then, you still have to deal with what to do with events that come in while the dialog is still raised. (Do you process them, or should you wait until the dialog box is dismissed?) I never really decided what the ''correct'' behaviour in this situation would be. ---- [CMcC]: In case it matters to you, this quickie will prevent recursive `vwait` while permitting iterative `vwait`: [[[CMcC]]]: In case it matters to you, this quickie will prevent recursive [[vwait]] while permitting iterative [[vwait]] - [[[CMcC]]] ====== rename ::vwait ::vwait_org proc ::vwait varname { proc ::vwait {var} { set result [uplevel 1 [list vwait_org $varname]] set result [uplevel 1 vwait_org $var] return $result } ====== Ken: I have one a problem with the follow code, i intended to transfer control to halt the procession of one function and go to other function which would call it again? Below is the code ====== set Flag 0 if {$Flag} { continue } else { vwait Flag vwait Flag } but for this code it seems that it would prompt an error code which is "can't wait for variable Flag" if flag stays at 0. So how should i proceed so that it would not prompt this error code.