Marek
Marek

Reputation: 10402

How to synchronize handling of filesystemwatcher events?

I have the following synchronization problem.

A thread runs in a loop and if it detects a folder in filesystem which was not processed yet (it may have been added while the app was not running), the folder is processed as a new folder by Process method:

void Loop()
{
 while (run)
 {
  lock(renameLock)
  {
    var newFolders = EnumerateNewFolders().ExceptThoseMarkedAsRenamed();
    Process(newFolders);
    MarkAsProcessed(newFolders);
  }
  Sleep();
 }
}

Renamed folders should not be processed as new but rather processed as renamed and marked as renamed so that they are not processed as new later.

To take care of this, I have a FileSystemWatcher and a handler for the Renamed event:

private void fsw_Renamed(object sender, RenamedEventArgs e)
{
  lock(renameLock)
  {
    ProcessRename(e);
    //mark folder as renamed so that ExceptThoseMarkedAsRenamed filters it out
    MarkAsRenamed(e);
  }
}

Usually this works and when a rename is performed, the folder is first marked as renamed in fsw_Renamed and then filtered out in the loop by ExceptThoseMarkedAsRenamed and not processed as new.

But sometimes the following order occurs:

  1. Folder is renamed in Windows Explorer and in the filesystem (while the Loop is holding lock)
  2. fsw_Renamed occurs, but cannot obtain the lock
  3. The looping thread is still holding lock and picks up the folder as new (it was not marked as renamed yet)
  4. The looping thread releases the lock
  5. Renamed event occurs, gets the lock, and folder is marked as renamed. But it is already too late since it was already processed as new.

I am having trouble figuring out how to ensure that a renamed folder is never processed as new.

A renamed folder is such folder for which the Renamed event occurred or will occur in near future, because there is an inherent delay between filesystem change and the FileSystemWatcher.Renamed event (and the actual change in filesystem and the event raised are not atomic).

How to ensure that the change done by fsw_Renamed is always taken into account by the Loop, even if the event occurred while Loop was holding the lock?

Upvotes: 1

Views: 1300

Answers (2)

didierc
didierc

Reputation: 14730

You want to process folders, with two different kinds of processing according to whether the folder is new or simply renamed. You are using an fs monitoring object which fires events when a given folder has been renamed. You should use a similar monitoring for new folders, and have these events create a task bound to the folder.

The thread catching the events wouldn't have the race condition issue, and would only dispatch new tasks, hence it would not be delayed by a processing and able to deal with new events when they occurs. The task queue could simply be a list of pairs containing the folder and the task to apply to it.

When a folder is scheduled for processing, any events happening to them should be filtered out by the monitor, by check the task queue to see if the folder is there. It could even change the task or remove it according to other factors (if the folder has been deleted before being processed for instance - perhaps it would be good to monitor that event too).

A task could be something like:

  • lock the folder to be processed (this avoid external influences on the folder)
  • process it
  • mark it as "processed" (take it out of the list)
  • unlock it

You cannot keep the renameLock, as it is the thing creating the problem in the first place. If you cannot lock the folder, perhaps you can move it (it's technically like a rename I suppose) somewhere else to do the processing, and then move it back in place.

This SO question deals with the problem of locking folders in C#.

Upvotes: 1

Panos Rontogiannis
Panos Rontogiannis

Reputation: 4172

You could try the following:

  1. Run the Loop method only at the startup of your application. Probably before subscribing to the FileSystemWatcher.
  2. Subscribe to the FileSystemWatcher.Created on the directory on which you subscribed to FileSystemWatcher.Renamed.
  3. Keep the FileSystemWatcher.Renamed subscription as is.
  4. Remove the lock since the two events should not conflict with each other.

Step 1. will handle directories created/renamed while your application is not running.

Step 2. will handle new directories while your application is running.

Step 3. will handle renamed directories while your application is running.

Upvotes: 1

Related Questions