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

Login
Author:         Frédéric Bonnet <[email protected]>
State:          Final
Type:           Project
Vote:           Done
Created:        23-Jan-2017
Post-History:   
Tcl-Version:    8.7
Tcl-Branch:     tip-462

Abstract

This TIP proposes to improve Tcl's handling of subprocesses created by the exec and open commands by adding a new ::tcl::process ensemble.

Rationale

This TIP is inspired by a request from FlightAware to fix the way Tcl currently handles child process exit status.

Subprocess creation can be either synchronous or asynchronous. In either case, a children with a non-zero return value indicates an error condition that is bubbled up to the Tcl error handling mechanism.

Synchronous subprocesses

Synchronous subprocesses are created using the exec command with no & terminal argument. Errors are raised synchronously as well.

Asynchronous subprocesses

Asynchronous subprocesses can be created using two distinct methods:

Error handling and status code

Errors are caught with the catch and try commands, with status codes given in the -errorcode options dictionary entry and the errorCode global variable in the form {CHILDKILLED pid sigName msg} / {CHILDSTATUS pid code} / {CHILDSUSP pid sigName msg}.

C-level access

The Tcl library provides the following procedures for managing subprocesses (excerpts from the Tcl documentation):

Moreover, Tcl_WaitPid is blocking unless called with the WNOHANG option.

Limitations

The current implementation is lacking several key features:

Specifications

A new ::tcl::process will be created:

::tcl::process subcommand ?arg ...: Subprocess management.

The following subcommand values are supported by ::tcl::process:

Note that msg and errorCode are only present for abnormally terminated processes (i.e. those where code is nonzero). Under the hood this command calls Tcl_WaitPid with the WNOHANG flag set for non-blocking behavior, unless the -wait switch is set (see below).

Additionally, ::tcl::process status accepts the following switches:

Examples

% ::tcl::process autopurge
true
% ::tcl::process autopurge false
false

% set pid1 [exec command1 a b c | command2 d e f &]
123 456
% set chan [open "|command1 a b c | command2 d e f"]
file123
% set pid2 [pid $chan]
789 1011

% ::tcl::process list
123 456 789 1011

% ::tcl::process status
123 0 456 {1 "child killed: write on pipe with no readers" {CHILDKILLED 456 SIGPIPE "write on pipe with no readers"}} 789 {1 "child suspended: background tty read" {CHILDSUSP 789 SIGTTIN "background tty read"}} 1011 {}

% ::tcl::process status 123
123 0

% ::tcl::process status 1011
1011 {}

% ::tcl::process status -wait
123 0 456 {1 "child killed: write on pipe with no readers" {CHILDKILLED 456 SIGPIPE "write on pipe with no readers"}} 789 {1 "child suspended: background tty read" {CHILDSUSP 789 SIGTTIN "background tty read"}} 1011 {1 "child process exited abnormally" {CHILDSTATUS 1011 -1}}

% ::tcl::process status 1011
1011 {1 "child process exited abnormally" {CHILDSTATUS 1011 -1}}

% ::tcl::process purge
% exec command1 1 2 3 &
1213
% ::tcl::process list
1213

Rejected Alternatives

The first version proposed to implement the feature as a new ps option to the existing info command. However, almost all operations in [info] are things that just examine state, not change it, and that's a principle-of-least-astonishment that should be upheld for the sake of less experienced users.

Reference implementation

The reference implementation is available on branch tip-462 in the Tcl Fossil repository.

History

The TIP has been modified to match the actual implementation that was part of the 9.0 release. See f5d0e75a49 for details.

Copyright

This document has been placed in the public domain.