\documentclass[]{article}
\usepackage{amsmath,graphicx,supertabular,hyperref,tabularx,ifthen}
\title{TIP \#332: Half-Close for Bidirectional Channels}
\date{September 25, 2008}
\author{Alexandre Ferrieux}
\urlstyle{sf}
\setlength{\parskip}{1ex}
\setlength{\parindent}{0pt}
\def\tipversion$#1${\texttt{\$#1\$}}
\def\tiplangle#1{\ensuremath{<}}
\def\tiprangle#1{\ensuremath{>}}
\def\tipbar#1{\ensuremath{|}}
\def\tipmail#1#2{\(\langle\){\small\expandafter\url{#1@#2}}\(\rangle\)}
\ifx\pdfoutput\undefined
\newcommand{\tipimage}[2]{\typeout{Make sure you download #1.eps}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.eps}}{\includegraphics[width=0.8\textwidth]{#1.eps}}}
\newcommand{\tipxref}[1]{}
\newcommand{\tipxrefend}{}
\else
\newcommand{\tipimage}[2]{\typeout{Make sure you create #1.pdf}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.pdf}}{\includegraphics[width=0.8\textwidth]{#1.pdf}}}
\pdfcatalog{/PageMode /UseOutlines}
\newcommand{\tipxref}[1]{\pdfannotlink attr {/C [0.5 0.5 1.0] /Border [0 0 1]} goto name {#1}}
\newcommand{\tipxrefend}{\pdfendlink}
\fi
\newenvironment{tipabstract}{\begin{abstract}}{\end{abstract}}
\begin{document}\maketitle
\begin{center}\begin{tabularx}{\linewidth}{|r@{: }X|}\hline
\textbf{TIP \#332}&\textbf{Half-Close for Bidirectional Channels}\\\hline
Author&

Alexandre Ferrieux \tipmail{alexandre.ferrieux}{gmail.com}
\\
Created&Thursday, \(\text{25}^{\text{th}}\) September 2008\\
Type&Project\\
Tcl Version&\(8.6\)\\
State&Final\\
Vote&Done\\
Version&\tipversion$Revision: 1.6 $\\
Keywords&Tcl, channel, close, socket, shutdown\\
Obsoletes&301\\
Post History&\\
\hline\end{tabularx}\end{center}
\thispagestyle{empty}\pagestyle{empty}
\begin{tipabstract}

This TIP proposes to extend the \textbf{close}/\textbf{chan close} commands to let them perform an unidirectional ``half-close'' on bidirectional channels.
\end{tipabstract}
\tableofcontents\setcounter{page}{0}\clearpage\pagestyle{plain}

\section{Background}

Bidirectional channels (sockets and command pipelines) allow Tcl to make an efficient use of a ``filter process'', by exchanging data back and forth over an abstract ``single'' channel.

However, this single channel abstraction comes with a too coarse-grained \textbf{close} primitive. Indeed, it closes both directions simultaneously, while it is often desirable to close ``gracefully'' the half-connection \textit{to} the filter process, leaving the return path open. The effect of such a half-close is that the filter receives a bona fide EOF alone, without a nearly simultaneous SIGPIPE on its write end if it happens to be writing at that time. Moreover, if the filter is itself comprised of a pipeline of processes, some of which doing buffered I/O, then this graceful EOF may be the only way of flushing the chain and receiving back precious data.

This technique is supported by all modern OSes: for pipes there are actually two separate file descriptors/handles, and it suffices to close() the write side; for sockets, a single fd is used, but a specific syscall, shutdown(), brings back the ability to half-close. Hence it is fairly natural for a universal ``OS glove'' like Tcl to expose this universal feature.

\section{Proposed Change}

This TIP proposes to extend the \textbf{close} and twin brother \textbf{chan close} commands to take an optional extra ``direction'' argument, indicating a half-close on the substream going in that direction:

\begin{quote}
\textbf{close} \textit{channel} ?\textbf{read}\tipbar{}\textbf{write}?
\end{quote}

When the extra direction argument (which may be abbreviated) is given, first the OS-level half-close is performed: this means a shutdown() on a socket, and a close() of one end of a pipe for a command pipeline. Then, the Tcl-level channel data structure is either kept or freed depending on whether the other direction is still open:
\begingroup\small\begin{verbatim}
	set f [open |command r+]
	...
	close $f w ;# $f still exists
	...
	close $f r ;# now $f is gone
\end{verbatim}
\endgroup

Also, a single-argument \textbf{close} on an already half-closed bi-channel is defined to just ``finish the job'', which allows to write blind cleanup procedures easily:
\begingroup\small\begin{verbatim}
	if {[catch {
	  set f [open |command r+]
	  ...
	  close $f w
	  ...
	} err]} {
	  ...
	  close $f ;# close what's left
	}
\end{verbatim}
\endgroup

In the case of a command pipeline, the child-reaping duty falls upon the shoulders of the last close or half-close, so that an error condition at this stage (like ``Child exited abnormally'') doesn't leak system resources.

Last, a half-close on an already closed half raises an error:
\begingroup\small\begin{verbatim}
	set [open |command r+]
	close $f w
	close $f w
	==> channel "file3" wasn't opened for writing
\end{verbatim}
\endgroup

And the same applies to wrong-sided unidirectional channels:
\begingroup\small\begin{verbatim}
	set [open filename r]
	close $f w
	==> channel "file3" wasn't opened for writing
\end{verbatim}
\endgroup

\section{Rationale}

The concept has gone full circle. From an initial half-close proposal very close to the current one, an ambitious \textbf{chan split} generalization was born in the surrounding enthusiasm, and specified in \cite{tip301}. Then, \cite{tip304}'s \textbf{chan pipe} was accepted, which addressed most of the ``splitting'' demand (asymmetric fconfigures on both ends of a command pipeline can be done by redirecting to a standalone pipe). Moreover, in hindsight it appears that the implementation of \cite{tip301} had very long-ranging effects on various channel implementations, which are more numerous today than before (TLS, reflection API).

As a consequence, \cite{tip301} is being withdrawn, and the current TIP goes back to its initial, lower profile: just provide the half-close.

\section{C Interface}

Luckily, the polymorphic channel API in Tcl has been fitted with a half-close function for nearly 10 years (!): the Tcl\_ChannelType structure has had a \textit{close2proc} member since 1998 in order to avoid deadlocks when closing a command pipeline. As a consequence, the implementation of half-close can be done with a constant channel ABI, avoiding any compatibility issue for extensions.

Still, the \textit{close2proc} member does not have public status. To promote symmetry between the script-level and public C APIs, this TIP proposes to add an entry in the main stub table with the following signature:

\begin{quote}
int \textbf{Tcl\_CloseEx}(Tcl\_Interp *\textit{interp}, Tcl\_Channel \textit{chan}, int \textit{flags})
\end{quote}

The behavior being defined as mirrorring the script-level semantics described bove, where \textit{flags} is either 0 (meaning bidirectional close) or one of TCL\_CLOSE\_READ and TCL\_CLOSE\_WRITE (meaning half-close).

\section{Reference Implementation}

See Patch 219159 [\url{https://sourceforge.net/tracker/index.php?func=detail&aid=219159&group_id=10894&atid=310894}].

After a discussion with Andreas Kupries, the plan is to half-close-enable only raw (unstacked) channels for the time being, raising an explicit error when trying a half-close on a non-bottom channel. This leaves time to carefully design the necessary API extension of the generic stacking and reflection layers, while preparing a fully compatible change (error cases becoming valid).

On the channel-type-dependent side, only sockets and pipelines will be half-close-enabled for now: these are assumed to represent the most pressing demand.

\section{Copyright}

This document has been placed in the public domain.

\section{Colophon}

\textit{TIP AutoGenerator --- written by Donal K. Fellows}
\begin{thebibliography}{TIP \#2}
\addcontentsline{toc}{section}{References}
\bibitem[TIP \#301]{tip301}
Alexandre Ferrieux,
\emph{Split Bidirectional Channels For Half-Close},
on-line at \url{http://purl.org/tcl/tip/301.html}
\bibitem[TIP \#304]{tip304}
Alexandre Ferrieux,
\emph{A Standalone [chan pipe] Primitive for Advanced Child IPC},
on-line at \url{http://purl.org/tcl/tip/304.html}
\end{thebibliography}
\end{document}
