[System.IO] Integrate FileSystemWatchers from CoreFX
[mono-project.git] / mcs / class / System / System.IO / CoreFXFileSystemWatcherProxy.cs
blob466acd1fff059e63f8cd736ca7334678afe64430
1 // Bridges the Mono and CoreFX FileSystemWatcher types.
3 using System.Collections.Generic;
4 using System.Collections.Concurrent;
5 using System.Threading;
6 using System.Threading.Tasks;
7 using System.Runtime.CompilerServices;
9 using C = System.IO.CoreFX.FileSystemWatcher;
10 using M = System.IO.FileSystemWatcher;
11 using Handle = System.Object;
13 namespace System.IO {
15 internal class CoreFXFileSystemWatcherProxy : IFileWatcher
17 static IFileWatcher instance; // Mono FSW objects -> this
18 static IDictionary<Handle, C> internal_map; // this -> CoreFX FSW objects
19 static ConditionalWeakTable<Handle, M> external_map; // this -> Mono FSW objects
20 static IDictionary<object, Handle> event_map; // CoreFX FSW events -> this
22 const int INTERRUPT_MS = 300;
24 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)
26 C internal_fsw = null;
27 M fsw = null;
28 bool live, havelock;
30 if (cancel_op != null) { // highest priority and must not lock
31 havelock = Monitor.TryEnter (instance, INTERRUPT_MS);
32 live = (handle != null && (internal_map.TryGetValue (handle, out internal_fsw) || external_map.TryGetValue (handle, out fsw))) ;
33 if (live && havelock)
34 try { cancel_op (internal_fsw, fsw); }
35 catch (Exception) { };
37 if (havelock)
38 Monitor.Exit (instance);
39 if (live && !havelock)
40 try {
41 var t = Task<bool>.Run( () => { cancel_op (internal_fsw, fsw); return true;});
42 t.Wait (INTERRUPT_MS);
44 catch (Exception) { };
45 return;
47 if (map_op != null && handle == null) {
48 lock (instance) {
49 try { map_op (internal_map, external_map, event_map, null); }
50 catch (Exception e) { throw new InvalidOperationException (nameof(map_op), e); }
52 return;
55 if (handle == null)
56 return;
58 lock (instance) {
59 live = (internal_map.TryGetValue (handle, out internal_fsw) && external_map.TryGetValue (handle, out fsw)) ;
60 if (live && map_op != null) {
61 try { map_op (internal_map, external_map, event_map, handle); }
62 catch (Exception e) { throw new InvalidOperationException (nameof(map_op), e); };
65 if (!live || object_op == null)
66 return;
68 try { object_op (internal_fsw, fsw); }
69 catch (Exception e) { throw new InvalidOperationException (nameof(object_op), e); };
72 protected void ProxyDispatch (object sender, FileAction action, FileSystemEventArgs args)
74 RenamedEventArgs renamed =
75 action == FileAction.RenamedNewName ? (RenamedEventArgs) args : null;
77 object handle = null;
79 Operation (map_op: (in_map, out_map, event_map, h) => event_map.TryGetValue (sender, out handle));
81 Operation (object_op: (_, fsw) => {
82 if (!fsw.EnableRaisingEvents)
83 return;
84 fsw.DispatchEvents (action, args.Name, ref renamed);
85 if (fsw.Waiting) {
86 fsw.Waiting = false;
87 System.Threading.Monitor.PulseAll (fsw);
89 }, handle: handle);
92 protected void ProxyDispatchError (object sender, ErrorEventArgs args)
94 object handle = null;
96 Operation (map_op: (in_map, out_map, event_map, _) => event_map.TryGetValue (sender, out handle));
98 Operation (object_op: (_, fsw) => fsw.DispatchErrorEvents (args),
99 handle: handle);
102 public object NewWatcher (M fsw)
104 var handle = new object ();
105 var result = new C ();
107 result.Changed += (object o, FileSystemEventArgs args) =>
108 { ProxyDispatch (o, FileAction.Modified, args); };
109 result.Created += (object o, FileSystemEventArgs args) =>
110 { ProxyDispatch (o, FileAction.Added, args); };
111 result.Deleted += (object o, FileSystemEventArgs args) =>
112 { ProxyDispatch (o, FileAction.Removed, args); };
113 result.Renamed += (object o, RenamedEventArgs args) =>
114 { ProxyDispatch (o, FileAction.RenamedNewName, args); };
116 result.Error += (object o, ErrorEventArgs args) =>
117 { ProxyDispatchError (handle, args); };
119 Operation (map_op: (in_map, out_map, event_map, _) => {
120 in_map.Add (handle, result);
121 out_map.Add (handle, fsw);
122 event_map.Add (result, handle);
125 return handle;
128 public void StartDispatching (object handle)
130 Operation (object_op: (internal_fsw, fsw) => {
131 internal_fsw.Path = fsw.Path;
132 internal_fsw.Filter = fsw.Filter;
133 internal_fsw.IncludeSubdirectories = fsw.IncludeSubdirectories;
134 internal_fsw.InternalBufferSize = fsw.InternalBufferSize;
135 internal_fsw.NotifyFilter = fsw.NotifyFilter;
136 internal_fsw.Site = fsw.Site;
137 internal_fsw.EnableRaisingEvents = true;
138 }, handle: handle);
141 public void StopDispatching (object handle)
143 Operation (handle: handle,
144 cancel_op: (internal_fsw, fsw) =>
146 if (internal_fsw != null)
147 internal_fsw.EnableRaisingEvents = false;
152 public void Dispose (object handle)
154 if (handle == null)
155 return;
157 Operation (handle: handle,
158 cancel_op: (internal_fsw, fsw) => {
159 if (internal_fsw != null)
160 internal_fsw.Dispose ();
161 var inner_key = internal_map [handle];
162 internal_map.Remove (handle);
163 external_map.Remove (handle);
164 event_map.Remove (inner_key);
165 handle = null;
169 public static bool GetInstance (out IFileWatcher watcher)
171 if (instance != null) {
172 watcher = instance;
173 return true;
176 internal_map = new ConcurrentDictionary <object, C> ();
177 external_map = new ConditionalWeakTable <object, M> ();
178 event_map = new ConcurrentDictionary <object, object> ();
179 instance = watcher = new CoreFXFileSystemWatcherProxy ();
180 return true;