** Description ** *** Named pipes in *nix *** (This section is adapted from the Linux man page [https://linux.die.net/man/7/pipe%|%pipe (7)].) '''Pipes''' and '''FIFOs''' (also known as '''named pipes''') provide a unidirectional interprocess communication channel. A pipe has a read end and a write end. Data written to the write end of a pipe can be read from the read end. A FIFO (short for "First In, First Out") has a name within the file system, and is opened as a regular file. Any process may open a FIFO, assuming the file permissions allow it, and may then read or write to it as appropriate. **** I/O on Pipes and FIFOs **** Pipes and FIFOs have exactly the same semantics once open. They provide a byte stream communication channel with no concept of message boundaries. **** Pipe Capacity **** A pipe has a limited capacity. If the pipe is full, then a write will block or fail, depending on whether the pipe was opened in non-blocking mode. In Linux the pipe capacity is 65536 bytes, but applications should not rely on a particular capacity. **** Portability notes **** According to [POSIX], pipes only need to be unidirectional. Linux implements unidirectional pipes. Portable applications should avoid reliance on bidirectional pipe semantics. *** Named pipes in Windows *** Windows also supports named pipes, albeit with different semantics. [TWAPI] provides [https://twapi.magicsplat.com/v4.3/namedpipe.html%|%commands] for communicating over named pipes from Tcl. *** Named pipes in Clusenix *** [http://web.archive.org/web/20010603004619/http://www.ibiblio.org/Dave/Dr-Fun/df9707/df970710.jpg] ** Event-driven I/O on named pipes ** In an interesting c.l.t posting, [Colin Macleod] describes what it takes to make a Unix FIFO (first in, first out) or named pipe, event-based: ---- I happen to be working on a project just now where I want to have an event-driven Tcl process read from a fifo. (This is on Solaris 2.6, in case it makes a difference.) My first attempts suggested that this could only be done by polling, but after digging around more I found that I could get fileevent to work as follows: ====== proc readpipe pipe { set data [read $pipe] ... process $data ... } ### Open the fifo, set up event-driven input from it. proc open_pipe pipename { exec /bin/sh -c "sleep 10 > $pipename" & set pipe [open $pipename r] fconfigure $pipe -blocking 0 fileevent $pipe readable [list readpipe $pipe] set dummy [open $pipename w] after 20000 {exec /bin/true}; # to get zombie sleep reaped } ====== The tricky bits are: 1. The process opening the fifo to read will block until another process opens it to write. I get past this by ''exec''-ing a shell which opens the pipe, writes nothing, then exits after 10 seconds. ''Note'' - it is critical that the "> $pipename" redirection is done by the shell (after it is forked as a new process) and not by Tcl. 1. If the fifo is closed by the writing process, ''fileevent'' will fire continuously because it sees end-of-file on the fifo. We can avoid this by making sure that at least one process always has the fifo open for writing, so I also open the fifo for writing from the same Tcl process and never write to it or close it. The 10-second delay in step 1 is to give this time to happen. 1. Finally, I noticed that the ''exec''-ed shell would hang around as a zombie after the sleep finished. The delayed and apparently pointless ''exec'' at the end is because running exec again has the side-effect of wait-ing for terminated background processes - documented under '''Tcl_DetachPids'''(3). [Lars H], 2008-08-10: My experience on the opening problem is slightly different: 1. Opening a pipe for ''reading'' is no problem, provided that you use the {RDONLY NONBLOCK} access mode; [open] returns immediately, and you can configure the channel as you wish. 2. Opening a pipe for ''writing'' is hazardous, because the [open] ''will'' block until the pipe has also been opened for reading; the access mode {WRONLY NONBLOCK} is rejected by the OS (throws error). The work-around is to first open the pipe for reading as above, second open it for writing (no NONBLOCK), and third close the read channel to the pipe. [DKF], 2011-09-30: The `WRONLY NONBLOCK` combo is correct, but you have to deal with a `POSIX ENXIO` error by rescheduling the attempt to open the pipe a bit later. I've seen reports that without NONBLOCK you can end up having problems in all threads in a process, but I don't know how much credence to give to those reports; after all, it might be a different bug... ---- Without suggesting that it is 100% bulletproof, this works fairly well: ====== ## ******************************************************** ## ## Name: fifo ## ## Description: ## Provide an anonymous fifo using cat. ## ## Parameters: ## ## Usage: ## trivial handler example: ## ## proc handle { fifo } { ## puts -nonewline [ gets $fifo ] ## } ## ## set fifo [ fifo handle ] ## puts $fifo peep! ## ## Comments: ## The input side of the fifo should never be flushed! ## The fifo can be closed like an ordinary file. proc fifo { { handler "" } } { if { [ catch { set fifo [ open |cat a+ ] fconfigure $fifo -blocking off fconfigure $fifo -buffering none if { [ string length $handler ] } { fileevent $fifo readable "$handler $fifo" } } err ] } { return -code error "[ myName ]: $err" } return $fifo } ## ******************************************************** ====== ---- (Explain how general file append operations can be detected as events only through polling.) An example can be seen in [tailf]. ** `fifo` module ** [dbohdan] 2019-04-13: The following is a Tcl 8.6 module for working with named pipes. It creates named pipes and disposes of them automatically using the [with] pattern. *** Use example *** ====== % fifo::with-fifos -template /tmp/pipe.io pipe1 pipe2 { puts [list fifos: $pipe1 $pipe2] exec tac $pipe1 & exec wc $pipe2 & exec echo hello\nworld | tee $pipe1 $pipe2 > /dev/null } fifos: /tmp/pipe_gwrbgf.io /tmp/pipe_7kG3E9.io 2 2 12 /tmp/pipe_7kG3E9.io world hello ====== *** Source code *** ====== # Copyright (c) 2019 dbohdan # License: MIT package require Tcl 8.6 namespace eval fifo { variable version 0.1.0 } proc fifo::mkfifo {{template {}}} { close [file tempfile path $template] file delete $path exec mkfifo $path return $path } proc fifo::with-fifos args { set template {} if {{-template} eq [lindex $args 0]} { set args [lassign $args _ template] } if {[llength $args] == 0} { error "wrong # args: should be\ \"with-fifos ?-template template? ?varName ...? script\"" } set varNames [lrange $args 0 end-1] set script [lindex $args end] try { foreach varName $varNames { upvar 1 $varName v set v [mkfifo $template] } uplevel 1 $script } finally { foreach varName $varNames { upvar 1 $varName v file delete $v } } } ====== ** See also ** * [chan pipe] for a built-in construct for working with unnamed pipes. <> File | Interprocess Communication | Channel | Glossary | With Pattern