User:Barche/File Change Notification

From K-3D

Jump to: navigation, search

For plugins like bitmap readers and mesh readers, it is useful for K-3D to know about changes to these files, and reload when needed.

Design

Proposed additions to k3d::iuser_interface - these methods will be used by the rest of the application to watch for changes. Note that not all user interface plugins will implement this functionality, so failure to watch a path is not necessarily an error condition.

class iuser_interface
{
public:
  // ... other stuff here ...

  /// Call a slot whenever given filesystem path is modified.  Note that we are watching the
  /// path, not an inode, so it isn't an error to specify a path for a nonexistent file.
  /// The slot will be called when a file is created / modified / renamed / deleted at that
  /// location.  Returns a nonzero watch identifier that is used to cancel the watch later-on,
  /// or 0 if there is an error or the implementation does not support path-watching.
  virtual uint_t watch_path(const filesystem::path& Path, const sigc::slot<void>& Slot) = 0;

  /// Stop watching the given path.
  virtual void unwatch_path(const uint_t WatchID) = 0;
};

New k3d::ifile_change_notifier interface, for use by notifier plugins - this functionality will be used as an abstraction for platform-specific APIs. Plugins implementing this interface will be used by user interface plugins as part of their internal implementation.

class ifile_change_notifier
{
public:
  // ... other stuff here ...

  /// Call a slot whenever given filesystem path is modified.  Note that we are watching the
  /// path, not an inode, so it isn't an error to specify a path for a nonexistent file.
  /// The slot will be called when a file is created / modified / renamed / deleted at that
  /// location.  Returns a nonzero watch identifier that is used to cancel the watch later-on,
  /// or 0 if there is an error.
  virtual uint_t watch_path(const filesystem::path& Path, const sigc::slot<void>& Slot) = 0;
  
  /// Stop watching the given path.
  virtual void unwatch_path(const uint_t WatchID) = 0;

  /// Returns true if there are any notification events waiting.
  virtual const bool_t pending_changes() = 0;
  
  /// Handles the next notification event (calls one slot), blocking if there are no events pending.
  virtual void notify_change() = 0;
};

Note that the slots and watch-identifiers associated with notifiers may-or-may-not-be identical to those associated with the user interface, depending on implementation:

  • A user interface plugin that uses polling-functionality can pass slots directly to the notifier, and return watch identifiers back to the caller. It would then poll the notifier plugin periodically (in an idle- or timeout-handler, for example):
while(file_change_plugin->pending_changes())
  file_change_plugin->notify_change();
  • A user interface plugin that uses a combination of threads and blocking can pass watch identifiers from the notifier plugin back to the caller, but will have to handle thread synchronization issues, most likely by making a separation between the slots passed to it by a caller and the slots it passes to the notifier plugin. In this case, a separate thread would handle blocking event notification:
while(true)
  file_change_plugin->notify_change();

Open Issues

  • Watching for file creation is non-trivial with inotify: it errors when a non-existing file is added to the watch list. We will have to work around this by watching the parent directory, recursing until an existing directory is found.
  • If multiple file watch plugins ara available, how do we choose? Should the user be allowed to configure this somehow? On Linux, the decision is currently made at compile time. Possibly, "k3d:load-order" metadata could be used, as in k3dsdk/mime_types.cpp. In that case, should the load-order value be configurable by the user, and how?
  • The current implementation is tested by using it directly in the k3d::mesh_reader, adding a boolean property to control if the file should be watched. Maybe we should use a new path property type that also emits signals when the file was changed on disk, and that has an interface to enable/disable watching.

Implementation progress

  • Linux polling and threaded implementation, based on inotify. Done
  • Win32 implementation using ReadDirectoryChangesW
  • OS X implementation
  • Tweak implementation(s) to allow watching non-existing files