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
;
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;
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
))) ;
34 try { cancel_op (internal_fsw, fsw); }
35 catch (Exception
) { }
;
38 Monitor
.Exit (instance
);
39 if (live
&& !havelock
)
41 var t
= Task
<bool>.Run( () => { cancel_op (internal_fsw, fsw); return true;}
);
42 t
.Wait (INTERRUPT_MS
);
44 catch (Exception
) { }
;
47 if (map_op
!= null && handle
== null) {
49 try { map_op (internal_map, external_map, event_map, null); }
50 catch (Exception e
) { throw new InvalidOperationException (nameof(map_op), e); }
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)
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;
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
)
84 fsw
.DispatchEvents (action
, args
.Name
, ref renamed
);
87 System
.Threading
.Monitor
.PulseAll (fsw
);
92 protected void ProxyDispatchError (object sender
, ErrorEventArgs args
)
96 Operation (map_op
: (in_map
, out_map
, event_map
, _
) => event_map
.TryGetValue (sender
, out handle
));
98 Operation (object_op
: (_
, fsw
) => fsw
.DispatchErrorEvents (args
),
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
);
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;
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
)
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
);
169 public static bool GetInstance (out IFileWatcher watcher
)
171 if (instance
!= null) {
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 ();