<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE TIP SYSTEM "http://www.tcl.tk/cgi-bin/tct/tip/tipxml.dtd">
<!-- Converted at Thu May 17 03:58:53 GMT 2012 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='304'>
<header><title>A Standalone [chan pipe] Primitive for Advanced Child IPC</title><author address="mailto:alexandre.ferrieux@gmail.com">Alexandre Ferrieux</author><status type='project' state='final' tclversion="8.6" vote='after'>$Revision: 1.15 $</status><history></history><created day='7' month='feb' year='2007' /><keyword>Tcl exec process subprocess pipeline channel</keyword></header>
<abstract>Currently, it is not easy to get both (separate) dataflows from the stdout and stderr of a child. BLT&apos;s <emph style="bold">bgexec</emph> does this in an extension, and could be added to the core. But the point of this TIP is to show that a much smaller code addition can provide a lower-level primitive with much more potential than <emph style="bold">bgexec</emph>&apos;s: a <emph style="italic">standalone pipe</emph> creation tool like TclX&apos;s <emph style="bold">pipe</emph> command.</abstract>
<body><section title="Background">
<para>Getting back both stdout and stderr from a child has long been an FAQ on <url ref="news:comp.lang.tcl"/>, to the point that <emph style="bold">bgexec</emph> has been offered in an extension, BLT, whose main job is very remote from IPC. Now this has been a problem for many, who didn&apos;t want to have the problems of distributing a script depending on an extension. Moreover, <emph style="bold">bgexec</emph> does not scale up, in that it cannot bring back the separate stderrs of all four children in:</para>
<verbatim><vline encoding='base64'>CXNldCBmZiBbb3BlbiAifGEgfCBiIHwgYyB8IGQiIHJd</vline></verbatim>
<para>A popular workaround for script-only purists is to spawn an external &quot;pump&quot; like <emph style="italic">cat</emph> in an [<emph style="bold">open</emph> ... r+], and redirect the wanted stderr to the write side of the pump. Its output can then be monitored through the read side:</para>
<verbatim><vline encoding='base64'>CXNldCBwdW1wIFtvcGVuICJ8Y2F0IiByK10=</vline><vline encoding='base64'>CXNldCBmMSBbb3BlbiAifGNtZCBhcmdzIDI+QCAkcHVtcCIgcl0=</vline><vline encoding='base64'>CWZpbGVldmVudCAkZjEgcmVhZGFibGUgZ290X3N0ZG91dA==</vline><vline encoding='base64'>CWZpbGVldmVudCAkcHVtcCByZWFkYWJsZSBnb3Rfc3RkZXJy</vline></verbatim>
<para>Now this is all but elegant of course, difficult to deploy on Windows (where you need an extra cat.exe), and not especially efficient since the &quot;pump&quot; consumes context switches and memory bandwidth only to emulate a <emph style="italic">single</emph> OS pipe when Tcl is forced to create <emph style="italic">two</emph> of them via [<emph style="bold">open</emph> ... r+].</para>
<para>For this latter performance issue, a better alternative is a named pipe. But it is even harder to create on Windows, and it is a nightmare to handle its lifecycle properly (it doesn&apos;t die automagically with the creating process; blocks on open() if other side is not ready).</para>
</section>
<section title="Proposed Change">
<para>All this points to the obvious solution: steal TclX&apos;s <emph style="bold">pipe</emph> command, which wraps the OS&apos;s pipe()/CreatePipe() syscall, yielding two Tcl channels wrapping the read and write side of the underlying pipe.</para>
<para>TclX&apos;s <emph style="bold">pipe</emph> command allows two syntaxes:</para>
<verbatim><vline encoding='base64'>CXBpcGUgcHIgcHc=</vline><vline encoding='base64'>CWZvcmVhY2gge3ByIHB3fSBbcGlwZV0gYnJlYWs=</vline></verbatim>
<para>Its application to following stderr is straightfoward:</para>
<verbatim><vline encoding='base64'>CXBpcGUgcHIgcHc=</vline><vline encoding='base64'>CXNldCBmMSBbb3BlbiAifGNtZCBhcmdzIDI+QCAkcHciIHJd</vline><vline encoding='base64'>CWZpbGVldmVudCAkZjEgcmVhZGFibGUgZ290X3N0ZG91dA==</vline><vline encoding='base64'>CWZpbGVldmVudCAkcHIgcmVhZGFibGUgZ290X3N0ZGVycg==</vline></verbatim>
<subsection title="Specification">
<para>The basic functionality is to return a pair of channels, so the script-level API does just that:</para>
<quote><emph style="bold">chan pipe</emph></quote>
<para>Create a pipe, and return a list of two channels wrapping either side of the pipe. The first element is the side opened for reading, the second for writing:</para>
<verbatim><vline encoding='base64'>CWxhc3NpZ24gW2NoYW4gcGlwZV0gcHIgcHc=</vline></verbatim>
<para>Subsequently, anything written to $pw will be readable on $pr. We purposefully drop the other [pipe pr pw] syntax from TclX, for the sake of minimality.</para>
</subsection>
<subsection title="C Interface">
<para>A counterpart to <emph style="bold">chan pipe</emph> is added to Tcl&apos;s C API:</para>
<quote>int <emph style="bold">Tcl_CreatePipe</emph>(Tcl_Interp *<emph style="italic">interp</emph>, Tcl_Channel *<emph style="italic">rchan</emph>, Tcl_Channel *<emph style="italic">wchan</emph>, int <emph style="italic">flags</emph>)</quote>
<para>The <emph style="italic">interp</emph> argument is used for reporting errors and registering the channels created, the <emph style="italic">rchan</emph> and <emph style="italic">wchan</emph> arguments point to variables into which to place the channels for each end of the pipeline, and the <emph style="italic">flags</emph> argument is reserved for future expansion.</para>
</subsection>
</section>
<section title="Discussion">
<para>It has been in TclX for years, so it is fireproof. The code can be directly copied from there, and is dead simple, just calling Tcl_MakeFileChannel and Tcl_RegisterChannel on both fds/handles. Just the low-level syscall changes between unix and windows.</para>
<subsection title="Additional Benefits">
<para>It should also be noted that this primitive allows to deprecate the use of [<emph style="bold">open</emph> &quot;|...&quot;] and <emph style="bold">pid</emph> themselves !</para>
<verbatim><vline encoding='base64'>CWxhc3NpZ24gW2NoYW4gcGlwZV0gcDFyIHAxdw==</vline><vline encoding='base64'>CWxhc3NpZ24gW2NoYW4gcGlwZV0gcDJyIHAydw==</vline><vline encoding='base64'>CXNldCBwaWRzIFtleGVjIGNtZCAkYXJncyA+QCAkcDF3IDI+QCAkcDJ3ICZd</vline><vline encoding='base64'>CWZpbGVldmVudCAkcDFyIHJlYWRhYmxlIGdvdF9zdGRvdXQ=</vline><vline encoding='base64'>CWZpbGVldmVudCAkcDJyIHJlYWRhYmxlIGdvdF9zdGRlcnI=</vline></verbatim>
<para>Taking this idea one step further, one can even deprecate the &quot;|&quot; in <emph style="bold">exec</emph>, with:</para>
<verbatim><vline encoding='base64'>CWV4ZWMgYSB8IGIgfCBjICY=</vline></verbatim>
<para>being rewritten as:</para>
<verbatim><vline encoding='base64'>CWV4ZWMgYSA+QCAkcDF3ICY=</vline><vline encoding='base64'>CWV4ZWMgYiA8QCAkcDFyID5AICRwMncgJg==</vline><vline encoding='base64'>CWV4ZWMgYyA8QCAkcDJyICY=</vline></verbatim>
<para>(I&apos;m not crazy about actually deprecating ol&apos;good and concise idioms in favour of the tedious lines above; however as an orthogonality worshipper I like to think it would be theoretically possible to reimplement them in pure Tcl thanks to the added power of one tiny extra primitive)</para>
<para>A more serious advantage, is that it allows the &quot;half-close&quot; of a command pipeline (detailed in <tipref type="text" tip="301"/> which the current TIP makes partly obsolete). Indeed</para>
<verbatim><vline encoding='base64'>CWxhc3NpZ24gW2NoYW4gcGlwZV0gcHIgcHc=</vline><vline encoding='base64'>CWxhc3NpZ24gW2NoYW4gcGlwZV0gcXIgcXc=</vline><vline encoding='base64'>CWV4ZWMgYSA8QCRwciA+QCRxdyAm</vline></verbatim>
<para>allows to close just the read side with</para>
<verbatim><vline encoding='base64'>CWNsb3NlICRwdw==</vline></verbatim>
<para>and still read all the output of the command up to its EOF:</para>
<verbatim><vline encoding='base64'>CXdoaWxlIHtbZ2V0cyAkcXIgbGluZV0+PTB9IHsuLi59</vline></verbatim>
<para>which is notoriously impossible with</para>
<verbatim><vline encoding='base64'>CXNldCBwcSBbb3BlbiB8YSByK10=</vline></verbatim>
</subsection>
</section>
<section title="Directions for Future Work">
<para>On unix, there&apos;s no reason to limit the use of unnamed pipes to stdout and stderr. An arbitrary pipe topology can be set up between several childs with descriptors 3,4,5... For this we could imagine a natural extension of <emph style="bold">exec</emph>&apos;s <emph style="bold">2&gt;@</emph> to <emph style="bold">3&gt;@</emph>, <emph style="bold">4&lt;@</emph>, etc.</para>
<verbatim><vline encoding='base64'>CWV4ZWMgZm9vID5AICRwIDM+QCAkcSA0PEAgJHIgJg==</vline><vline encoding='base64'>CWV4ZWMgYmFyIDxAICRxID5AICRyIDQ8QCAkcCAm</vline></verbatim>
<para>Of course, such uses are extreme, but useful in complicated IPC setups to achieve much better performance (through direct point-to-point pipes) than would a simpler &quot;star&quot; or &quot;blackboard&quot; topology (where all children write back to a central message routing process).</para>
</section>
<section title="Reference Implementation">
<para>A patch against the Tcl HEAD (8.6) is located at <url ref="http://sf.net/tracker/?func=detail&amp;aid=1978495&amp;group_id=10894&amp;atid=310894"/></para>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
</section>
</body></TIP>

