For the last few days I’ve focused my attention on getting Windows file notifications working. Of course, this journey began with getting Haskell working on Windows, which I found surprisingly painless. I believe I only had one location that didn’t get added to my %PATH% variable that broke cabal-install actions, and otherwise the Haskell Platform downloaded and worked like a charm.
In the purely Haskell portion of Win32-notify (not to be confused with pure Haskell functions) I followed INotify’s design pattern to spawn two threads for each “directory watch”; one thread uses a foreign blocking I/O call to retrieve events as fast as possible and put them in a Haskell channel. The other thread reads from the channel and dispatches the associated handler. One nice thing about the Windows API is that it supports listening on an entire sub-tree of the directory structure so I didn’t have to implement recursive directory tracking myself like I did with INotify on Linux.
In working with the rather long-in-the-tooth Win32-notify package I’ve tried to avoid touching any code that uses the foreign function interface. I’ve succeeded, for the most part, so far.
I say for the most part because I added the interruptible qualifier to the foreign import after observing that my little test program would hang while quitting until one more file system event was received. The source of the problem was this: foreign imported functions (even safe foreign imports with -threaded enabled in GHC) aren’t interrupted from completing a blocking I/O call when a ThreadKilled exception is thrown at their thread. As a result, the thread recursively listening for events needed to finish its most recent I/O call before the thread would die.
I say for now because it appears that the current usage of the Windows file system API supports a modified event, but no write-close (or similar) event. This means that, for example, if I save a file in Emacs I get two modified events instead of one, which could be a problem if I kick of a compilation of a source file every time it is modified. I’m sure I’ll eventually be forced to address this issue, but I am happy to put off reading Windows API documentation for as long as possible.
Next up will be hooking into the existing Mac file watcher library. I’ve linked the github repositories where you can find my work below.
Just offering an update on what I’ve been working on for the last several days.
In addition to writing code to abstract the relevant features of INotify (continued from last post), I’ve rebooted my Haskell environment for Linux and posted the initial code to github.
I’m running Arch Linux on my laptop so I originally installed Haskell using pacman packages. Unfortunately, some pacman/Haskell issues ended up causing dependency failures when I attempted to install some necessary Haskell packages to do my work. I’ve heard that some other Google Summer of Code 2012 projects are working on improving dependency resolution in Cabal, which is good news. Anyway, after both pacman and cabal-install failed to install the packages I elected to reinstall Haskell to a user-specific location, build/bootstrap Cabal+cabal-install, and start using cabal-install for Haskell package management. The process was a little long because my laptop is old and slow, but things seem to be running smoothly now. Special thanks to sciolizer for suggesting the long but effective fix, as well as showing me how to build a local copy of documentation automatically with cabal-install.
The github repository, hfsnotify, contains broken code at the moment (I was encouraged to get it available to my mentors and the community, even though it’s a work in progress).
I started playing around with the existing file watcher Haskell code cited in the project proposal today. Since I’m working on a Linux laptop I naturally started with the Linux hinotify package before moving to the Mac and Windows packages.
Here is what I’ve learned so far:
- hinotify throws an exception if it’s asked to watch a file that doesn’t exist (blah!)
- Created and Deleted events are reported as expected for directories and files, but hinotify is non-recursive so I’ll have to implement recursive directory tracking myself
- Many events triggered by things such as listing a directory report Open and Close events without any path — i.e., maybeFilePath=Nothing; I think these events belong to the directory that I’m watching
- Even though it was reported that vim does something weird with moving and/or overwriting files when it saves them hinotify doesn’t report anything unexpected; most importantly, it reports a Modified event on the file that is saved, though it only reports Created, Open, Close when a new file is created (i.e., saved for the first time)
The next task will be to get working Haskell environments on Mac and Windows machines so that I can do preliminary tests of those file watchers. Then I should be able to offer a unified recursive or non-recursive watcher that is attentive to some file pattern such as /.*[.][l]?hs[c]?/.
Welcome to the first post on my Google Summer of Code 2012 blog! This blog will document the progress of my project this summer.
I’ll be working on a project for haskell.org aimed at improving Haskell development environments by decreasing time wasted to rebuild and redeploy modified code.
For a more detailed description of the project check out the project proposal. More to come soon!