Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mcs/class/System/System.IO/KeventWatcher.cs')
-rw-r--r--mcs/class/System/System.IO/KeventWatcher.cs351
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);
+ }
+}
+