247 lines
6.9 KiB
Plaintext
247 lines
6.9 KiB
Plaintext
'\"
|
|
'\" Copyright (c) 1995-1996 Sun Microsystems, Inc.
|
|
'\"
|
|
'\" See the file "license.terms" for information on usage and redistribution
|
|
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
'\"
|
|
.TH vwait n 8.0 Tcl "Tcl Built-In Commands"
|
|
.so man.macros
|
|
.BS
|
|
'\" Note: do not modify the .SH NAME line immediately below!
|
|
.SH NAME
|
|
vwait \- Process events until a variable is written
|
|
.SH SYNOPSIS
|
|
\fBvwait\fR \fIvarName\fR
|
|
.BE
|
|
.SH DESCRIPTION
|
|
.PP
|
|
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 the global variable
|
|
\fIvarName\fR. Once \fIvarName\fR has been set, the \fBvwait\fR
|
|
command will return as soon as the event handler that modified
|
|
\fIvarName\fR completes. The \fIvarName\fR argument is always interpreted as
|
|
a variable name with respect to the global namespace, but can refer to any
|
|
namespace's variables if the fully-qualified name is given.
|
|
.PP
|
|
In some cases the \fBvwait\fR command may not return immediately
|
|
after \fIvarName\fR is set. This happens if the event handler
|
|
that sets \fIvarName\fR does not complete immediately. For example,
|
|
if an event handler sets \fIvarName\fR and then itself calls
|
|
\fBvwait\fR to wait for a different variable, then it may not return
|
|
for a long time. During this time the top-level \fBvwait\fR is
|
|
blocked waiting for the event handler to complete, so it cannot
|
|
return either. (See the \fBNESTED VWAITS BY EXAMPLE\fR below.)
|
|
.PP
|
|
To be clear, \fImultiple \fBvwait\fI calls will nest and will not happen in
|
|
parallel\fR. The outermost call to \fBvwait\fR will not return until all the
|
|
inner ones do. It is recommended that code should never nest \fBvwait\fR
|
|
calls (by avoiding putting them in event callbacks) but when that is not
|
|
possible, care should be taken to add interlock variables to the code to
|
|
prevent all reentrant calls to \fBvwait\fR that are not \fIstrictly\fR
|
|
necessary. Be aware that the synchronous modes of operation of some Tcl
|
|
packages (e.g.,\ \fBhttp\fR) use \fBvwait\fR internally; if using the event
|
|
loop, it is best to use the asynchronous callback-based modes of operation of
|
|
those packages where available.
|
|
.SH EXAMPLES
|
|
.PP
|
|
Run the event-loop continually until some event calls \fBexit\fR.
|
|
(You can use any variable not mentioned elsewhere, but the name
|
|
\fIforever\fR reminds you at a glance of the intent.)
|
|
.PP
|
|
.CS
|
|
\fBvwait\fR forever
|
|
.CE
|
|
.PP
|
|
Wait five seconds for a connection to a server socket, otherwise
|
|
close the socket and continue running the script:
|
|
.PP
|
|
.CS
|
|
# Initialise the state
|
|
after 5000 set state timeout
|
|
set server [socket -server accept 12345]
|
|
proc accept {args} {
|
|
global state connectionInfo
|
|
set state accepted
|
|
set connectionInfo $args
|
|
}
|
|
|
|
# Wait for something to happen
|
|
\fBvwait\fR state
|
|
|
|
# Clean up events that could have happened
|
|
close $server
|
|
after cancel set state timeout
|
|
|
|
# Do something based on how the vwait finished...
|
|
switch $state {
|
|
timeout {
|
|
puts "no connection on port 12345"
|
|
}
|
|
accepted {
|
|
puts "connection: $connectionInfo"
|
|
puts [lindex $connectionInfo 0] "Hello there!"
|
|
}
|
|
}
|
|
.CE
|
|
.PP
|
|
A command that will wait for some time delay by waiting for a namespace
|
|
variable to be set. Includes an interlock to prevent nested waits.
|
|
.PP
|
|
.CS
|
|
namespace eval example {
|
|
variable v done
|
|
proc wait {delay} {
|
|
variable v
|
|
if {$v ne "waiting"} {
|
|
set v waiting
|
|
after $delay [namespace code {set v done}]
|
|
\fBvwait\fR [namespace which -variable v]
|
|
}
|
|
return $v
|
|
}
|
|
}
|
|
.CE
|
|
.PP
|
|
When running inside a \fBcoroutine\fR, an alternative to using \fBvwait\fR is
|
|
to \fByield\fR to an outer event loop and to get recommenced when the variable
|
|
is set, or at an idle moment after that.
|
|
.PP
|
|
.CS
|
|
coroutine task apply {{} {
|
|
# simulate [after 1000]
|
|
after 1000 [info coroutine]
|
|
yield
|
|
|
|
# schedule the setting of a global variable, as normal
|
|
after 2000 {set var 1}
|
|
|
|
# simulate [\fBvwait\fR var]
|
|
proc updatedVar {task args} {
|
|
after idle $task
|
|
trace remove variable ::var write "updatedVar $task"
|
|
}
|
|
trace add variable ::var write "updatedVar [info coroutine]"
|
|
yield
|
|
}}
|
|
.CE
|
|
.SS "NESTED VWAITS BY EXAMPLE"
|
|
.PP
|
|
This example demonstrates what can happen when the \fBvwait\fR command is
|
|
nested. The script will never finish because the waiting for the \fIa\fR
|
|
variable never finishes; that \fBvwait\fR command is still waiting for a
|
|
script scheduled with \fBafter\fR to complete, which just happens to be
|
|
running an inner \fBvwait\fR (for \fIb\fR) even though the event that the
|
|
outer \fBvwait\fR was waiting for (the setting of \fIa\fR) has occurred.
|
|
.PP
|
|
.CS
|
|
after 500 {
|
|
puts "waiting for b"
|
|
\fBvwait\fR b
|
|
puts "b was set"
|
|
}
|
|
after 1000 {
|
|
puts "setting a"
|
|
set a 10
|
|
}
|
|
puts "waiting for a"
|
|
\fBvwait\fR a
|
|
puts "a was set"
|
|
puts "setting b"
|
|
set b 42
|
|
.CE
|
|
.PP
|
|
If you run the above code, you get this output:
|
|
.PP
|
|
.CS
|
|
waiting for a
|
|
waiting for b
|
|
setting a
|
|
.CE
|
|
.PP
|
|
The script will never print
|
|
.QW "a was set"
|
|
until after it has printed
|
|
.QW "b was set"
|
|
because of the nesting of \fBvwait\fR commands, and yet \fIb\fR will not be
|
|
set until after the outer \fBvwait\fR returns, so the script has deadlocked.
|
|
The only ways to avoid this are to either structure the overall program in
|
|
continuation-passing style or to use \fBcoroutine\fR to make the continuations
|
|
implicit. The first of these options would be written as:
|
|
.PP
|
|
.CS
|
|
after 500 {
|
|
puts "waiting for b"
|
|
trace add variable b write {apply {args {
|
|
global a b
|
|
trace remove variable ::b write \e
|
|
[lrange [info level 0] 0 1]
|
|
puts "b was set"
|
|
set ::done ok
|
|
}}}
|
|
}
|
|
after 1000 {
|
|
puts "setting a"
|
|
set a 10
|
|
}
|
|
puts "waiting for a"
|
|
trace add variable a write {apply {args {
|
|
global a b
|
|
trace remove variable a write [lrange [info level 0] 0 1]
|
|
puts "a was set"
|
|
puts "setting b"
|
|
set b 42
|
|
}}}
|
|
\fBvwait\fR done
|
|
.CE
|
|
.PP
|
|
The second option, with \fBcoroutine\fR and some helper procedures, is done
|
|
like this:
|
|
.PP
|
|
.CS
|
|
# A coroutine-based wait-for-variable command
|
|
proc waitvar globalVar {
|
|
trace add variable ::$globalVar write \e
|
|
[list apply {{v c args} {
|
|
trace remove variable $v write \e
|
|
[lrange [info level 0] 0 3]
|
|
after 0 $c
|
|
}} ::$globalVar [info coroutine]]
|
|
yield
|
|
}
|
|
# A coroutine-based wait-for-some-time command
|
|
proc waittime ms {
|
|
after $ms [info coroutine]
|
|
yield
|
|
}
|
|
|
|
coroutine task-1 eval {
|
|
puts "waiting for a"
|
|
waitvar a
|
|
puts "a was set"
|
|
puts "setting b"
|
|
set b 42
|
|
}
|
|
coroutine task-2 eval {
|
|
waittime 500
|
|
puts "waiting for b"
|
|
waitvar b
|
|
puts "b was set"
|
|
set done ok
|
|
}
|
|
coroutine task-3 eval {
|
|
waittime 1000
|
|
puts "setting a"
|
|
set a 10
|
|
}
|
|
\fBvwait\fR done
|
|
.CE
|
|
.SH "SEE ALSO"
|
|
global(n), update(n)
|
|
.SH KEYWORDS
|
|
asynchronous I/O, event, variable, wait
|
|
'\" Local Variables:
|
|
'\" mode: nroff
|
|
'\" fill-column: 78
|
|
'\" End:
|