[runtime] Fix test_op_il_seq_point in amd64.
[mono.git] / mcs / class / System / System.IO / FAMWatcher.cs
1 // 
2 // System.IO.FAM.cs: interface with libfam
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (c) 2004 Novell, Inc. (http://www.novell.com)
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36 using System.Text;
37 using System.Threading;
38
39 namespace System.IO {
40         struct FAMConnection {
41                 public int FD;
42                 public IntPtr opaque;
43         }
44
45         struct FAMRequest {
46                 public int ReqNum;
47         }
48
49         enum FAMCodes {
50                 Changed = 1,
51                 Deleted = 2,
52                 StartExecuting = 3, 
53                 StopExecuting = 4,
54                 Created = 5,
55                 Moved = 6, 
56                 Acknowledge = 7,
57                 Exists = 8,
58                 EndExist = 9
59         };
60
61         class FAMData {
62                 public FileSystemWatcher FSW;
63                 public string Directory;
64                 public string FileMask;
65                 public bool IncludeSubdirs;
66                 public bool Enabled;
67                 public FAMRequest Request;
68                 public Hashtable SubDirs;
69         }
70
71         class FAMWatcher : IFileWatcher
72         {
73                 static bool failed;
74                 static FAMWatcher instance;
75                 static Hashtable watches;
76                 static Hashtable requests;
77                 static FAMConnection conn;
78                 static Thread thread;
79                 static bool stop;
80                 static bool use_gamin;
81                 
82                 private FAMWatcher ()
83                 {
84                 }
85                 
86                 // Locked by caller
87                 public static bool GetInstance (out IFileWatcher watcher, bool gamin)
88                 {
89                         if (failed == true) {
90                                 watcher = null;
91                                 return false;
92                         }
93
94                         if (instance != null) {
95                                 watcher = instance;
96                                 return true;
97                         }
98
99                         use_gamin = gamin;
100                         watches = Hashtable.Synchronized (new Hashtable ());
101                         requests = Hashtable.Synchronized (new Hashtable ());
102                         if (FAMOpen (out conn) == -1) {
103                                 failed = true;
104                                 watcher = null;
105                                 return false;
106                         }
107
108                         instance = new FAMWatcher ();
109                         watcher = instance;
110                         return true;
111                 }
112                 
113                 public void StartDispatching (FileSystemWatcher fsw)
114                 {
115                         FAMData data;
116                         lock (this) {
117                                 if (thread == null) {
118                                         thread = new Thread (new ThreadStart (Monitor));
119                                         thread.IsBackground = true;
120                                         thread.Start ();
121                                 }
122
123                                 data = (FAMData) watches [fsw];
124                         }
125
126                         if (data == null) {
127                                 data = new FAMData ();
128                                 data.FSW = fsw;
129                                 data.Directory = fsw.FullPath;
130                                 data.FileMask = fsw.MangledFilter;
131                                 data.IncludeSubdirs = fsw.IncludeSubdirectories;
132                                 if (data.IncludeSubdirs)
133                                         data.SubDirs = new Hashtable ();
134
135                                 data.Enabled = true;
136                                 StartMonitoringDirectory (data, false);
137                                 lock (this) {
138                                         watches [fsw] = data;
139                                         requests [data.Request.ReqNum] = data;
140                                         stop = false;
141                                 }
142                         }
143                 }
144
145                 static void StartMonitoringDirectory (FAMData data, bool justcreated)
146                 {
147                         FAMRequest fr;
148                         FileSystemWatcher fsw;
149                         if (FAMMonitorDirectory (ref conn, data.Directory, out fr, IntPtr.Zero) == -1)
150                                 throw new Win32Exception ();
151
152                         fsw = data.FSW;
153                         data.Request = fr;
154
155                         if (data.IncludeSubdirs) {
156                                 foreach (string directory in Directory.GetDirectories (data.Directory)) {
157                                         FAMData fd = new FAMData ();
158                                         fd.FSW = data.FSW;
159                                         fd.Directory = directory;
160                                         fd.FileMask = data.FSW.MangledFilter;
161                                         fd.IncludeSubdirs = true;
162                                         fd.SubDirs = new Hashtable ();
163                                         fd.Enabled = true;
164
165                                         if (justcreated) {
166                                                 lock (fsw) {
167                                                         RenamedEventArgs renamed = null;
168
169                                                         fsw.DispatchEvents (FileAction.Added, directory, ref renamed);
170
171                                                         if (fsw.Waiting) {
172                                                                 fsw.Waiting = false;
173                                                                 System.Threading.Monitor.PulseAll (fsw);
174                                                         }
175                                                 }
176                                         }
177
178                                         StartMonitoringDirectory (fd, justcreated);
179                                         data.SubDirs [directory] = fd;
180                                         requests [fd.Request.ReqNum] = fd;
181                                 }
182                         }
183
184                         if (justcreated) {
185                                 foreach (string filename in Directory.GetFiles(data.Directory)) {
186                                         lock (fsw) {
187                                                 RenamedEventArgs renamed = null;
188
189                                                 fsw.DispatchEvents (FileAction.Added, filename, ref renamed);
190                                                 /* If a file has been created, then it has been written to */
191                                                 fsw.DispatchEvents (FileAction.Modified, filename, ref renamed);
192
193                                                 if (fsw.Waiting) {
194                                                         fsw.Waiting = false;
195                                                         System.Threading.Monitor.PulseAll(fsw);
196                                                 }
197                                         }
198                                 }
199                         }
200                 }
201
202                 public void StopDispatching (FileSystemWatcher fsw)
203                 {
204                         FAMData data;
205                         lock (this) {
206                                 data = (FAMData) watches [fsw];
207                                 if (data == null)
208                                         return;
209
210                                 StopMonitoringDirectory (data);
211                                 watches.Remove (fsw);
212                                 requests.Remove (data.Request.ReqNum);
213                                 if (watches.Count == 0)
214                                         stop = true;
215
216                                 if (!data.IncludeSubdirs)
217                                         return;
218
219                                 foreach (FAMData fd in data.SubDirs.Values) {
220                                         StopMonitoringDirectory (fd);
221                                         requests.Remove (fd.Request.ReqNum);
222                                 }
223                         }
224                 }
225
226                 static void StopMonitoringDirectory (FAMData data)
227                 {
228                         if (FAMCancelMonitor (ref conn, ref data.Request) == -1)
229                                 throw new Win32Exception ();
230                 }
231
232                 void Monitor ()
233                 {
234                         while (!stop) {
235                                 int haveEvents;
236                                 lock (this) {
237                                         haveEvents = FAMPending (ref conn);
238                                 }
239
240                                 if (haveEvents > 0) {
241                                         ProcessEvents ();
242                                 } else {
243                                         Thread.Sleep (500);
244                                 }
245                         }
246
247                         lock (this) {
248                                 thread = null;
249                                 stop = false;
250                         }
251                 }
252
253                 const NotifyFilters changed =   NotifyFilters.Attributes |
254                                                 NotifyFilters.LastAccess |
255                                                 NotifyFilters.Size      |
256                                                 NotifyFilters.LastWrite;
257
258                 void ProcessEvents ()
259                 {
260                         ArrayList newdirs = null;
261                         lock (this) {
262                                 do {
263                                         int code;
264                                         string filename;
265                                         int requestNumber;
266                                         FileSystemWatcher fsw;
267
268                                         if (InternalFAMNextEvent (ref conn, out filename,
269                                                                   out code, out requestNumber) != 1)
270                                                 return;
271
272                                         bool found = false;
273                                         switch ((FAMCodes) code) {
274                                         case FAMCodes.Changed:
275                                         case FAMCodes.Deleted:
276                                         case FAMCodes.Created:
277                                                 found = requests.ContainsKey (requestNumber);
278                                                 break;
279                                         case FAMCodes.Moved:
280                                         case FAMCodes.StartExecuting:
281                                         case FAMCodes.StopExecuting:
282                                         case FAMCodes.Acknowledge:
283                                         case FAMCodes.Exists:
284                                         case FAMCodes.EndExist:
285                                         default:
286                                                 found = false;
287                                                 break;
288                                         }
289
290                                         if (!found)
291                                                 continue;
292                                         
293                                         FAMData data = (FAMData) requests [requestNumber];
294                                         if (!data.Enabled)
295                                                 continue;
296
297                                         fsw = data.FSW;
298                                         NotifyFilters flt = fsw.NotifyFilter;
299                                         RenamedEventArgs renamed = null;
300                                         FileAction fa = 0;
301                                         if (code == (int) FAMCodes.Changed && (flt & changed) != 0)
302                                                 fa = FileAction.Modified;
303                                         else if (code == (int) FAMCodes.Deleted)
304                                                 fa = FileAction.Removed;
305                                         else if (code == (int) FAMCodes.Created)
306                                                 fa = FileAction.Added;
307
308                                         if (fa == 0)
309                                                 continue;
310
311                                         if (fsw.IncludeSubdirectories) {
312                                                 string full = fsw.FullPath;
313                                                 string datadir = data.Directory;
314                                                 if (datadir != full) {
315                                                         int len = full.Length;
316                                                         int slash = 1;
317                                                         if (len > 1 && full [len - 1] == Path.DirectorySeparatorChar)
318                                                                 slash = 0;
319                                                         string reldir = datadir.Substring (full.Length + slash);
320                                                         datadir = Path.Combine (datadir, filename);
321                                                         filename = Path.Combine (reldir, filename);
322                                                 } else {
323                                                         datadir = Path.Combine (full, filename);
324                                                 }
325
326                                                 if (fa == FileAction.Added && Directory.Exists (datadir)) {
327                                                         if (newdirs == null)
328                                                                 newdirs = new ArrayList (4);
329
330                                                         FAMData fd = new FAMData ();
331                                                         fd.FSW = fsw;
332                                                         fd.Directory = datadir;
333                                                         fd.FileMask = fsw.MangledFilter;
334                                                         fd.IncludeSubdirs = true;
335                                                         fd.SubDirs = new Hashtable ();
336                                                         fd.Enabled = true;
337                                                         newdirs.Add (fd);
338                                                         newdirs.Add (data);
339                                                 }
340                                         }
341
342                                         if (filename != data.Directory && !fsw.Pattern.IsMatch (filename))
343                                                 continue;
344
345                                         lock (fsw) {
346                                                 fsw.DispatchEvents (fa, filename, ref renamed);
347                                                 if (fsw.Waiting) {
348                                                         fsw.Waiting = false;
349                                                         System.Threading.Monitor.PulseAll (fsw);
350                                                 }
351                                         }
352                                 } while (FAMPending (ref conn) > 0);
353                         }
354
355
356                         if (newdirs != null) {
357                                 int count = newdirs.Count;
358                                 for (int n = 0; n < count; n += 2) {
359                                         FAMData newdir = (FAMData) newdirs [n];
360                                         FAMData parent = (FAMData) newdirs [n + 1];
361                                         StartMonitoringDirectory (newdir, true);
362                                         requests [newdir.Request.ReqNum] = newdir;
363                                         lock (parent) {
364                                                 parent.SubDirs [newdir.Directory] = newdir;
365                                         }
366                                 }
367                                 newdirs.Clear ();
368                         }
369                 }
370
371                 ~FAMWatcher ()
372                 {
373                         FAMClose (ref conn);
374                 }
375
376                 static int FAMOpen (out FAMConnection fc)
377                 {
378                         if (use_gamin)
379                                 return gamin_Open (out fc);
380                         return fam_Open (out fc);
381                 }
382
383                 static int FAMClose (ref FAMConnection fc)
384                 {
385                         if (use_gamin)
386                                 return gamin_Close (ref fc);
387                         return fam_Close (ref fc);
388                 }
389
390                 static int FAMMonitorDirectory (ref FAMConnection fc, string filename, out FAMRequest fr, IntPtr user_data)
391                 {
392                         if (use_gamin)
393                                 return gamin_MonitorDirectory (ref fc, filename, out fr, user_data);
394                         return fam_MonitorDirectory (ref fc, filename, out fr, user_data);
395                 }
396
397                 static int FAMCancelMonitor (ref FAMConnection fc, ref FAMRequest fr)
398                 {
399                         if (use_gamin)
400                                 return gamin_CancelMonitor (ref fc, ref fr);
401                         return fam_CancelMonitor (ref fc, ref fr);
402                 }
403
404                 static int FAMPending (ref FAMConnection fc)
405                 {
406                         if (use_gamin)
407                                 return gamin_Pending (ref fc);
408                         return fam_Pending (ref fc);
409                 }
410
411
412                 
413                 [DllImport ("libfam.so.0", EntryPoint="FAMOpen")]
414                 extern static int fam_Open (out FAMConnection fc);
415
416                 [DllImport ("libfam.so.0", EntryPoint="FAMClose")]
417                 extern static int fam_Close (ref FAMConnection fc);
418
419                 [DllImport ("libfam.so.0", EntryPoint="FAMMonitorDirectory")]
420                 extern static int fam_MonitorDirectory (ref FAMConnection fc, string filename,
421                                                         out FAMRequest fr, IntPtr user_data);
422
423                 [DllImport ("libfam.so.0", EntryPoint="FAMCancelMonitor")]
424                 extern static int fam_CancelMonitor (ref FAMConnection fc, ref FAMRequest fr);
425
426                 [DllImport ("libfam.so.0", EntryPoint="FAMPending")]
427                 extern static int fam_Pending (ref FAMConnection fc);
428
429                 [DllImport ("libgamin-1.so.0", EntryPoint="FAMOpen")]
430                 extern static int gamin_Open (out FAMConnection fc);
431
432                 [DllImport ("libgamin-1.so.0", EntryPoint="FAMClose")]
433                 extern static int gamin_Close (ref FAMConnection fc);
434
435                 [DllImport ("libgamin-1.so.0", EntryPoint="FAMMonitorDirectory")]
436                 extern static int gamin_MonitorDirectory (ref FAMConnection fc, string filename,
437                                                         out FAMRequest fr, IntPtr user_data);
438
439                 [DllImport ("libgamin-1.so.0", EntryPoint="FAMCancelMonitor")]
440                 extern static int gamin_CancelMonitor (ref FAMConnection fc, ref FAMRequest fr);
441
442                 [DllImport ("libgamin-1.so.0", EntryPoint="FAMPending")]
443                 extern static int gamin_Pending (ref FAMConnection fc);
444
445                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
446                 extern static int InternalFAMNextEvent (ref FAMConnection fc, out string filename,
447                                                         out int code, out int reqnum);
448
449         }
450 }
451