Introducing Tcl 8.7 Part 4: process management

Published

This is the fourth in a series of posts describing new features in the upcoming 8.7 release of Tcl. It introduces the new process command added to allow monitoring and management of subprocesses spawned from Tcl.

To take Tcl 8.7 for a spin, you can download a pre-alpha binary for your platform. Alternatively, you can build it yourself from the core-8-branch branch in the Tcl fossil repository.

It is fairly common in Tcl scripts to invoke external programs for specialized tasks and Tcl provides several means for doing this including the built-in exec and open commands. This post assumes the reader is familiar with these commands. If not, read an overview here.

However, Tcl's support for monitoring status and errors in spawned subprocesses has been limited, particularly when run asynchronously. The ::tcl::process command ensemble fills in this missing capability.

Checking status of subprocesses

Consider the following attempt for the Tcl shell to invoke a copy of itself to run a non-existing script.

% exec [info nameofexe] nosuchfile.tcl
couldn't read file "nosuchfile.tcl": no such file or directory
% set errorCode
CHILDSTATUS 12816 1

Because the invocation was synchronous, an error was raised with an error code so if desired, the user or invoking application could take appropriate action.

Many times however, it is desired for the subprocess to execute asynchronously, perhaps because it is a monitoring application that needs to be constantly running, or it is time consuming while the parent needs to do other work etc. Tcl's exec command runs subprocesses asynchronously in the background when an ampersand is appended.

% exec [info nameofexe] nosuchfile.tcl &
24288

In this case, all the parent got back was the PID of the spawned process with no means to find out its status, whether it is still running or has exited and if the latter, the exit code. The parent had no means of taking remedial action, such as restarting the program, without the use of external platform-specific extensions. Tcl 8.7 remedies this with the introduction of the process status command.

% tcl::process status 24288
24288 {1 {child process exited abnormally} {CHILDSTATUS 24288 1}} 

Given a PID or a list of PIDs (as a single argument), the command returns a dictionary keyed by the subprocess ids. If the subprocess is still running, the associated value is an empty list. Otherwise it is a list of up to three elements. The first is the exit code. If the exit code is not 0, indicating an error exit, the second element is an error message and the third an error code list in the standard errorCode format. If a PID is not a subprocess, it is not included in the returned dictionary.

Note that for asynchronous processes created with the open pipeline syntax, it was possible to obtain exit status when closing the pipe. However, this could not be done in non-blocking fashion making it impossible to just check status and do other processing if the subprocess had not completed or to check multiple subprocesses simultaneously.

Synchronizing with subprocess exits

Normally, the tcl::process status command returns immediately. However, the -wait option can be specified to have it block until all subprocesses passed in the argument have exited, either normally or with error. This can be used to synchronize with one or more asynchronously started processes.

Cleaning up after yourself

When a process started by Tcl exits, certain resources are not immediately released until the waitpid system call (on U*x) or the subprocess handle is closed (on Windows). In older versions, as well as Tcl 8.7 by default, this is done on the next call that starts a subprocess. Tcl offers more control over this through two commands, tcl::process purge and tcl::process autopurge.

The first of these cleans up the resources associated with the specified subprocesses. Once this is done the exit status of those subprocesses is no longer available. This has multiple benefits in that the resources are not left hanging around until the next exec and moreover, provides control over which resources are released.

The other related command autopurge provides control over Tcl's automatic purging on every exec or open pipeline invocation. If called with a boolean false value, this automatic purging, which is on by default, is turned off. What this allows is for several subprocess invocations to be done in sequence without losing the exit status of preceding subprocesses.

The following sequence illustrates the use.

% tcl::process autopurge 0
0
% set first [exec [info name] nosuchfile.exe &]
22920
% set second [exec [info name] nosuchfile.exe &]
21252
% tcl::process status
21252 {1 {child process exited abnormally} {CHILDSTATUS 21252 1}} 22920 {1 {child process exited abnormally} {CHILDSTATUS 22920 1}}
% tcl::process purge $first; # Status of $first is purge, $second still available
% tcl::process status
21252 {1 {child process exited abnormally} {CHILDSTATUS 21252 1}}
% tcl::process purge; # ALL subprocess status is discarded
% tcl::process status
%

Introspection

The last command in the process ensemble is list which is primarily useful in interactive introspection. This simply returns a list of subprocess PIDs.

% exec notepad.exe &
23096
% exec notepad.exe &
6364
% tcl::process list
6364 23096

Open pipelines

Our examples above all used exec to run subprocesses. The commands work in identical fashion with subprocess created through open.

References

TIP 462: Add New ::tcl::process Ensemble for Subprocess Management

process manpage