diff options
Diffstat (limited to 'mcs/class/System/System.IO')
-rwxr-xr-x | mcs/class/System/System.IO/ChangeLog | 21 | ||||
-rw-r--r-- | mcs/class/System/System.IO/DefaultWatcher.cs | 30 | ||||
-rw-r--r-- | mcs/class/System/System.IO/FAMWatcher.cs | 10 | ||||
-rw-r--r-- | mcs/class/System/System.IO/KeventWatcher.cs | 351 |
4 files changed, 402 insertions, 10 deletions
diff --git a/mcs/class/System/System.IO/ChangeLog b/mcs/class/System/System.IO/ChangeLog index 085d366180a..014914f20ca 100755 --- a/mcs/class/System/System.IO/ChangeLog +++ b/mcs/class/System/System.IO/ChangeLog @@ -1,3 +1,24 @@ +2004-10-07 Gonzalo Paniagua Javier <gonzalo@ximian.com> + + * DefaultWatcher.cs: if the file is removed between reading the + directory and filling the file info, catch the exception and ignore the + file. Fixes bug #59482. + +2004-10-07 Gonzalo Paniagua Javier <gonzalo@ximian.com> + + * DefaultWatcher.cs: don't use Directory.GetFileSystemEntries when the + pattern has no wildcards. Fixes bug #67447. + +2004-09-04 Gonzalo Paniagua Javier <gonzalo@ximian.com> + + * FAMWatcher.cs: s/fam/libfam.so.0/ so that g_module finds it even + when the development package is not installed. + +2004-08-06 Geoff Norton <gnorton@customerdna.com> + + * FileSystemWatcher.cs: Use the new KeventWatcher if its supported + * KeventWatcher.cs: Added to cvs + 2004-06-24 Gonzalo Paniagua Javier <gonzalo@ximian.com> * DefaultWatcher.cs: fixed subdirectories notifications and don't diff --git a/mcs/class/System/System.IO/DefaultWatcher.cs b/mcs/class/System/System.IO/DefaultWatcher.cs index 36d6d939f4e..b68149c4ca3 100644 --- a/mcs/class/System/System.IO/DefaultWatcher.cs +++ b/mcs/class/System/System.IO/DefaultWatcher.cs @@ -37,9 +37,10 @@ namespace System.IO { class DefaultWatcherData { public FileSystemWatcher FSW; public string Directory; - public string FileMask; + public string FileMask; // If NoWildcards, contains the full path to the file. public bool IncludeSubdirs; public bool Enabled; + public bool NoWildcards; public DateTime DisabledTime; public Hashtable Files; } @@ -100,7 +101,12 @@ namespace System.IO { data.FSW = fsw; data.Directory = fsw.FullPath; - data.FileMask = fsw.MangledFilter; + data.NoWildcards = !fsw.Pattern.HasWildcard; + if (data.NoWildcards) + data.FileMask = Path.Combine (data.Directory, fsw.MangledFilter); + else + data.FileMask = fsw.MangledFilter; + data.IncludeSubdirs = fsw.IncludeSubdirectories; data.Enabled = true; data.DisabledTime = DateTime.MaxValue; @@ -185,6 +191,7 @@ namespace System.IO { } } + static string [] NoStringsArray = new string [0]; void DoFiles (DefaultWatcherData data, string directory, bool dispatch) { bool direxists = Directory.Exists (directory); @@ -194,10 +201,16 @@ namespace System.IO { } string [] files = null; - if (direxists) { + if (!direxists) { + files = NoStringsArray; + } else if (!data.NoWildcards) { files = Directory.GetFileSystemEntries (directory, data.FileMask); } else { - files = new string [0]; + // The pattern does not have wildcards + if (File.Exists (data.FileMask)) + files = new string [] { data.FileMask }; + else + files = NoStringsArray; } /* Set all as untested */ @@ -211,7 +224,14 @@ namespace System.IO { foreach (string filename in files) { FileData fd = (FileData) data.Files [filename]; if (fd == null) { - data.Files.Add (filename, CreateFileData (directory, filename)); + try { + data.Files.Add (filename, CreateFileData (directory, filename)); + } catch { + // The file might have been removed in the meanwhile + data.Files.Remove (filename); + continue; + } + if (dispatch) DispatchEvents (data.FSW, FileAction.Added, filename); } else if (fd.Directory == directory) { diff --git a/mcs/class/System/System.IO/FAMWatcher.cs b/mcs/class/System/System.IO/FAMWatcher.cs index 21e9bfeaff6..146411b8e91 100644 --- a/mcs/class/System/System.IO/FAMWatcher.cs +++ b/mcs/class/System/System.IO/FAMWatcher.cs @@ -320,24 +320,24 @@ namespace System.IO { } } - [DllImport ("fam")] + [DllImport ("libfam.so.0")] extern static int FAMOpen (out FAMConnection fc); - [DllImport ("fam")] + [DllImport ("libfam.so.0")] extern static int FAMClose (ref FAMConnection fc); - [DllImport ("fam")] + [DllImport ("libfam.so.0")] extern static int FAMMonitorDirectory (ref FAMConnection fc, string filename, out FAMRequest fr, IntPtr user_data); - [DllImport ("fam")] + [DllImport ("libfam.so.0")] extern static int FAMCancelMonitor (ref FAMConnection fc, ref FAMRequest fr); [MethodImplAttribute(MethodImplOptions.InternalCall)] extern static int InternalFAMNextEvent (ref FAMConnection fc, out string filename, out int code, out int reqnum); - [DllImport ("fam")] + [DllImport ("libfam.so.0")] extern static int FAMPending (ref FAMConnection fc); } } diff --git a/mcs/class/System/System.IO/KeventWatcher.cs b/mcs/class/System/System.IO/KeventWatcher.cs new file mode 100644 index 00000000000..56eab5a1eef --- /dev/null +++ b/mcs/class/System/System.IO/KeventWatcher.cs @@ -0,0 +1,351 @@ +// +// System.IO.KeventWatcher.cs: interface with osx kevent +// +// Authors: +// Geoff Norton (gnorton@customerdna.com) +// +// (c) 2004 Geoff Norton + +// +// 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; +using System.Collections; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace System.IO { + + struct kevent { + public int ident; + public short filter; + public ushort flags; + public uint fflags; + public int data; + public string udata; + } + + struct timespec { + public int tv_sec; + public int tv_usec; + } + + class KeventFileData { + public FileSystemInfo fsi; + public DateTime LastAccessTime; + public DateTime LastWriteTime; + + public KeventFileData(FileSystemInfo fsi, DateTime LastAccessTime, DateTime LastWriteTime) { + this.fsi = fsi; + this.LastAccessTime = LastAccessTime; + this.LastWriteTime = LastWriteTime; + } + } + + class KeventData { + public FileSystemWatcher FSW; + public string Directory; + public string FileMask; + public bool IncludeSubdirs; + public bool Enabled; + public Hashtable DirEntries; + public kevent ev; + } + + class KeventWatcher : IFileWatcher + { + static bool failed; + static KeventWatcher instance; + static Hashtable watches; + static Hashtable requests; + static Thread thread; + static int conn; + static bool stop; + + private KeventWatcher () + { + } + + public static bool GetInstance (out IFileWatcher watcher) + { + lock (typeof (KeventWatcher)) { + if (failed == true) { + watcher = null; + return false; + } + + if (instance != null) { + watcher = instance; + return true; + } + + watches = Hashtable.Synchronized (new Hashtable ()); + requests = Hashtable.Synchronized (new Hashtable ()); + conn = kqueue(); + if (conn == -1) { + failed = true; + watcher = null; + return false; + } + + instance = new KeventWatcher (); + watcher = instance; + return true; + } + } + + public void StartDispatching (FileSystemWatcher fsw) + { + KeventData data; + lock (this) { + if (thread == null) { + thread = new Thread (new ThreadStart (Monitor)); + thread.IsBackground = true; + thread.Start (); + } + + data = (KeventData) watches [fsw]; + } + + if (data == null) { + data = new KeventData (); + data.FSW = fsw; + data.Directory = fsw.FullPath; + data.FileMask = fsw.MangledFilter; + data.IncludeSubdirs = fsw.IncludeSubdirectories; + + data.Enabled = true; + lock (this) { + StartMonitoringDirectory (data); + watches [fsw] = data; + stop = false; + } + } + } + + static void StartMonitoringDirectory (KeventData data) + { + DirectoryInfo dir = new DirectoryInfo (data.Directory); + if(data.DirEntries == null) { + data.DirEntries = new Hashtable(); + foreach (FileSystemInfo fsi in dir.GetFileSystemInfos() ) + data.DirEntries.Add(fsi.FullName, new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime)); + } + + int fd = open(data.Directory, 0, 0); + kevent ev = new kevent(); + timespec nullts = new timespec(); + nullts.tv_sec = 0; + nullts.tv_usec = 0; + if (fd > 0) { + ev.ident = fd; + ev.filter = -4; + ev.flags = 1 | 4 | 20; + ev.fflags = 20 | 2 | 1 | 8; + ev.data = 0; + ev.udata = data.Directory; + kevent outev = new kevent(); + outev.udata = ""; + kevent (conn, ref ev, 1, ref outev, 0, ref nullts); + data.ev = ev; + requests [fd] = data; + } + + if (!data.IncludeSubdirs) + return; + + } + + public void StopDispatching (FileSystemWatcher fsw) + { + KeventData data; + lock (this) { + data = (KeventData) watches [fsw]; + if (data == null) + return; + + StopMonitoringDirectory (data); + watches.Remove (fsw); + if (watches.Count == 0) + stop = true; + + if (!data.IncludeSubdirs) + return; + + } + } + + static void StopMonitoringDirectory (KeventData data) + { + close(data.ev.ident); + } + + void Monitor () + { + + while (!stop) { + kevent ev = new kevent(); + ev.udata = ""; + kevent nullev = new kevent(); + nullev.udata = ""; + timespec ts = new timespec(); + ts.tv_sec = 0; + ts.tv_usec = 0; + int haveEvents; + lock (this) { + haveEvents = kevent (conn, ref nullev, 0, ref ev, 1, ref ts); + } + + if (haveEvents > 0) { + // Restart monitoring + KeventData data = (KeventData) requests [ev.ident]; + StartMonitoringDirectory(data); + ProcessEvent (ev); + } else { + System.Threading.Thread.Sleep (500); + } + } + + lock (this) { + thread = null; + stop = false; + } + } + + void ProcessEvent (kevent ev) + { + lock (this) { + KeventData data = (KeventData) requests [ev.ident]; + if (!data.Enabled) + return; + + FileSystemWatcher fsw; + string filename = ""; + + fsw = data.FSW; + FileAction fa = 0; + DirectoryInfo dir = new DirectoryInfo (data.Directory); + FileSystemInfo changedFsi = null; + + try { + foreach (FileSystemInfo fsi in dir.GetFileSystemInfos() ) + if (data.DirEntries.ContainsKey (fsi.FullName) && (fsi is FileInfo)) { + KeventFileData entry = (KeventFileData) data.DirEntries [fsi.FullName]; + if ( (entry.LastWriteTime != fsi.LastWriteTime) || (entry.LastAccessTime != fsi.LastAccessTime) ) { + filename = fsi.FullName; + fa = FileAction.Modified; + data.DirEntries [fsi.FullName] = new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime); + if (fsw.IncludeSubdirectories && fsi is DirectoryInfo) { + data.Directory = filename; + requests [ev.ident] = data; + ProcessEvent(ev); + } + PostEvent(filename, fsw, fa, changedFsi); + } + } + } catch (Exception) { + // The file system infos were changed while we processed them + } + // Deleted + try { + bool deleteMatched = true; + while(deleteMatched) { + foreach (KeventFileData entry in data.DirEntries.Values) { + if (!File.Exists (entry.fsi.FullName) && !Directory.Exists (entry.fsi.FullName)) { + filename = entry.fsi.FullName; + fa = FileAction.Removed; + data.DirEntries.Remove (entry.fsi.FullName); + PostEvent(filename, fsw, fa, changedFsi); + break; + } + } + deleteMatched = false; + } + } catch (Exception) { + // The file system infos were changed while we processed them + } + // Added + try { + foreach (FileSystemInfo fsi in dir.GetFileSystemInfos()) + if (!data.DirEntries.ContainsKey (fsi.FullName)) { + changedFsi = fsi; + filename = fsi.FullName; + fa = FileAction.Added; + data.DirEntries [fsi.FullName] = new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime); + PostEvent(filename, fsw, fa, changedFsi); + } + } catch (Exception) { + // The file system infos were changed while we processed them + } + + + } + } + + private void PostEvent (string filename, FileSystemWatcher fsw, FileAction fa, FileSystemInfo changedFsi) { + RenamedEventArgs renamed = null; + if (fa == 0) + return; + + if (fsw.IncludeSubdirectories && fa == FileAction.Added) { + if (changedFsi is DirectoryInfo) { + KeventData newdirdata = new KeventData (); + newdirdata.FSW = fsw; + newdirdata.Directory = changedFsi.FullName; + newdirdata.FileMask = fsw.MangledFilter; + newdirdata.IncludeSubdirs = fsw.IncludeSubdirectories; + + newdirdata.Enabled = true; + lock (this) { + StartMonitoringDirectory (newdirdata); + } + } + } + + if (!fsw.Pattern.IsMatch(filename)) + return; + + lock (fsw) { + fsw.DispatchEvents (fa, filename, ref renamed); + if (fsw.Waiting) { + fsw.Waiting = false; + System.Threading.Monitor.PulseAll (fsw); + } + } + } + + [DllImport ("libc")] + extern static int open(string path, int flags, int mode_t); + + [DllImport ("libc")] + extern static int close(int fd); + + [DllImport ("libc")] + extern static int kqueue(); + + [DllImport ("libc")] + extern static int kevent(int kqueue, ref kevent ev, int nchanges, ref kevent evtlist, int nevents, ref timespec ts); + } +} + |