Bug 16259 - KEvent FileSystemWatcher doesn't detect changes to subdirectories
Summary: KEvent FileSystemWatcher doesn't detect changes to subdirectories
Status: RESOLVED FIXED
Alias: None
Product: Class Libraries
Classification: Mono
Component: System ()
Version: 3.2.x
Hardware: Macintosh Mac OS
: --- major
Target Milestone: Untriaged
Assignee: Cody Russell
URL:
Depends on:
Blocks:
 
Reported: 2013-11-15 08:29 UTC by Weeble
Modified: 2014-12-03 15:06 UTC (History)
5 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Weeble 2013-11-15 08:29:35 UTC
Specifically, it doesn't detect changes to subdirectories that exist prior to the FileSystemWatcher being started:

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Reflection;

class Program
{
        public static void Main(string[] args)
        {
            var tempPath = Path.Combine(Path.GetTempPath(),
                Guid.NewGuid().ToString());
            Directory.CreateDirectory(tempPath);
            Directory.CreateDirectory(Path.Combine(tempPath, "pre-exists"));

            var fsw = new FileSystemWatcher(tempPath,"*");
            fsw.IncludeSubdirectories = true;
            fsw.NotifyFilter =
                NotifyFilters.DirectoryName |
                NotifyFilters.FileName |
                NotifyFilters.CreationTime |
                NotifyFilters.Attributes |
                NotifyFilters.LastWrite |
                NotifyFilters.Size;

            fsw.Created += FswOnChanged;
            fsw.Changed += FswOnChanged;
            fsw.Deleted += FswOnChanged;
            fsw.Renamed += FswOnChanged;
            fsw.EnableRaisingEvents = true;

            Console.WriteLine("Created watcher");

            // getting watcher type:
            FieldInfo fieldInfo = typeof(FileSystemWatcher).GetField("watcher",
                BindingFlags.Static | BindingFlags.NonPublic);
            if (fieldInfo != null)
            {
                object watcher = fieldInfo.GetValue(null);
                Console.WriteLine("watcher type: {0}", watcher.GetType());
            }

            Thread.Sleep(2000);
            Directory.CreateDirectory(Path.Combine(tempPath, "pre-exists", "subdir"));
            Thread.Sleep(2000);
            Directory.CreateDirectory(Path.Combine(tempPath, "new"));
            Thread.Sleep(2000);
            Directory.CreateDirectory(Path.Combine(tempPath, "new", "subdir"));
            Thread.Sleep(2000);

            fsw.Dispose();

            Directory.Delete(tempPath, true);

        }

        private static void FswOnChanged(object sender, FileSystemEventArgs
fileSystemEventArgs)
        {
            Console.WriteLine("Changed path: '{0}', type: {1}",
                fileSystemEventArgs.FullPath,
                fileSystemEventArgs.ChangeType);
        }
}

Output in .NET on Windows:

Created watcher
Changed path: 'C:\Users\AndrewW\AppData\Local\Temp\d220a35f-d740-408a-9e4f-58cb9da92bc9\pre-exists\subdir', type: Created
Changed path: 'C:\Users\AndrewW\AppData\Local\Temp\d220a35f-d740-408a-9e4f-58cb9da92bc9\pre-exists', type: Changed
Changed path: 'C:\Users\AndrewW\AppData\Local\Temp\d220a35f-d740-408a-9e4f-58cb9da92bc9\new', type: Created
Changed path: 'C:\Users\AndrewW\AppData\Local\Temp\d220a35f-d740-408a-9e4f-58cb9da92bc9\new\subdir', type: Created
Changed path: 'C:\Users\AndrewW\AppData\Local\Temp\d220a35f-d740-408a-9e4f-58cb9da92bc9\new', type: Changed

Output in mono 3.2.3 on OS X:

Created watcher
watcher type: System.IO.KeventWatcher
Changed path: '/tmp/f268a275-fd64-41b4-8f80-f08ce5514804/new', type: Created
Changed path: '/tmp/f268a275-fd64-41b4-8f80-f08ce5514804/new/subdir', type: Created

Output in mono 3.2.3 on Linux:

Created watcher
watcher type: System.IO.InotifyWatcher
Changed path: '/tmp/5fc78851-a300-4f88-b96e-5fc2a07697b8/pre-exists/subdir', type: Created
Changed path: '/tmp/5fc78851-a300-4f88-b96e-5fc2a07697b8/new', type: Created
Changed path: '/tmp/5fc78851-a300-4f88-b96e-5fc2a07697b8/new/subdir', type: Created

Observe that the KeventWatcher generated no event for "pre-exists/subdir".

Possibly related: https://bugzilla.xamarin.com/show_bug.cgi?id=5747
Comment 1 Weeble 2013-11-15 12:27:19 UTC
I'm trying to make sense of KeventWatcher.cs. Some bits are surprising:

https://github.com/mono/mono/blob/master/mcs/class/System/System.IO/KeventWatcher.cs#L170

Lines 170 & 171 appear to set bit-fields:

ev.flags = 1 | 4 | 20;
ev.fflags = 20 | 2 | 1 | 8;

I wonder if the author meant 0x20 rather than 20? 20 seems a very surprising value to set here.

https://github.com/mono/mono/blob/master/mcs/class/System/System.IO/KeventWatcher.cs#L181

https://github.com/mono/mono/blob/master/mcs/class/System/System.IO/KeventWatcher.cs#L199

Lines 181 and 199 are both conditional return statements at the very end of a function:

    ...
    if (!data.IncludeSubdirs)
        return;
}

That seems redundant. Perhaps something else was supposed to happen after this point in each function?
Comment 2 Miguel de Icaza [MSFT] 2014-05-13 18:03:26 UTC
I was trying to fix a bug on FileSystemWatcher and ran into exactly this issue.

The value 20 is also very suspect;   I am adding a little cleanup to the file now, that might help identify the problem
Comment 3 Cody Russell 2014-05-15 02:37:52 UTC
I started looking at this tonight, and I think I see what's going on. I'd like to continue working on this and get it fixed this weekend.
Comment 4 Cody Russell 2014-06-08 18:55:01 UTC
https://github.com/mono/mono/pull/1093
Comment 5 dev 2014-09-24 19:08:48 UTC
Is there an ETA for a solution ? This is a nasty bug.
Thanks
Comment 6 Miguel de Icaza [MSFT] 2014-09-24 21:18:37 UTC
It is being actively worked on.
Comment 7 Cody Russell 2014-11-20 10:41:06 UTC
I forgot to close this bug.. this stuff has been merged into Mono master.
Comment 8 dev 2014-11-27 11:40:27 UTC
Thank you Cody,
I have tested this using a small Xamarin.Mac project (referencing .NET 4.5.1) and the issue still appears to be present. I also tried the above code, same result. This is using the latest stable channel (Xamarin Studio 5.5.4 build 15 on OSX 10.9.5).
I assume the fix is not included in the referenced Xamarin.Mac (which shows a version of 1.10.0.18 in the references list).
So the question is, how can I ensure to use the correct reference ? I also tried referencing /Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/XamMac.dll
but had the same negative result.
Thank you
Comment 9 Cody Russell 2014-12-03 15:06:05 UTC
So, if you're using Xamarin stable channel then you probably don't have these changes yet. In fact, while they've been committed to our git repos I don't think they've been actually released yet.