1vwait(n) Tcl Built-In Commands vwait(n)
2
3
4
5______________________________________________________________________________
6
8 vwait - Process events until a variable is written
9
11 vwait varName
12______________________________________________________________________________
13
15 This command enters the Tcl event loop to process events, blocking the
16 application if no events are ready. It continues processing events
17 until some event handler sets the value of the global variable varName.
18 Once varName has been set, the vwait command will return as soon as the
19 event handler that modified varName completes. The varName argument is
20 always interpreted as a variable name with respect to the global names‐
21 pace, but can refer to any namespace's variables if the fully-qualified
22 name is given.
23
24 In some cases the vwait command may not return immediately after var‐
25 Name is set. This happens if the event handler that sets varName does
26 not complete immediately. For example, if an event handler sets var‐
27 Name and then itself calls vwait to wait for a different variable, then
28 it may not return for a long time. During this time the top-level
29 vwait is blocked waiting for the event handler to complete, so it can‐
30 not return either. (See the NESTED VWAITS BY EXAMPLE below.)
31
32 To be clear, multiple vwait calls will nest and will not happen in par‐
33 allel. The outermost call to vwait will not return until all the inner
34 ones do. It is recommended that code should never nest vwait calls (by
35 avoiding putting them in event callbacks) but when that is not possi‐
36 ble, care should be taken to add interlock variables to the code to
37 prevent all reentrant calls to vwait that are not strictly necessary.
38 Be aware that the synchronous modes of operation of some Tcl packages
39 (e.g., http) use vwait internally; if using the event loop, it is best
40 to use the asynchronous callback-based modes of operation of those
41 packages where available.
42
44 Run the event-loop continually until some event calls exit. (You can
45 use any variable not mentioned elsewhere, but the name forever reminds
46 you at a glance of the intent.)
47
48 vwait forever
49
50 Wait five seconds for a connection to a server socket, otherwise close
51 the socket and continue running the script:
52
53 # Initialise the state
54 after 5000 set state timeout
55 set server [socket -server accept 12345]
56 proc accept {args} {
57 global state connectionInfo
58 set state accepted
59 set connectionInfo $args
60 }
61
62 # Wait for something to happen
63 vwait state
64
65 # Clean up events that could have happened
66 close $server
67 after cancel set state timeout
68
69 # Do something based on how the vwait finished...
70 switch $state {
71 timeout {
72 puts "no connection on port 12345"
73 }
74 accepted {
75 puts "connection: $connectionInfo"
76 puts [lindex $connectionInfo 0] "Hello there!"
77 }
78 }
79
80 A command that will wait for some time delay by waiting for a namespace
81 variable to be set. Includes an interlock to prevent nested waits.
82
83 namespace eval example {
84 variable v done
85 proc wait {delay} {
86 variable v
87 if {$v ne "waiting"} {
88 set v waiting
89 after $delay [namespace code {set v done}]
90 vwait [namespace which -variable v]
91 }
92 return $v
93 }
94 }
95
96 When running inside a coroutine, an alternative to using vwait is to
97 yield to an outer event loop and to get recommenced when the variable
98 is set, or at an idle moment after that.
99
100 coroutine task apply {{} {
101 # simulate [after 1000]
102 after 1000 [info coroutine]
103 yield
104
105 # schedule the setting of a global variable, as normal
106 after 2000 {set var 1}
107
108 # simulate [vwait var]
109 proc updatedVar {task args} {
110 after idle $task
111 trace remove variable ::var write "updatedVar $task"
112 }
113 trace add variable ::var write "updatedVar [info coroutine]"
114 yield
115 }}
116
117 NESTED VWAITS BY EXAMPLE
118 This example demonstrates what can happen when the vwait command is
119 nested. The script will never finish because the waiting for the a
120 variable never finishes; that vwait command is still waiting for a
121 script scheduled with after to complete, which just happens to be run‐
122 ning an inner vwait (for b) even though the event that the outer vwait
123 was waiting for (the setting of a) has occurred.
124
125 after 500 {
126 puts "waiting for b"
127 vwait b
128 puts "b was set"
129 }
130 after 1000 {
131 puts "setting a"
132 set a 10
133 }
134 puts "waiting for a"
135 vwait a
136 puts "a was set"
137 puts "setting b"
138 set b 42
139
140 If you run the above code, you get this output:
141
142 waiting for a
143 waiting for b
144 setting a
145
146 The script will never print “a was set” until after it has printed “b
147 was set” because of the nesting of vwait commands, and yet b will not
148 be set until after the outer vwait returns, so the script has dead‐
149 locked. The only ways to avoid this are to either structure the over‐
150 all program in continuation-passing style or to use coroutine to make
151 the continuations implicit. The first of these options would be written
152 as:
153
154 after 500 {
155 puts "waiting for b"
156 trace add variable b write {apply {args {
157 global a b
158 trace remove variable ::b write \
159 [lrange [info level 0] 0 1]
160 puts "b was set"
161 set ::done ok
162 }}}
163 }
164 after 1000 {
165 puts "setting a"
166 set a 10
167 }
168 puts "waiting for a"
169 trace add variable a write {apply {args {
170 global a b
171 trace remove variable a write [lrange [info level 0] 0 1]
172 puts "a was set"
173 puts "setting b"
174 set b 42
175 }}}
176 vwait done
177
178 The second option, with coroutine and some helper procedures, is done
179 like this:
180
181 # A coroutine-based wait-for-variable command
182 proc waitvar globalVar {
183 trace add variable ::$globalVar write \
184 [list apply {{v c args} {
185 trace remove variable $v write \
186 [lrange [info level 0] 0 3]
187 after 0 $c
188 }} ::$globalVar [info coroutine]]
189 yield
190 }
191 # A coroutine-based wait-for-some-time command
192 proc waittime ms {
193 after $ms [info coroutine]
194 yield
195 }
196
197 coroutine task-1 eval {
198 puts "waiting for a"
199 waitvar a
200 puts "a was set"
201 puts "setting b"
202 set b 42
203 }
204 coroutine task-2 eval {
205 waittime 500
206 puts "waiting for b"
207 waitvar b
208 puts "b was set"
209 set done ok
210 }
211 coroutine task-3 eval {
212 waittime 1000
213 puts "setting a"
214 set a 10
215 }
216 vwait done
217
219 global(n), update(n)
220
222 asynchronous I/O, event, variable, wait
223
224
225
226Tcl 8.0 vwait(n)