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.

Copyright

This document has been placed in the public domain.