[CGM] Here are quick-and-dirty versions of coroutine-enabled after, gets and vwait, with a simple test program. This uses the new coroutine facilities in tcl8.6a2. When run: 3 coroutines are started using proc counter which increment and print local counters at different speeds, using co_after for their delays. 1 coroutine is started using proc count_input which reads lines of input using co_gets, echoes them preceeded by line count, then if the input is integer it is assigned to variable ::trigger1 otherwise it's assigned to variable trigger2. 2 coroutines are started (procs waiter1, waiter2) which use co_vwait to wait in parallel for updates to variables ::trigger1 and ::trigger2 then add/append these to a total and report them. ---- # Helpers lifted from wiki: namespace path ::tcl::unsupported proc spawn cmd { set k [gensym] coroutine $k {*}$cmd } proc gensym {{prefix "::coroutine"}} { variable gensymid return $prefix[incr gensymid] } ######################################################################### # co_after delay # - calling coroutine pauses for (at least) delay milliseconds and then # continues, while allowing other events to be processed in the meantime. proc co_after ms { after $ms [infoCoroutine] yield } proc counter {id ms} { set count 0 while 1 { puts "$id [incr count]" co_after $ms } } spawn {counter A 2000} spawn {counter {B } 3000} spawn {counter {C } 4000} ######################################################################### # co_gets channelId # - calling coroutine reads a line from specified channel. If no data is # available the coroutine waits for data, while allowing other events to # be processed. proc co_gets chan { fileevent $chan readable [infoCoroutine] yield fileevent $chan readable {} gets $chan } proc count_input {} { set count 0 while 1 { set input [co_gets stdin] puts "INPUT LINE [incr count]: $input" if {[string is integer -strict $input]} { #incr total $input #puts "TOTAL: $total" set ::trigger1 $input } else { set ::trigger2 $input } } } spawn count_input ######################################################################### # co_vwait variable # - calling coroutine waits until the named variable is set and then # continues. Unlike standard vwait, these calls do not nest, ie. # multiple co::vwaits wait in parallel and can be activated in any order. proc co_vwait_callback {coro args} {$coro} proc co_vwait varname { upvar $varname var set callback "co_vwait_callback [infoCoroutine]" trace add variable var write $callback yield trace remove variable var write $callback } proc waiter1 {} { set total 0 while 1 { co_vwait ::trigger1 puts "WAITER1 TOTAL: [incr total $::trigger1]" } } proc waiter2 {} { set buffer {} while 1 { co_vwait ::trigger2 puts "WAITER2 BUFFER: [append buffer $::trigger2]" } } spawn waiter1 spawn waiter2 ######################################################################### vwait forever ---- [NEM] Very nice! I particularly like ''co_gets'': it makes non-blocking I/O just about as convenient as the blocking version. ---- !!!!!! %| [Category Control Structure] |% !!!!!!