PE Haskell after part 1

posted on 2014-08-21

This is part 1 of a series of Program Evolution in Haskell blog describing the various stages of developing a small program called after.

Because I am still learning about Haskell, this Program Evolution series will describe all the faults and fixes I had to do to get a "complete" Haskell program solution.

Program introduction

The program is called after and it's a very small script I'm converting to Haskell. The Bash version of the code is


if [[ -z "$CMDPATTERN" ]]; then
    echo "Need command pattern as first argument"
    exit 1
echo -n "Waiting for PID(S): " `pgrep --full "$CMDPATTERN"`

while [[ "`pgrep --count --full "$CMDPATTERN"`" -gt 1 ]]; do
    sleep 5

You can use this utility to block till another process is done. For example, when you started a compilation and decide to start compilation after that compilation is done, you could have compile everything running in one console and run after compile everything; compile this in another console.

Initial commit

The initial commit contains a simplified version of the real program.

Instead of waiting for a command, we wait for a process id (PID) by monitoring /proc.

We started by using hinotify to check for changes on the /proc directory. If a process is removed or added, we could check if our process is still running.

Because hinotify will call a handler function from another thread, we lock on an MVar. This is done in After.hs:

module After where

import System.INotify
import Control.Concurrent

justShowAndUnlock :: MVar String -> Event -> IO()
justShowAndUnlock mvar event = do
    putStrLn (show event)
    putMVar mvar "Yep, this is happening"

afterPid :: String -> IO ()
afterPid pid = do
    inotifyHandle <- initINotify
    unockedAfter <- newEmptyMVar
    watchHandle <- addWatch inotifyHandle [DeleteSelf, MoveSelf, OnlyDir] ("/proc/" ++ pid) (justShowAndUnlock unockedAfter)
    seen <- takeMVar unockedAfter
    putStrLn seen

The takeMVar call will block as long as justShowAndUnlock is not called.

The problem however is, that it is never called. Turns out you can not use inotify to monitor /proc.

From man 7 inotify:

Inotify reports only events that a user-space program triggers through the filesystem API. As a result, it does not catch remote events that occur on network filesystems. (Applications must fall back to polling the filesystem to catch such events.) Furthermore, various pseudo- filesystems such as /proc, /sys, and /dev/pts are not monitorable with inotify.

DOH! In the next part we will look at using a polling thread as an alternative to using inotify.

Update 2014-08-24: part 2 has been published.