Named Pipes

08 Mar 2015

A couple of weeks ago I heard about named pipes, a very neat feature of unix-like systems. This feature allowed me to speed up my TDD cycle. In general, though, I think named pipes fall into the “good-to-know” category for your developer toolbelt.

First off all, a unix pipeline allows a set of processes to have their I/O chained together. A typical example that you’ve probably seen or used before is:

$ ls -l | grep Documents
drwx------+  3 jassa  staff   102 Feb  6 19:59 Documents

Each pipe (|) connects the output of the left program, to the input of the right program. In this example we pass the output of ls -l (a list of files and directories) to grep Documents (which will return the lines that match our “Documents” search). Pipes have many useful applications, and named pipes are an extension of them.

A traditional pipe lives as long as the processes involved are running. A named pipe, on the other hand, is persistent and sits in your system like any other file. Whatever you write to it, can be read elsewhere. Here’s an example (we need two terminal sessions because reading/writing to a named pipe is I/O blocking).

# Terminal 1
$ mkfifo myNamedPipe # creates a file named “myNamedPipe”
$ echo "Foo" > myNamedPipe # send “Foo” to myNamedPipe
  # ..this process hangs until we read the contents of myNamedPipe elsewhere

# Terminal 2
$ cat myNamedPipe
Foo # returns immediately, and the process in terminal 1 also returns

Reading and writing to named pipes are I/O blocking operations. Both processes depend on each other and will be blocked until you read the contents of your named pipe. Our previous example with an anonymous pipe could become:

# Terminal 1
$ ls -l | grep $(cat myNamedPipe)
  # hangs…

# Terminal 2
$ echo “Documents” > myNamedPipe

# Back in terminal 1 our process returns…
drwx------+  3 jassa  staff   102 Feb  6 19:59 Documents

By now you should start to realize the power of named pipes. I switched to Vim recently (most people I work with use it, so in order to pair effectively I left Emacs for a while), and on top of that I started working on a Java project that uses maven to manage its build. I decided to avoid the typical slow and bloated IDEs, and just try a Vim workflow instead. I value simplicity, not just in my code but also in my tools, and named pipes allowed me to achieve a tight feedback loop without the overhead an IDE and third-party libraries bring into the mix.

Basically I created a custom autorunner for whenever my tests change, using Vim and maven.

My "manual" TDD workflow looks like this:

  1. Make and save changes to SomeClassTest.java
  2. Run mvn -Dtest=SomeClassTest clean test in a command prompt

In order to automate the 2nd step, I had to setup a couple of things in my terminal.

  1. Create a named pipe (in my case mkfifo .test-class)
  2. Run an endless loop that evaluates the name of the desired test class

     $ while true; do mvn -Dtest=$(cat .test-class) clean test; done
    

    This endless loop will be blocked waiting to read from the .test-class pipe. If we echo “SomeClassTest” > .test-class it will run the given test and block again waiting to read.

The last step is making Vim write the class name to the pipe every time we save our changes. In my project’s local .vimrc (See the exrc option):

:autocmd BufWritePost *Test.java :call RunTest()

function! RunTest()
    :call system('echo ' . expand('%:tr') . ' > .test-pipe')
endfunction

This function runs every time we save a file that ends with Test.java, and passes the current file name to our named pipe. Now my tests run automatically whenever I save my changes. It might not look like much, but consider the seconds saved from every time I had to switch to another tab, hit the up arrow and press enter (or alternatively reverse-i-search for “test”).

What I liked the most about this approach is that with a couple tweaks you can reuse it for pretty much any language and environment.

This is only the first use that I give to named pipes. Like I said, it’s probably one of those features that are useful to know about, but probably not immediately. I don’t think I have any other use for them right now, but I bet if I encounter a problem they can solve, they’ll pop up in my head.