summaryrefslogtreecommitdiffstats
path: root/tcl8.6/doc/vwait.n
diff options
context:
space:
mode:
Diffstat (limited to 'tcl8.6/doc/vwait.n')
-rw-r--r--tcl8.6/doc/vwait.n246
1 files changed, 246 insertions, 0 deletions
diff --git a/tcl8.6/doc/vwait.n b/tcl8.6/doc/vwait.n
new file mode 100644
index 0000000..f64d39c
--- /dev/null
+++ b/tcl8.6/doc/vwait.n
@@ -0,0 +1,246 @@
+'\"
+'\" 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: