dmitrymatveev.co.uk
BlogRussian blogGSoC 2011GSoC 2010AboutMaintenance
Back to posts
Afterword

The Google Summer Of Code has ended for four month ago. Since then, the both projects have got some improvements, tiny and significant.

Glib kqueue backend

The patch is available in the NetBSD's pkgsrc, thanks to Julio Merino for packaging. You will need to rebuild the glib2 package as follows:

$ cd /usr/pkgsrc/devel/glib2
$ PKG_DEFAULT_OPTIONS=kqueue make

Please report about any issues to my personal mail (see the About page).

Recently I have been contacted by Antoine Jacoutot. Antoine is an OpenBSD and GNOME contributor. He has integrated the gio patch into OpenBSD's package collection, so now the functionality is available in OpenBSD too. Antoine also mentioned that he can help with pushing the patch to upstream, and I will work on in in Jan/Feb.

libinotify

As far as I know, today libinotify is packaged in two systems:

  1. NetBSD -- http://pkgsrc.se/devel/libinotify. Thanks to Thomas Klausner for packaging.
  2. FreeBSD -- http://www.freebsd.org/cgi/cvsweb.cgi/ports/devel/libinotify/. Thanks to Stanislav Sedov for FreeBSD patches and packaging.

Some problems were found on OpenBSD -- there is no pthread_barrier(3), so I will have to write my own synchronization primitive instead of using this convenient one.

libinotify has helped to run incrond on NetBSD and FreeBSD. Soon I will prepare a package for it. Also, porting is planned.

If you want to build and intall the latest libinotify on your BSD system:

$ git clone git://github.com/dmatveev/libinotify-kqueue.git
$ cd libinotify-kqueue
$ autoreconf -fvi
$ ./configure
$ make
$ make test
# make install

Some tests (IN_OPEN, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE) will fail and it is expected -- these events just are not implemented.

So, the work is in progress, Happy Holidays to everyone!

Add a comment
Testing the behavior

All planned features for libinotify-kqueue are implemented, and now I am working on tests.

Tests are especially important for a library that emulates an existing behavior. So I have decided to cover the library with behavioral tests, not with the unit tests.

How to test the behaviour of such an entity like inotify? To answer this question, we have to figure out what it provides at first.

So, with inotify we can:

  1. Start a watch;
  2. Modify a watch;
  3. Register notifications from watch(es);
  4. Remove a watch.

Great. But if we want to register notifications from the file system, we should produce the activity on the file system at first. So we need to have two entities: one produces activity (a producer) and one registers it (a consumer).

Next, we have to ensure that the registered events are the same events that we have expected. So the scheme how the producer and the consumer communicate might look like the following:

  1. Producer tells to consumer, which events are expected to appear;
  2. Consumer starts registering events;
  3. Producer starts generating a file system activity;
  4. Consumer register all the expected events OR reaches the timeout;
  5. Consumer specifies to producer, which events have left unregistered;
  6. Producer processes this info and passes or failes the appropriate test case.

The communication between the producer and consumer can be also generalized like this:

  1. Producer tells to consumer, what to do;
  2. Consumer executing the action;
  3. Producer waits while the consumer works (producer can do its own work here too);
  4. Consumer tells the results to the producer;
  5. Producer passes or failes the test case.

Besides monitoring for expected events, action is also can be:

  • Starting a watch or updating flags of an existing watch;
  • Stopping a watch.

These actions give us the additional functionality, quite suitable for covering sophicated scenarions.

The test suite have to run on both Linux and NetBSD. All tests have to pass on Linux, some of them are expected to fail on NetBSD. I will write some tests knowing that they will fail on NetBSD intentionally.

The test suite should also have entities for logging and tracking passed and failed tests.

Considering all these requirements, I have developed my own tiny BDD framework. Its last version can be viewed here (and then, the more recent version will be available in the master branch).

It was an interesting experience to make it run on both Linux and NetBSD. I have represented both consumer and producer by threads (probably it is possible to run them in a single thread, but in my vision it would require to build an FSM and with the FSM it would be harder to understand the flow of tests), and the hardest part (as always) were in its synchronization. Pthread barriers fit perferctly here, and it worked fine on Linux but didn't work well on NetBSD. So I had to implement my own primitives atop of mutexes and condition variables for it.

Currently I am covering inotify with the tests on Linux. There are 2 tests with 16 test cases in total now, but it is just a beggining and I expect to get 50-70 test cases in total. Anyway, the first test already crashes my library to death :) But I will resolve all these issues only after writing all the tests.

Test suite running on Debian GNU/Linux, larger version, 10K

Add a comment
First results on the libinotify-kqueue compatibility library

Some hours ago I have just merged a big squashed commit into the master branch of the second part of my GSoC project.

The code is the implementation of the inotify emulation and a test application. Not all the planned events are supported currently, but it is just a question of time.

During the last two weeks I have changed some design decisions and rewrote some pieces of code some times. The general complication is in the inotify behavior: when you start watching a directory, inotify also watches all the files in this directory. So we have a dependency between a user-supplied entry (a directory) and automatically watched entries (the files of that directory). Obviously, the dependency files should be also monitored and events from them should be fired to user. Manipulating with this mess on C is an unforgettable experience :)

Now the library has come to its final conceptual form, however I will write more detailed overview a bit later, after a set of refactorings and clean-ups.

The screenshot below illustrates what and how it works:

A larger version (20K) is also available.

Add a comment
Midterm evaluations

Its time fot Google Summer of Code midterm evaluations and here I will summarize the current project status.

GIO/kqueue backend

All the planned functionality is implemented:

  • File and directory monitoring, supported events are
    • G_FILE_MONITOR_EVENT_CREATED
    • G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED
    • G_FILE_MONITOR_EVENT_CHANGED
    • G_FILE_MONITOR_EVENT_MOVED
    • G_FILE_MONITOR_EVENT_DELETED
  • Monitoring for files and directories which do not exist yet;
  • Monitoring cancellation.

Current limitations

  • G_FILE_MONITOR_EVENT_PRE_UNMOUNT and G_FILE_MONITOR_EVENT_UNMOUNTED events are not supported;
  • Pair moves (i.e. when a GFileMonitor emits DELETED and CREATED events for a file move) are not supported;
  • Backend opens a file descriptor for each monitored entry, so the observed file's FS can not be unmounted.

All the issues pointed by you and Glib guys were resolved and merged into my kqueue/master branch.

Furher plans

  • Make the kqueue backend behave more like an inotify one, if there will be significant differences;
  • Fixes and further improvements (i.e. more error handling and robustness);
  • Tests. I have not found the monitor tests in Glib test suite, so I am going to write my own.

Brief design overview

Backend runs an additional thread, a kqueue thread, where themonitoring is performed.

When user creates a GFileMonitor instance for a file, backend tries to open that file and if the file is not found, the backend pushes itinto a missing files list. If the file was found, the backend pushes its FD to a queue and then sends a signal (via a socket) to the kqueue thread. The kqueue thread wakes up and takes this descriptor for the further kevent().

When a call to the kevent() returns, the kqueue thread searches for signalled descriptors, marshals and writes it into a local socket.

This socket is watched by Glib in its main event loop. Backend reads the notifications coming from a kqueue thread, converts kqueue filter flags into GIO event flags and then emits the "changed" signal on the appropriate GFileMonitor instances.

Backend periodically traverses the missing files list (if it is notempty) and tries to start monitoring on each entry. If the file hasappeared on the FS and the monitoring has started successfully, the backend removes this entry from the missing files list. Otherwise, it will repeat checking on missing files until the user will cancel the monitoring on the appropriate GFileMonitor instance.

libinotify compatibility library

In development.

I have just finally took the general design decisions, so it is planned to works like follows.

On each call to inotify_init() or inotify_init1() a new thread and a new kqueue will be created. The kqueue will sleep on the monitored descriptors in that thread, like in the gio/kqueue backend. A socketpair will be created and one of its descriptors will be returned to a user as an inotify interface, and another one will be used by the kqueue thread.

The tricky ones are inotify_add_watch() and inotify_rm_watch() functions. The both return a value to an end user, indicating a failure or a success. These functions will push an object (representing the operation requested) to a special internal queue (separate for each kqueue thread). Then an appropriate kqueue thread will be awakened and will perform the requested operation (add/modify/remove an entry). The kqueue thread will also put the operation status (success or failure, an identificator of an added watch, etc) to a per-operation storage so it than could be used in the inotify_add_watch() and inotify_rm_watch() functions as return values.

The problem is in the implementation of this scheme and in that inotify_add_watch() and inotify_rm_watch() functions should sleep while the kqueue thread will perform the requested operations.

I have decided to use SIMPLEQ for a queue and pthread barriers for thread synchronization, so the implementation now looks clear for me.

The inotify emulation library will not support some kinds of events, like IN_CLOSE[_XXX], IN_OPEN and so on, because kqueue has no analogues to it. Some other events, like IN_MOVED_FROM or IN_MOVED_TO (for a directory) I will try to simulate with caching and diffing the directory's contents between notifications.

Also, in Linux a user can determine the total count of pending notifications on the inotify's file descriptor with fcntl(). With an userspace emulation it will not be possible to do the same on BSDs, I think.

Add a comment
backend (7)
libinotify (4)
gio (3)
results (3)
glib (2)
inotify (2)
current (1)
week7 (1)
kevent (1)
week-2 (1)
smalltalk (1)
week2 (1)
tests (1)
week0 (1)
week-1 (1)
fixes (1)
gmainloop (1)
week4 (1)
poll (1)