Reputation: 10402
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:
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
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:
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
Reputation: 4172
You could try the following:
Loop
method only at the startup of your application. Probably before subscribing to the FileSystemWatcher
.FileSystemWatcher.Created
on the directory on which you subscribed to FileSystemWatcher.Renamed
.FileSystemWatcher.Renamed
subscription as is.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