blt::bgexec(3tcl) | BLT Built-In Commands | blt::bgexec(3tcl) |
bgexec - Run programs in the background while handling Tk events. kill - Terminate program or send signal.
blt::bgexec varName ?switches? program
?arg?...
blt::kill processid ?signal?
The kill command terminates a processid or under unix sends a signal.
The bgexec command executes a program pipleline using the Tcl event-loop allowing other events to continue to be serviced. Upon completion it sets the global variable varName with a list of 4 status values: a text token, the process-id, the exit code, and a text message. Bgexec provides capabilities similar to the exec command, but with added support for callbacks, output to variables and termination.
When used with no options, the returned value from bgexec is the output from the program. But when the last arg is an ampersand (&) the program runs detached, and bgexec immediately returns with a list of the process ids created in the command pipeline. Detached processes can be interrupted and terminated simply by setting varName.
The valid switches are as follows:
Invoking bgexec without a trailing ampersand will block and wait for result. However, other Tcl events continue to be serviced. This prevents Tcl from hanging, eg:
pack [text .t] set val [blt::bgexec myStatus du -s]
Note that text widget .t continues to respond to events.
Here is an example that invokes the Unix du program with a -command callback.
proc Done {data status} { puts "Done($status)\n$data" } blt::bgexec myStatus -command Done du -s $dir &
When du has completed, the handler Done is called with data and status. Also, the global variable myStatus is set to contain the program's exit status, eg:
EXITED 26811 0 {child completed normally}
If myStatus is set before du has completed, the process will be killed. Under Unix, this sends a signal (SIGKILL by default). Under Win32, TerminateProcess is called.
Here is another example, this time using the -output option to direct output to a variable.
global myStatus myOutput blt::bgexec myStatus -output myOutput du -s $dir puts "Disk usage for $dir is $myOutput"
Upon completion, MyOutput will contain the output of the program.
Various bgexec options can be used to capture stderr separately from stdout.
global myStatus myOutput myErrs blt::bgexec myStatus -output myOutput -error myErrs du -s $dir
The -error option is similar to -output in that it sets a variable when the program completes with data written to stderr.
By default, bgexec treats variable or command options as being in the global namespace. The -local option can change this to use the current namespace. Thus data can be collected to namespace-local variables even those inside of procs, eg.
proc Work {} {
blt::bgexec myStatus -local 1 -output val -error err du -s
puts "VAL=$val"
puts "ERR=$err" }
which collects data to local variables.
For detached processes, -local will cause data to aggregate to namespace variables, ie. outside the proc, eg.
namespace eval ::Ns {
set pval {}
set perr {}
proc Work {} {
blt::bgexec myStatus -local 1 -output pval -error perr du -s &
} }
This collects data to ::Ns::pval and stderr to ::Ns::perr. Similarly, proc names (eg -onoutput) will be relative to the current namespace.
The -output and -error variables are set only after the program completes. But if a program runs for a long time, you can gather data as it becomes available using the -onoutput option. As new data becomes available, this command is executed, with data appended as an argument.
proc GetInfo { data } { puts $data } blt::bgexec myStatus -onoutput GetInfo du -s $dir
The -onerror option performs a similar function for the stderr data stream.
Like exec, bgexec returns an error if the exit code of the program is non-zero. To handle this invoke bgexec from within a catch.
catch { blt::bgexec myStatus -output myOutput du -s $dir }
Detached jobs will generate an error only if the program startup failed. Otherwise the only indication is the status code set in myStatus.
By default, bgexec waits for a program to finish and returns the resulting output. To detach a program simply append an ampersand (&) as the last argument on the command line, eg.
global myStatus myOutput blt::bgexec myStatus -output myOutput du -s $dir &
Bgexec will then return immediately with the spawned process ids as the result. If needed tkwait can be used to wait for the program to finish:
global myStatus myOutput blt::bgexec myStatus -output myOutput du -s $dir &
... tkwait variable myStatus
Note however that using tkwait can be dangerous. Multiple tkwait/vwait calls must complete in the reverse order called. The BLT busy command can be used to try and enforce this, but a better alternative is to just use -command instead.
Using bgexec without an ampersand will not hang Tcl: events continue to be serviced by the event handler while the call blocks. Also unlike exec, an error will not be generated if output is appears on stderr. And output from stderr can be separately managed and collected (without having to redirect to files). Finally, bgexec ensures that invoked processes get properly cleaned up at termination.
Since Tk 4.0, a subset of bgexec can be achieved using the fileevent command. The steps for running a program in the background are:
Execute the program with the open command (using the "|" syntax) and save the file handle.
global fileId
set fileId [open "|du -s $dir" r]
Next register a Tcl code snippet with fileevent to be run whenever output is available on the file handle. The code snippet will read from the file handle and save the output in a variable.
fileevent fileId readable {
if { [gets $fileId line] < 0 } {
close $fileId
set output $temp
unset fileId temp
} else {
append temp $line
} }
However, Bgexec is simpler and less error prone than using open + fileevent. You don't have to worry about non-blocking I/O. Everything is handled for you automatically.
Moreover, bgexec can run programs that fileevent can not. Fileevent assumes that the when stdout is closed the program has completed. But some programs, like the Unix compress program, reopen stdout, fooling fileevent into thinking the program has terminated. In the example above, we assume that the program will write and flush its output line-by-line. However when running another program, your application can block in the gets command reading a partial line.
Bgexec gives you get back the exit status of the program. It also lets you reliably kill detached processes and allows you to collect data from both stdout and stderr individually. Finally, since data collection is handled in C code, bgexec is faster and more efficient.
busy, exec, tkwait, vwait
exec, background, busy
2.5 | BLT |