Fix too many ]s in try
[tcl-tlc-base.git] / scripts / process.itcl
blob82306ea6346675c8c5d31aeb94b9a5860e33ade9
1 # vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4
3 # Handlers invoked:
4 # output(channel, data) - When output arrives from the child, channel is
5 # one of {stdout, stderr}
6 # reaped(result) - When the child dies and has been reaped. result
7 # is the numeric result from the child.
8 # death_by_signal(childpid, sigal_name, msg)
9 # - When a child is killed by a signal. signame
10 # is something like SIGTERM, msg is something
11 # like "software termination signal"
13 class tlc::Process {
14 inherit tlc::Baselog tlc::Signalsource tlc::Handlers
16 constructor {args} {}
17 destructor {}
19 public {
20 variable cmd ""
22 method pids {}
23 method output {}
24 method stdout {}
25 method stderr {}
26 method result {}
27 method waitfor_output {match}
30 private {
31 variable pids {}
32 variable res
33 variable handle
34 variable stderr_handle
35 variable buf {}
36 variable output_matches
37 variable output_vwaits
38 variable dominos
39 variable seq 0
41 method readable {}
42 method stderr_handler {subcmd channelId args}
43 method check_output_vwaits {}
44 method abort_waits {}
49 body tlc::Process::constructor {args} { #<<<
50 package require Tcl 8.5
52 array set output_matches {}
53 array set output_vwaits {}
54 array set dominos {}
56 tlc::Signal ::#auto signals(running) -name "$this running"
57 tlc::Signal ::#auto signals(finished) -name "$this finished"
58 tlc::Domino ::#auto dominos(check_output_vwaits) -name "$this check_output_vwaits"
60 configure {*}$args
62 foreach reqf {cmd} {
63 if {$reqf == ""} {
64 error "Must specify -$reqf"
68 set stderr_handle [chan create write [code $this stderr_handler]]
70 #set cmdline [list {*}$cmd 2>@ $stderr_handle]
71 #set cmdline [list {*}$cmd >@ $stderr_handle]
72 #set cmdline [list {*}$cmd 2>@ stdout]
73 set cmdline [list {*}$cmd 2>@1]
74 #set cmdline [list {*}$cmd]
75 #puts $stderr_handle "hello, world"; flush $stderr_handle
76 set handle [open |$cmdline r]
77 chan configure $handle \
78 -buffering none \
79 -blocking 0 \
80 -translation binary \
81 -encoding binary
82 set pids [pid $handle]
83 chan event $handle readable [code $this readable]
84 $signals(running) set_state 1
86 $dominos(check_output_vwaits) attach_output [code $this check_output_vwaits]
89 #>>>
90 body tlc::Process::destructor {} { #<<<
91 tlc::try {
92 $signals(running) set_state 0
93 } onerr {
94 default {STDMSG}
96 if {[info exists handle]} {
97 tlc::try {
98 chan close $handle
99 } onerr {
100 default {STDMSG}
102 unset handle
104 if {[info exists stderr_handle]} {
105 tlc::try {
106 chan close $stderr_handle
107 } onerr {
108 default {STDMSG}
110 unset stderr_handle
113 if {$::tcl_platform(platform) == "unix"} {
114 foreach pid $pids {
115 catch {exec kill -15 $pid}
119 abort_waits
122 #>>>
123 body tlc::Process::readable {} { #<<<
124 set dat [read $handle]
125 if {$dat != ""} {
126 lappend buf [list stdout $dat]
128 if {[chan eof $handle]} {
129 tlc::try {
130 chan configure $handle -blocking 1
131 chan close $handle
132 } onerr {
133 CHILDSTATUS {
134 lassign $::errorCode code childpid res
137 CHILDKILLED {
138 lassign $::errorCode code childpid sig msg
139 invoke_handlers death_by_signal $childpid $sig $msg
140 set res ""
143 default {
144 log error "Child died in an interesting way: $errmsg ($::errorCode)"
145 set res ""
147 } onok {
148 set res 0
150 set pids {}
151 unset handle
153 $dominos(check_output_vwaits) tip
154 invoke_handlers output stdout $dat
156 $signals(running) set_state 0
157 invoke_handlers reaped $res
158 $signals(finished) set_state 1
159 abort_waits
160 return
163 $dominos(check_output_vwaits) tip
165 invoke_handlers output stdout $dat
168 #>>>
169 body tlc::Process::stderr_handler {subcmd channelId args} { #<<<
170 switch -- $subcmd {
171 initialize {
172 lassign $args mode
173 if {$mode != "write"} {
174 error "Only writing is supported"
176 return {
177 initialize
178 finalize
179 watch
181 write
182 blocking
186 finalize {
189 watch {
192 write {
193 lassign $args data
194 lappend buf [list stderr $data]
195 invoke_handlers output stderr $data
197 $dominos(check_output_vwaits) tip
199 return [string length $data]
202 blocking {
203 lassign $args mode
206 default {
207 error "Unsupported subcommand: ($subcmd)"
212 #>>>
213 body tlc::Process::output {} { #<<<
214 set build ""
215 foreach chunk $buf {
216 lassign $chunk channel data
217 append build $data
219 return $build
222 #>>>
223 body tlc::Process::stdout {} { #<<<
224 set build ""
225 foreach chunk $buf {
226 lassign $chunk channel data
227 if {$channel != "stdout"} continue
228 append build $data
230 return $build
233 #>>>
234 body tlc::Process::stderr {} { #<<<
235 set build ""
236 foreach chunk $buf {
237 lassign $chunk channel data
238 if {$channel != "stderr"} continue
239 append build $data
241 return $build
244 #>>>
245 body tlc::Process::result {} { #<<<
246 if {![$signals(finished) state]} {
247 error "Child yet lives"
249 return $res
252 #>>>
253 body tlc::Process::waitfor_output {match} { #<<<
254 if {[string match "*$match*" $buf]} {
255 return
257 set myseq [incr seq]
258 set output_matches($myseq) $match
259 set output_vwaits($myseq) ""
260 vwait [scope output_vwaits($myseq)]
261 set result $output_vwaits($myseq)
262 array unset output_vwaits $myseq
264 switch -- [lindex $result 0] {
265 ok {return}
266 error {error [lindex $result 1] "" [lindex $result 2]}
270 #>>>
271 body tlc::Process::check_output_vwaits {} { #<<<
272 set plain [output]
273 foreach {matchseq match} [array get output_matches] {
274 if {[string match "*$match*" $plain]} {
275 array unset output_matches $matchseq
276 set output_vwaits($matchseq) [list ok]
281 #>>>
282 body tlc::Process::abort_waits {} { #<<<
283 $dominos(check_output_vwaits) force_if_pending
284 foreach {matchseq match} [array get output_matches] {
285 array unset output_matches $matchseq
286 set output_vwaits($matchseq) [list error "child died while waiting for \"$match\"" [list child_died $match]]
290 #>>>
291 body tlc::Process::pids {} { #<<<
292 return $pids
295 #>>>