diff options
Diffstat (limited to 'mcs/class/System/System.IO/KeventWatcher.cs')
-rw-r--r-- | mcs/class/System/System.IO/KeventWatcher.cs | 351 |
1 files changed, 351 insertions, 0 deletions
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); + } +} + |