diff options
author | Alexander Köplinger <alex.koeplinger@outlook.com> | 2018-02-28 15:42:15 +0300 |
---|---|---|
committer | Alexander Köplinger <alex.koeplinger@outlook.com> | 2018-02-28 15:42:15 +0300 |
commit | cb956b58a6a5a84bcb2acc2302ad60fba01c0e16 (patch) | |
tree | 47fee96acef1664e9f22df3f4be25e1cb16efc9a /mcs/class/System/System.IO | |
parent | fdefcf1f25b38fca764dd154716fbd3b83ed72c0 (diff) |
Revert "Revert "[System.IO] Integrate FileSystemWatchers from CoreFX The FSEvent CoreFX watcher becomes the new default for MacOS.""
This reverts commit 2174a521a3e45f6ce4db967a53edcc1ee2dd0433.
Diffstat (limited to 'mcs/class/System/System.IO')
-rw-r--r-- | mcs/class/System/System.IO/CoreFXFileSystemWatcherProxy.cs | 183 | ||||
-rw-r--r-- | mcs/class/System/System.IO/DefaultWatcher.cs | 11 | ||||
-rw-r--r-- | mcs/class/System/System.IO/FAMWatcher.cs | 10 | ||||
-rw-r--r-- | mcs/class/System/System.IO/FileSystemWatcher.cs | 100 | ||||
-rw-r--r-- | mcs/class/System/System.IO/IFileWatcher.cs | 5 | ||||
-rw-r--r-- | mcs/class/System/System.IO/InotifyWatcher.cs | 11 | ||||
-rw-r--r-- | mcs/class/System/System.IO/KeventWatcher.cs | 11 | ||||
-rw-r--r-- | mcs/class/System/System.IO/NullFileWatcher.cs | 9 | ||||
-rw-r--r-- | mcs/class/System/System.IO/WaitForChangedResult.cs | 69 | ||||
-rw-r--r-- | mcs/class/System/System.IO/WindowsWatcher.cs | 53 |
10 files changed, 304 insertions, 158 deletions
diff --git a/mcs/class/System/System.IO/CoreFXFileSystemWatcherProxy.cs b/mcs/class/System/System.IO/CoreFXFileSystemWatcherProxy.cs new file mode 100644 index 00000000000..466acd1fff0 --- /dev/null +++ b/mcs/class/System/System.IO/CoreFXFileSystemWatcherProxy.cs @@ -0,0 +1,183 @@ +// Bridges the Mono and CoreFX FileSystemWatcher types. + +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.CompilerServices; + +using C = System.IO.CoreFX.FileSystemWatcher; +using M = System.IO.FileSystemWatcher; +using Handle = System.Object; + +namespace System.IO { + + internal class CoreFXFileSystemWatcherProxy : IFileWatcher + { + static IFileWatcher instance; // Mono FSW objects -> this + static IDictionary<Handle, C> internal_map; // this -> CoreFX FSW objects + static ConditionalWeakTable<Handle, M> external_map; // this -> Mono FSW objects + static IDictionary<object, Handle> event_map; // CoreFX FSW events -> this + + const int INTERRUPT_MS = 300; + + protected void Operation (Action<IDictionary<object, C>, ConditionalWeakTable<object, M>, IDictionary<object, object>, Handle> map_op = null, Action<C, M> object_op = null, object handle = null, Action<C, M> cancel_op = null) + { + C internal_fsw = null; + M fsw = null; + bool live, havelock; + + if (cancel_op != null) { // highest priority and must not lock + havelock = Monitor.TryEnter (instance, INTERRUPT_MS); + live = (handle != null && (internal_map.TryGetValue (handle, out internal_fsw) || external_map.TryGetValue (handle, out fsw))) ; + if (live && havelock) + try { cancel_op (internal_fsw, fsw); } + catch (Exception) { }; + + if (havelock) + Monitor.Exit (instance); + if (live && !havelock) + try { + var t = Task<bool>.Run( () => { cancel_op (internal_fsw, fsw); return true;}); + t.Wait (INTERRUPT_MS); + } + catch (Exception) { }; + return; + } + if (map_op != null && handle == null) { + lock (instance) { + try { map_op (internal_map, external_map, event_map, null); } + catch (Exception e) { throw new InvalidOperationException (nameof(map_op), e); } + } + return; + } + + if (handle == null) + return; + + lock (instance) { + live = (internal_map.TryGetValue (handle, out internal_fsw) && external_map.TryGetValue (handle, out fsw)) ; + if (live && map_op != null) { + try { map_op (internal_map, external_map, event_map, handle); } + catch (Exception e) { throw new InvalidOperationException (nameof(map_op), e); }; + } + } + if (!live || object_op == null) + return; + + try { object_op (internal_fsw, fsw); } + catch (Exception e) { throw new InvalidOperationException (nameof(object_op), e); }; + } + + protected void ProxyDispatch (object sender, FileAction action, FileSystemEventArgs args) + { + RenamedEventArgs renamed = + action == FileAction.RenamedNewName ? (RenamedEventArgs) args : null; + + object handle = null; + + Operation (map_op: (in_map, out_map, event_map, h) => event_map.TryGetValue (sender, out handle)); + + Operation (object_op: (_, fsw) => { + if (!fsw.EnableRaisingEvents) + return; + fsw.DispatchEvents (action, args.Name, ref renamed); + if (fsw.Waiting) { + fsw.Waiting = false; + System.Threading.Monitor.PulseAll (fsw); + } + }, handle: handle); + } + + protected void ProxyDispatchError (object sender, ErrorEventArgs args) + { + object handle = null; + + Operation (map_op: (in_map, out_map, event_map, _) => event_map.TryGetValue (sender, out handle)); + + Operation (object_op: (_, fsw) => fsw.DispatchErrorEvents (args), + handle: handle); + } + + public object NewWatcher (M fsw) + { + var handle = new object (); + var result = new C (); + + result.Changed += (object o, FileSystemEventArgs args) => + { ProxyDispatch (o, FileAction.Modified, args); }; + result.Created += (object o, FileSystemEventArgs args) => + { ProxyDispatch (o, FileAction.Added, args); }; + result.Deleted += (object o, FileSystemEventArgs args) => + { ProxyDispatch (o, FileAction.Removed, args); }; + result.Renamed += (object o, RenamedEventArgs args) => + { ProxyDispatch (o, FileAction.RenamedNewName, args); }; + + result.Error += (object o, ErrorEventArgs args) => + { ProxyDispatchError (handle, args); }; + + Operation (map_op: (in_map, out_map, event_map, _) => { + in_map.Add (handle, result); + out_map.Add (handle, fsw); + event_map.Add (result, handle); + }); + + return handle; + } + + public void StartDispatching (object handle) + { + Operation (object_op: (internal_fsw, fsw) => { + internal_fsw.Path = fsw.Path; + internal_fsw.Filter = fsw.Filter; + internal_fsw.IncludeSubdirectories = fsw.IncludeSubdirectories; + internal_fsw.InternalBufferSize = fsw.InternalBufferSize; + internal_fsw.NotifyFilter = fsw.NotifyFilter; + internal_fsw.Site = fsw.Site; + internal_fsw.EnableRaisingEvents = true; + }, handle: handle); + } + + public void StopDispatching (object handle) + { + Operation (handle: handle, + cancel_op: (internal_fsw, fsw) => + { + if (internal_fsw != null) + internal_fsw.EnableRaisingEvents = false; + + }); + } + + public void Dispose (object handle) + { + if (handle == null) + return; + + Operation (handle: handle, + cancel_op: (internal_fsw, fsw) => { + if (internal_fsw != null) + internal_fsw.Dispose (); + var inner_key = internal_map [handle]; + internal_map.Remove (handle); + external_map.Remove (handle); + event_map.Remove (inner_key); + handle = null; + }); + } + + public static bool GetInstance (out IFileWatcher watcher) + { + if (instance != null) { + watcher = instance; + return true; + } + + internal_map = new ConcurrentDictionary <object, C> (); + external_map = new ConditionalWeakTable <object, M> (); + event_map = new ConcurrentDictionary <object, object> (); + instance = watcher = new CoreFXFileSystemWatcherProxy (); + return true; + } + } +} diff --git a/mcs/class/System/System.IO/DefaultWatcher.cs b/mcs/class/System/System.IO/DefaultWatcher.cs index cbda2b294aa..28186f8451b 100644 --- a/mcs/class/System/System.IO/DefaultWatcher.cs +++ b/mcs/class/System/System.IO/DefaultWatcher.cs @@ -79,8 +79,9 @@ namespace System.IO { return true; } - public void StartDispatching (FileSystemWatcher fsw) + public void StartDispatching (object handle) { + var fsw = handle as FileSystemWatcher; DefaultWatcherData data; lock (this) { if (watches == null) @@ -116,8 +117,9 @@ namespace System.IO { } } - public void StopDispatching (FileSystemWatcher fsw) + public void StopDispatching (object handle) { + var fsw = handle as FileSystemWatcher; DefaultWatcherData data; lock (this) { if (watches == null) return; @@ -132,6 +134,11 @@ namespace System.IO { } } + public void Dispose (object handle) + { + // does nothing + } + void Monitor () { diff --git a/mcs/class/System/System.IO/FAMWatcher.cs b/mcs/class/System/System.IO/FAMWatcher.cs index 63faf5fed7c..d0ee6526ee5 100644 --- a/mcs/class/System/System.IO/FAMWatcher.cs +++ b/mcs/class/System/System.IO/FAMWatcher.cs @@ -110,8 +110,9 @@ namespace System.IO { return true; } - public void StartDispatching (FileSystemWatcher fsw) + public void StartDispatching (object handle) { + var fsw = handle as FileSystemWatcher; FAMData data; lock (this) { if (thread == null) { @@ -199,8 +200,9 @@ namespace System.IO { } } - public void StopDispatching (FileSystemWatcher fsw) + public void StopDispatching (object handle) { + var fsw = handle as FileSystemWatcher; FAMData data; lock (this) { data = (FAMData) watches [fsw]; @@ -408,6 +410,10 @@ namespace System.IO { return fam_Pending (ref fc); } + public void Dispose (object handle) + { + // does nothing + } [DllImport ("libfam.so.0", EntryPoint="FAMOpen")] diff --git a/mcs/class/System/System.IO/FileSystemWatcher.cs b/mcs/class/System/System.IO/FileSystemWatcher.cs index 53e31d61f7e..c27900524d3 100644 --- a/mcs/class/System/System.IO/FileSystemWatcher.cs +++ b/mcs/class/System/System.IO/FileSystemWatcher.cs @@ -45,6 +45,8 @@ namespace System.IO { #region Fields + bool inited; + bool start_requested; bool enableRaisingEvents; string filter; bool includeSubdirectories; @@ -59,6 +61,7 @@ namespace System.IO { bool disposed; string mangledFilter; static IFileWatcher watcher; + object watcher_handle; static object lockobj = new object (); #endregion // Fields @@ -95,6 +98,8 @@ namespace System.IO { if (!Directory.Exists (path)) throw new ArgumentException ("Directory does not exist", "path"); + this.inited = false; + this.start_requested = false; this.enableRaisingEvents = false; this.filter = filter; this.includeSubdirectories = false; @@ -121,28 +126,39 @@ namespace System.IO { switch (mode) { case 1: // windows ok = DefaultWatcher.GetInstance (out watcher); - //ok = WindowsWatcher.GetInstance (out watcher); + watcher_handle = this; break; case 2: // libfam ok = FAMWatcher.GetInstance (out watcher, false); + watcher_handle = this; break; case 3: // kevent ok = KeventWatcher.GetInstance (out watcher); + watcher_handle = this; break; case 4: // libgamin ok = FAMWatcher.GetInstance (out watcher, true); + watcher_handle = this; break; case 5: // inotify ok = InotifyWatcher.GetInstance (out watcher, true); + watcher_handle = this; + break; + case 6: // CoreFX + ok = CoreFXFileSystemWatcherProxy.GetInstance (out watcher); + watcher_handle = (watcher as CoreFXFileSystemWatcherProxy).NewWatcher (this); break; } if (mode == 0 || !ok) { if (String.Compare (managed, "disabled", true) == 0) NullFileWatcher.GetInstance (out watcher); - else + else { DefaultWatcher.GetInstance (out watcher); + watcher_handle = this; + } } + this.inited = true; ShowWatcherInfo (); } @@ -173,9 +189,6 @@ namespace System.IO { return mangledFilter; string filterLocal = "*.*"; - if (!(watcher.GetType () == typeof (WindowsWatcher))) - filterLocal = "*"; - return filterLocal; } } @@ -210,6 +223,12 @@ namespace System.IO { public bool EnableRaisingEvents { get { return enableRaisingEvents; } set { + if (disposed) + throw new ObjectDisposedException (GetType().Name); + + start_requested = true; + if (!inited) + return; if (value == enableRaisingEvents) return; // Do nothing @@ -218,6 +237,7 @@ namespace System.IO { Start (); } else { Stop (); + start_requested = false; } } } @@ -232,7 +252,7 @@ namespace System.IO { if (value == null || value == "") value = "*.*"; - if (filter != value) { + if (!string.Equals(filter, value, PathInternal.StringComparison)) { filter = value; pattern = null; mangledFilter = null; @@ -264,8 +284,8 @@ namespace System.IO { if (internalBufferSize == value) return; - if (value < 4196) - value = 4196; + if (value < 4096) + value = 4096; internalBufferSize = value; if (enableRaisingEvents) { @@ -299,7 +319,11 @@ namespace System.IO { public string Path { get { return path; } set { - if (path == value) + if (disposed) + throw new ObjectDisposedException (GetType().Name); + + value = (value == null) ? string.Empty : value; + if (string.Equals(path, value, PathInternal.StringComparison)) return; bool exists = false; @@ -312,10 +336,10 @@ namespace System.IO { } if (exc != null) - throw new ArgumentException ("Invalid directory name", "value", exc); + throw new ArgumentException(SR.Format(SR.InvalidDirName, value), nameof(Path)); if (!exists) - throw new ArgumentException ("Directory does not exist", "value"); + throw new ArgumentException(SR.Format(SR.InvalidDirName_NotExists, value), nameof(Path)); path = value; fullpath = null; @@ -329,7 +353,12 @@ namespace System.IO { [Browsable(false)] public override ISite Site { get { return base.Site; } - set { base.Site = value; } + set + { + base.Site = value; + if (Site != null && Site.DesignMode) + this.EnableRaisingEvents = true; + } } [DefaultValue(null)] @@ -347,27 +376,41 @@ namespace System.IO { public void BeginInit () { // Not necessary in Mono + // but if called, EndInit() must be called + inited = false; } protected override void Dispose (bool disposing) { - if (!disposed) { - disposed = true; - Stop (); - } + if (disposed) + return; + + try { + watcher.StopDispatching (watcher_handle); + watcher.Dispose (watcher_handle); + } catch (Exception) { } + watcher_handle = null; + watcher = null; + + disposed = true; base.Dispose (disposing); + GC.SuppressFinalize (this); } ~FileSystemWatcher () { - disposed = true; - Stop (); + if (disposed) + return; + + Dispose (false); } public void EndInit () { - // Not necessary in Mono + inited = true; + if (start_requested) + this.EnableRaisingEvents = true; } enum EventType { @@ -384,13 +427,13 @@ namespace System.IO { foreach (var target in ev.GetInvocationList()) { switch (evtype) { case EventType.RenameEvent: - ((RenamedEventHandler)target).BeginInvoke (this, (RenamedEventArgs)arg, null, null); + ((RenamedEventHandler)target).Invoke (this, (RenamedEventArgs)arg); break; case EventType.ErrorEvent: - ((ErrorEventHandler)target).BeginInvoke (this, (ErrorEventArgs)arg, null, null); + ((ErrorEventHandler)target).Invoke (this, (ErrorEventArgs)arg); break; case EventType.FileSystemEvent: - ((FileSystemEventHandler)target).BeginInvoke (this, (FileSystemEventArgs)arg, null, null); + ((FileSystemEventHandler)target).Invoke (this, (FileSystemEventArgs)arg); break; } } @@ -503,12 +546,20 @@ namespace System.IO { void Start () { - watcher.StartDispatching (this); + if (disposed) + return; + if (watcher_handle == null) + return; + watcher.StartDispatching (watcher_handle); } void Stop () { - watcher.StopDispatching (this); + if (disposed) + return; + if (watcher_handle == null) + return; + watcher.StopDispatching (watcher_handle); } #endregion // Methods @@ -537,6 +588,7 @@ namespace System.IO { /* 3 -> Kevent */ /* 4 -> gamin */ /* 5 -> inotify */ + /* 6 -> CoreFX */ [MethodImplAttribute(MethodImplOptions.InternalCall)] static extern int InternalSupportsFSW (); } diff --git a/mcs/class/System/System.IO/IFileWatcher.cs b/mcs/class/System/System.IO/IFileWatcher.cs index 7ed4878fa6d..d7e6055a1d5 100644 --- a/mcs/class/System/System.IO/IFileWatcher.cs +++ b/mcs/class/System/System.IO/IFileWatcher.cs @@ -30,8 +30,9 @@ namespace System.IO { interface IFileWatcher { - void StartDispatching (FileSystemWatcher fsw); - void StopDispatching (FileSystemWatcher fsw); + void StartDispatching (object fsw); + void StopDispatching (object fsw); + void Dispose (object fsw); } } diff --git a/mcs/class/System/System.IO/InotifyWatcher.cs b/mcs/class/System/System.IO/InotifyWatcher.cs index fe023429a0c..8668533ff83 100644 --- a/mcs/class/System/System.IO/InotifyWatcher.cs +++ b/mcs/class/System/System.IO/InotifyWatcher.cs @@ -133,8 +133,9 @@ namespace System.IO { return true; } - public void StartDispatching (FileSystemWatcher fsw) + public void StartDispatching (object handle) { + var fsw = handle as FileSystemWatcher; ParentInotifyData parent; lock (this) { if ((long) FD == -1) @@ -328,8 +329,9 @@ namespace System.IO { } } - public void StopDispatching (FileSystemWatcher fsw) + public void StopDispatching (object handle) { + var fsw = handle as FileSystemWatcher; ParentInotifyData parent; lock (this) { parent = (ParentInotifyData) watches [fsw]; @@ -608,6 +610,11 @@ namespace System.IO { return AddWatch (fd, directory, mask); } + public void Dispose (object handle) + { + // does nothing + } + [DllImport ("libc", EntryPoint="close")] internal extern static int Close (IntPtr fd); diff --git a/mcs/class/System/System.IO/KeventWatcher.cs b/mcs/class/System/System.IO/KeventWatcher.cs index 778671764ea..5a1984f7099 100644 --- a/mcs/class/System/System.IO/KeventWatcher.cs +++ b/mcs/class/System/System.IO/KeventWatcher.cs @@ -736,8 +736,9 @@ namespace System.IO { return true; } - public void StartDispatching (FileSystemWatcher fsw) + public void StartDispatching (object handle) { + var fsw = handle as FileSystemWatcher; KqueueMonitor monitor; if (watches.ContainsKey (fsw)) { @@ -750,14 +751,20 @@ namespace System.IO { monitor.Start (); } - public void StopDispatching (FileSystemWatcher fsw) + public void StopDispatching (object handle) { + var fsw = handle as FileSystemWatcher; KqueueMonitor monitor = (KqueueMonitor)watches [fsw]; if (monitor == null) return; monitor.Stop (); } + + public void Dispose (object handle) + { + // does nothing + } [DllImport ("libc")] extern static int close (int fd); diff --git a/mcs/class/System/System.IO/NullFileWatcher.cs b/mcs/class/System/System.IO/NullFileWatcher.cs index 33f314f55b8..ea1cbccfaee 100644 --- a/mcs/class/System/System.IO/NullFileWatcher.cs +++ b/mcs/class/System/System.IO/NullFileWatcher.cs @@ -35,12 +35,17 @@ namespace System.IO { static IFileWatcher instance; - public void StartDispatching (FileSystemWatcher fsw) + public void StartDispatching (object handle) { // does nothing } - public void StopDispatching (FileSystemWatcher fsw) + public void StopDispatching (object handle) + { + // does nothing + } + + public void Dispose (object handle) { // does nothing } diff --git a/mcs/class/System/System.IO/WaitForChangedResult.cs b/mcs/class/System/System.IO/WaitForChangedResult.cs deleted file mode 100644 index 7a6765b1e07..00000000000 --- a/mcs/class/System/System.IO/WaitForChangedResult.cs +++ /dev/null @@ -1,69 +0,0 @@ -//
-// System.IO.WaitForChangedResult.cs
-//
-// Author:
-// Tim Coleman (tim@timcoleman.com)
-//
-// Copyright (C) Tim Coleman, 2002
-//
- -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -
-using System;
-
-namespace System.IO {
- public struct WaitForChangedResult {
-
- #region Fields
-
- WatcherChangeTypes changeType;
- string name;
- string oldName;
- bool timedOut;
-
- #endregion // Fields
-
- #region Properties
-
- public WatcherChangeTypes ChangeType {
- get { return changeType; }
- set { changeType = value; }
- }
-
- public string Name {
- get { return name; }
- set { name = value; }
- }
-
- public string OldName {
- get { return oldName; }
- set { oldName = value; }
- }
-
- public bool TimedOut {
- get { return timedOut; }
- set { timedOut = value; }
- }
-
- #endregion // Properties
- }
-}
diff --git a/mcs/class/System/System.IO/WindowsWatcher.cs b/mcs/class/System/System.IO/WindowsWatcher.cs deleted file mode 100644 index dc5e848f13d..00000000000 --- a/mcs/class/System/System.IO/WindowsWatcher.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// System.IO.WindowsWatcher.cs: windows IFileWatcher -// -// Authors: -// Gonzalo Paniagua Javier (gonzalo@ximian.com) -// -// (c) 2004 Novell, Inc. (http://www.novell.com) -// - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -namespace System.IO { - class WindowsWatcher : IFileWatcher - { - private WindowsWatcher () - { - } - - // Locked by caller - public static bool GetInstance (out IFileWatcher watcher) - { - throw new NotSupportedException (); - } - - public void StartDispatching (FileSystemWatcher fsw) - { - } - - public void StopDispatching (FileSystemWatcher fsw) - { - } - } -} - |