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:
authorDuncan Mak <duncan.mak@xamarin.com>2015-02-02 22:18:31 +0300
committerDuncan Mak <duncan.mak@xamarin.com>2015-02-02 22:18:31 +0300
commitde2f33f7b2d390e297e00b2ae33502ef37f79115 (patch)
tree63d6a7a768d1a1520b8a802914425b23868e78b8
parent7569e1a18d6ed45c9b65a74213d7e8ecc81f7b3d (diff)
Revert "Revert "[System.IO] Phase 2 of revamping KeventWatcher (FileSystemWatcher implementation for OS X/kqueue). Dozens of bugs and reliability issues fixed.""mono-3.12.0.76
This reverts commit d778fb3631576e42dfa35c53951ea3334f9f2651.
-rw-r--r--mcs/class/System/System.IO/FileSystemWatcher.cs69
-rw-r--r--mcs/class/System/System.IO/KeventWatcher.cs466
-rw-r--r--mcs/class/System/System.IO/SearchPattern.cs23
3 files changed, 370 insertions, 188 deletions
diff --git a/mcs/class/System/System.IO/FileSystemWatcher.cs b/mcs/class/System/System.IO/FileSystemWatcher.cs
index 125f07805f9..0d293e53188 100644
--- a/mcs/class/System/System.IO/FileSystemWatcher.cs
+++ b/mcs/class/System/System.IO/FileSystemWatcher.cs
@@ -183,7 +183,10 @@ namespace System.IO {
internal SearchPattern2 Pattern {
get {
if (pattern == null) {
- pattern = new SearchPattern2 (MangledFilter);
+ if (watcher.GetType () == typeof (KeventWatcher))
+ pattern = new SearchPattern2 (MangledFilter, true); //assume we want to ignore case (OS X)
+ else
+ pattern = new SearchPattern2 (MangledFilter);
}
return pattern;
}
@@ -372,52 +375,60 @@ namespace System.IO {
ErrorEvent,
RenameEvent
}
- private void RaiseEvent (Delegate ev, EventArgs arg, EventType evtype)
- {
- if (ev == null)
- return;
-
- if (synchronizingObject == null) {
- switch (evtype) {
- case EventType.RenameEvent:
- ((RenamedEventHandler)ev).BeginInvoke (this, (RenamedEventArgs) arg, null, null);
- break;
- case EventType.ErrorEvent:
- ((ErrorEventHandler)ev).BeginInvoke (this, (ErrorEventArgs) arg, null, null);
- break;
- case EventType.FileSystemEvent:
- ((FileSystemEventHandler)ev).BeginInvoke (this, (FileSystemEventArgs) arg, null, null);
- break;
- }
- return;
- }
- synchronizingObject.BeginInvoke (ev, new object [] {this, arg});
- }
-
protected void OnChanged (FileSystemEventArgs e)
{
- RaiseEvent (Changed, e, EventType.FileSystemEvent);
+ if (Changed == null)
+ return;
+
+ if (synchronizingObject == null)
+ Changed (this, e);
+ else
+ synchronizingObject.BeginInvoke (Changed, new object[] { this, e });
}
protected void OnCreated (FileSystemEventArgs e)
{
- RaiseEvent (Created, e, EventType.FileSystemEvent);
+ if (Created == null)
+ return;
+
+ if (synchronizingObject == null)
+ Created (this, e);
+ else
+ synchronizingObject.BeginInvoke (Created, new object[] { this, e });
}
protected void OnDeleted (FileSystemEventArgs e)
{
- RaiseEvent (Deleted, e, EventType.FileSystemEvent);
+ if (Deleted == null)
+ return;
+
+ if (synchronizingObject == null)
+ Deleted (this, e);
+ else
+ synchronizingObject.BeginInvoke (Deleted, new object[] { this, e });
}
- protected void OnError (ErrorEventArgs e)
+ internal void OnError (ErrorEventArgs e)
{
- RaiseEvent (Error, e, EventType.ErrorEvent);
+ if (Error == null)
+ return;
+
+ if (synchronizingObject == null)
+ Error (this, e);
+ else
+ synchronizingObject.BeginInvoke (Error, new object[] { this, e });
}
protected void OnRenamed (RenamedEventArgs e)
{
- RaiseEvent (Renamed, e, EventType.RenameEvent);
+ if (Renamed == null)
+ return;
+
+ if (synchronizingObject == null)
+ Renamed (this, e);
+ else
+ synchronizingObject.BeginInvoke (Renamed, new object[] { this, e });
}
public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType)
diff --git a/mcs/class/System/System.IO/KeventWatcher.cs b/mcs/class/System/System.IO/KeventWatcher.cs
index 2b96de34404..f3cc7dcc183 100644
--- a/mcs/class/System/System.IO/KeventWatcher.cs
+++ b/mcs/class/System/System.IO/KeventWatcher.cs
@@ -4,6 +4,7 @@
// Authors:
// Geoff Norton (gnorton@customerdna.com)
// Cody Russell (cody@xamarin.com)
+// Alexis Christoforides (lexas@xamarin.com)
//
// (c) 2004 Geoff Norton
// Copyright 2014 Xamarin Inc
@@ -36,6 +37,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
+using System.Reflection;
namespace System.IO {
@@ -73,6 +75,7 @@ namespace System.IO {
VM = -11
}
+ [Flags]
enum FilterFlags : uint {
ReadPoll = EventFlags.Flag0,
ReadOutOfBand = EventFlags.Flag1,
@@ -128,7 +131,7 @@ namespace System.IO {
[StructLayout(LayoutKind.Sequential)]
struct kevent : IDisposable {
- public int ident;
+ public UIntPtr ident;
public EventFilter filter;
public EventFlags flags;
public FilterFlags fflags;
@@ -140,17 +143,21 @@ namespace System.IO {
if (udata != IntPtr.Zero)
Marshal.FreeHGlobal (udata);
}
+
+
}
+ [StructLayout(LayoutKind.Sequential)]
struct timespec {
- public int tv_sec;
- public int tv_usec;
+ public IntPtr tv_sec;
+ public IntPtr tv_usec;
}
class PathData
{
public string Path;
public bool IsDirectory;
+ public int Fd;
}
class KqueueMonitor : IDisposable
@@ -168,211 +175,367 @@ namespace System.IO {
public void Dispose ()
{
- Stop ();
+ CleanUp ();
}
public void Start ()
{
- conn = kqueue ();
+ lock (stateLock) {
+ if (started)
+ return;
- if (thread == null) {
- thread = new Thread (new ThreadStart (Monitor));
+ conn = kqueue ();
+
+ if (conn == -1)
+ throw new IOException (String.Format (
+ "kqueue() error at init, error code = '{0}'", Marshal.GetLastWin32Error ()));
+
+ thread = new Thread (() => DoMonitor ());
thread.IsBackground = true;
thread.Start ();
- }
- var pathData = Add (fsw.FullPath);
+ startedEvent.WaitOne ();
- Scan (pathData);
+ if (failedInit) {
+ thread.Join ();
+ CleanUp ();
+ throw new IOException ("Monitor thread failed while initializing.");
+ }
+ else
+ started = true;
+ }
}
public void Stop ()
{
- stop = true;
-
- if (thread != null)
- thread.Interrupt ();
+ lock (stateLock) {
+ if (!started)
+ return;
+
+ requestStop = true;
+ thread.Join ();
+ requestStop = false;
+
+ CleanUp ();
+ started = false;
+ }
+ }
+ void CleanUp ()
+ {
if (conn != -1)
close (conn);
conn = -1;
+
+ foreach (int fd in fdsDict.Keys)
+ close (fd);
+
+ fdsDict.Clear ();
+ pathsDict.Clear ();
}
- private PathData FindPath (string path)
+ void DoMonitor ()
{
- foreach (KeyValuePair<PathData, int> kv in paths) {
- if (kv.Key.Path == path)
- return kv.Key;
+ Exception exc = null;
+ failedInit = false;
+
+ try {
+ Setup ();
+ } catch (Exception e) {
+ failedInit = true;
+ exc = e;
+ } finally {
+ startedEvent.Set ();
+ }
+
+ if (failedInit) {
+ fsw.OnError (new ErrorEventArgs (exc));
+ return;
}
- return null;
+ try {
+ Monitor ();
+ } catch (Exception e) {
+ exc = e;
+ } finally {
+ if (!requestStop) { // failure
+ CleanUp ();
+ started = false;
+ }
+ if (exc != null)
+ fsw.OnError (new ErrorEventArgs (exc));
+ }
}
- private PathData FindPath (int fd)
+ void Setup ()
+ {
+ var initialFds = new List<int> ();
+
+ // GetFilenameFromFd() returns the *realpath* which can be different than fsw.FullPath because symlinks.
+ // If so, introduce a fixup step.
+ int fd = open (fsw.FullPath, O_EVTONLY, 0);
+ var resolvedFullPath = GetFilenameFromFd (fd);
+ close (fd);
+
+ if (resolvedFullPath != fsw.FullPath)
+ fixupPath = resolvedFullPath;
+ else
+ fixupPath = null;
+
+ Scan (fsw.FullPath, false, ref initialFds);
+
+ var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)0 };
+ var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point
+ var changes = CreateChangeList (ref initialFds);
+
+ int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout);
+
+ if (numEvents == -1) {
+ var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", Marshal.GetLastWin32Error ());
+ throw new IOException (errMsg);
+ }
+ }
+
+ kevent[] CreateChangeList (ref List<int> FdList)
{
- foreach (KeyValuePair<PathData, int> kv in paths) {
- if (kv.Value == fd)
- return kv.Key;
+ if (FdList.Count == 0)
+ return emptyEventList;
+
+ var changes = new List<kevent> ();
+ foreach (int fd in FdList) {
+ var change = new kevent {
+
+ ident = (UIntPtr)fd,
+ filter = EventFilter.Vnode,
+ flags = EventFlags.Add | EventFlags.Enable | EventFlags.Clear,
+ fflags = FilterFlags.VNodeDelete | FilterFlags.VNodeExtend |
+ FilterFlags.VNodeRename | FilterFlags.VNodeAttrib |
+ FilterFlags.VNodeLink | FilterFlags.VNodeRevoke |
+ FilterFlags.VNodeWrite,
+ data = IntPtr.Zero,
+ udata = IntPtr.Zero
+ };
+
+ changes.Add (change);
}
+ FdList.Clear ();
- return null;
+ return changes.ToArray ();
}
- private void Monitor ()
+ void Monitor ()
{
- bool firstRun = true;
+ var timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)500000000 };
+ var eventBuffer = new kevent[32];
+ var newFds = new List<int> ();
+ List<PathData> removeQueue = new List<PathData> ();
+ List<string> rescanQueue = new List<string> ();
- while (!stop) {
- removeQueue.ForEach (Remove);
+ while (!requestStop) {
+ var changes = CreateChangeList (ref newFds);
- var changes = new List<kevent> ();
- var outEvents = new List<kevent> ();
+ int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref timeout);
- rescanQueue.ForEach (fd => {
- var path = FindPath (fd);
- Scan (path, !firstRun);
- });
- rescanQueue.Clear ();
-
- foreach (KeyValuePair<PathData, int> kv in paths) {
- var change = new kevent {
- ident = kv.Value,
- filter = EventFilter.Vnode,
- flags = EventFlags.Add | EventFlags.Enable | EventFlags.Clear,
- fflags = FilterFlags.VNodeDelete | FilterFlags.VNodeExtend |
- FilterFlags.VNodeRename | FilterFlags.VNodeAttrib |
- FilterFlags.VNodeLink | FilterFlags.VNodeRevoke |
- FilterFlags.VNodeWrite,
- data = IntPtr.Zero,
- udata = IntPtr.Zero
- };
-
- changes.Add (change);
- outEvents.Add (new kevent ());
+ if (numEvents == -1) {
+ var errMsg = String.Format ("kevent() error, error code = '{0}'", Marshal.GetLastWin32Error ());
+ fsw.OnError (new ErrorEventArgs (new IOException (errMsg)));
}
- if (changes.Count > 0) {
- var outArray = outEvents.ToArray ();
- var changesArray = changes.ToArray ();
- int numEvents = kevent (conn, changesArray, changesArray.Length, outArray, outArray.Length, IntPtr.Zero);
-
- for (var i = 0; i < numEvents; i++) {
- var kevt = outArray [i];
- var pathData = FindPath (kevt.ident);
-
- if ((kevt.fflags & FilterFlags.VNodeDelete) != 0) {
- removeQueue.Add (kevt.ident);
- PostEvent (FileAction.Removed, pathData.Path);
- } else if (((kevt.fflags & FilterFlags.VNodeRename) != 0) || ((kevt.fflags & FilterFlags.VNodeRevoke) != 0) || ((kevt.fflags & FilterFlags.VNodeWrite) != 0)) {
- if (pathData.IsDirectory && Directory.Exists (pathData.Path))
- rescanQueue.Add (kevt.ident);
-
- if ((kevt.fflags & FilterFlags.VNodeRename) != 0) {
- var fd = paths [pathData];
- var newFilename = GetFilenameFromFd (fd);
- var oldFilename = pathData.Path;
-
- Remove (pathData);
- PostEvent (FileAction.RenamedNewName, oldFilename, newFilename);
-
- Add (newFilename, false);
- }
- } else if ((kevt.fflags & FilterFlags.VNodeAttrib) != 0) {
- PostEvent (FileAction.Modified, pathData.Path);
- }
+ if (numEvents == 0)
+ continue;
+
+ for (var i = 0; i < numEvents; i++) {
+ var kevt = eventBuffer [i];
+ var pathData = fdsDict [(int)kevt.ident];
+
+ if ((kevt.flags & EventFlags.Error) == EventFlags.Error) {
+ var errMsg = String.Format ("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data);
+ fsw.OnError (new ErrorEventArgs (new IOException (errMsg)));
+ continue;
}
- } else {
- Thread.Sleep (500);
+
+ if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke)
+ removeQueue.Add (pathData);
+
+ else if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) {
+ if (pathData.IsDirectory)
+ rescanQueue.Add (pathData.Path);
+ else
+ PostEvent (FileAction.Modified, pathData.Path);
+ }
+
+ else if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) {
+ var newFilename = GetFilenameFromFd (pathData.Fd);
+
+ if (newFilename.StartsWith (fsw.FullPath))
+ Rename (pathData, newFilename);
+ else //moved outside of our watched dir so stop watching
+ RemoveTree (pathData);
+ }
+
+ else if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend)
+ PostEvent (FileAction.Modified, pathData.Path);
}
- firstRun = false;
+ removeQueue.ForEach (Remove);
+ removeQueue.Clear ();
+
+ rescanQueue.ForEach (path => {
+ Scan (path, true, ref newFds);
+ });
+ rescanQueue.Clear ();
}
}
- private PathData Add (string path, bool postEvents = false)
+ PathData Add (string path, bool postEvents, ref List<int> fds)
{
+ PathData pathData;
+ pathsDict.TryGetValue (path, out pathData);
+
+ if (pathData != null)
+ return pathData;
+
var fd = open (path, O_EVTONLY, 0);
- if (fd == -1)
+ if (fd == -1) {
+ fsw.OnError (new ErrorEventArgs (new IOException (String.Format (
+ "open() error while attempting to process path '{0}', error code = '{1}'", path, Marshal.GetLastWin32Error ()))));
return null;
+ }
- var attrs = File.GetAttributes (path);
- bool isDir = false;
- if ((attrs & FileAttributes.Directory) == FileAttributes.Directory)
- isDir = true;
+ try {
+ fds.Add (fd);
- var pathData = new PathData {
- Path = path,
- IsDirectory = isDir
- };
+ var attrs = File.GetAttributes (path);
- if (FindPath (path) == null) {
- paths.Add (pathData, fd);
+ pathData = new PathData {
+ Path = path,
+ Fd = fd,
+ IsDirectory = (attrs & FileAttributes.Directory) == FileAttributes.Directory
+ };
+
+ pathsDict.Add (path, pathData);
+ fdsDict.Add (fd, pathData);
if (postEvents)
PostEvent (FileAction.Added, path);
+
+ return pathData;
+ } catch (Exception e) {
+ close (fd);
+ fsw.OnError (new ErrorEventArgs (e));
+ return null;
}
- return pathData;
}
- private void Remove (int fd)
+ void Remove (PathData pathData)
{
- var path = FindPath (fd);
- paths.Remove (path);
- removeQueue.Remove (fd);
-
- close (fd);
+ fdsDict.Remove (pathData.Fd);
+ pathsDict.Remove (pathData.Path);
+ close (pathData.Fd);
+ PostEvent (FileAction.Removed, pathData.Path);
}
- private void Remove (PathData pathData)
+ void RemoveTree (PathData pathData)
{
- var fd = paths [pathData];
- paths.Remove (pathData);
- removeQueue.Remove (fd);
+ var toRemove = new List<PathData> ();
- close (fd);
+ toRemove.Add (pathData);
+
+ if (pathData.IsDirectory) {
+ var prefix = pathData.Path + Path.DirectorySeparatorChar;
+ foreach (var path in pathsDict.Keys)
+ if (path.StartsWith (prefix)) {
+ toRemove.Add (pathsDict [path]);
+ }
+ }
+ toRemove.ForEach (Remove);
}
- private void Scan (PathData pathData, bool postEvents = false)
+ void Rename (PathData pathData, string newRoot)
{
- var path = pathData.Path;
+ var toRename = new List<PathData> ();
+ var oldRoot = pathData.Path;
+
+ toRename.Add (pathData);
+
+ if (pathData.IsDirectory) {
+ var prefix = oldRoot + Path.DirectorySeparatorChar;
+ foreach (var path in pathsDict.Keys)
+ if (path.StartsWith (prefix))
+ toRename.Add (pathsDict [path]);
+ }
- Add (path, postEvents);
+ toRename.ForEach ((pd) => {
+ var oldPath = pd.Path;
+ var newPath = newRoot + oldPath.Substring (oldRoot.Length);
+ pd.Path = newPath;
+ pathsDict.Remove (oldPath);
+ pathsDict.Add (newPath, pd);
+ });
- if (!fsw.IncludeSubdirectories)
+ PostEvent (FileAction.RenamedNewName, oldRoot, newRoot);
+ }
+
+ void Scan (string path, bool postEvents, ref List<int> fds)
+ {
+ if (requestStop)
+ return;
+
+ var pathData = Add (path, postEvents, ref fds);
+
+ if (pathData == null)
+ return;
+
+ if (!pathData.IsDirectory)
return;
- var attrs = File.GetAttributes (path);
- if ((attrs & FileAttributes.Directory) == FileAttributes.Directory) {
- var dirsToProcess = new List<string> ();
- dirsToProcess.Add (path);
+ var dirsToProcess = new List<string> ();
+ dirsToProcess.Add (path);
+
+ while (dirsToProcess.Count > 0) {
+ var tmp = dirsToProcess [0];
+ dirsToProcess.RemoveAt (0);
+
+ var info = new DirectoryInfo (tmp);
+ FileSystemInfo[] fsInfos = null;
+ try {
+ fsInfos = info.GetFileSystemInfos ();
+
+ } catch (IOException) {
+ // this can happen if the directory has been deleted already.
+ // that's okay, just keep processing the other dirs.
+ fsInfos = new FileSystemInfo[0];
+ }
- while (dirsToProcess.Count > 0) {
- var tmp = dirsToProcess [0];
- dirsToProcess.RemoveAt (0);
+ foreach (var fsi in fsInfos) {
+ if ((fsi.Attributes & FileAttributes.Directory) == FileAttributes.Directory && !fsw.IncludeSubdirectories)
+ continue;
- var info = new DirectoryInfo (tmp);
- foreach (var fsi in info.GetFileSystemInfos ()) {
- if (Add (fsi.FullName, postEvents) == null)
- continue;
+ if ((fsi.Attributes & FileAttributes.Directory) != FileAttributes.Directory && !fsw.Pattern.IsMatch (fsi.FullName))
+ continue;
- var childAttrs = File.GetAttributes (fsi.FullName);
- if ((childAttrs & FileAttributes.Directory) == FileAttributes.Directory)
- dirsToProcess.Add (fsi.FullName);
- }
+ var currentPathData = Add (fsi.FullName, postEvents, ref fds);
+
+ if (currentPathData != null && currentPathData.IsDirectory)
+ dirsToProcess.Add (fsi.FullName);
}
}
}
-
- private void PostEvent (FileAction action, string path, string newPath = null)
+
+ void PostEvent (FileAction action, string path, string newPath = null)
{
RenamedEventArgs renamed = null;
if (action == 0)
return;
+ // only post events that match filter pattern. check both old and new paths for renames
+ if (!fsw.Pattern.IsMatch (path) && (newPath == null || !fsw.Pattern.IsMatch (newPath)))
+ return;
+
if (action == FileAction.RenamedNewName)
renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, "", newPath, path);
@@ -388,23 +551,36 @@ namespace System.IO {
private string GetFilenameFromFd (int fd)
{
- var sb = new StringBuilder (1024);
+ var sb = new StringBuilder (__DARWIN_MAXPATHLEN);
- if (fcntl (fd, F_GETPATH, sb) != -1)
+ if (fcntl (fd, F_GETPATH, sb) != -1) {
+ if (fixupPath != null)
+ sb.Replace (fixupPath, fsw.FullPath, 0, fixupPath.Length); // see Setup()
return sb.ToString ();
- else
+ } else {
+ fsw.OnError (new ErrorEventArgs (new IOException (String.Format (
+ "fcntl() error while attempting to get path for fd '{0}', error code = '{1}'", fd, Marshal.GetLastWin32Error ()))));
return String.Empty;
+ }
}
- private const int O_EVTONLY = 0x8000;
- private const int F_GETPATH = 50;
- private FileSystemWatcher fsw;
- private int conn;
- private Thread thread;
- private bool stop;
- private readonly List<int> removeQueue = new List<int> ();
- private readonly List<int> rescanQueue = new List<int> ();
- private readonly Dictionary<PathData, int> paths = new Dictionary<PathData, int> ();
+ const int O_EVTONLY = 0x8000;
+ const int F_GETPATH = 50;
+ const int __DARWIN_MAXPATHLEN = 1024;
+ static readonly kevent[] emptyEventList = new System.IO.kevent[0];
+
+ FileSystemWatcher fsw;
+ int conn;
+ Thread thread;
+ volatile bool requestStop = false;
+ AutoResetEvent startedEvent = new AutoResetEvent (false);
+ bool started = false;
+ bool failedInit = false;
+ object stateLock = new object ();
+
+ readonly Dictionary<string, PathData> pathsDict = new Dictionary<string, PathData> ();
+ readonly Dictionary<int, PathData> fdsDict = new Dictionary<int, PathData> ();
+ string fixupPath = null;
[DllImport ("libc", EntryPoint="fcntl", CharSet=CharSet.Auto, SetLastError=true)]
static extern int fcntl (int file_names_by_descriptor, int cmd, StringBuilder sb);
@@ -419,7 +595,7 @@ namespace System.IO {
extern static int kqueue ();
[DllImport ("libc")]
- extern static int kevent(int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, IntPtr time);
+ extern static int kevent (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, [In] ref timespec time);
}
class KeventWatcher : IFileWatcher
@@ -467,10 +643,9 @@ namespace System.IO {
monitor = (KqueueMonitor)watches [fsw];
} else {
monitor = new KqueueMonitor (fsw);
+ watches.Add (fsw, monitor);
}
-
- watches.Add (fsw, monitor);
-
+
monitor.Start ();
}
@@ -482,8 +657,7 @@ namespace System.IO {
monitor.Stop ();
}
-
-
+
[DllImport ("libc")]
extern static int close (int fd);
diff --git a/mcs/class/System/System.IO/SearchPattern.cs b/mcs/class/System/System.IO/SearchPattern.cs
index 0fbab4e5e7e..f302dffaaa0 100644
--- a/mcs/class/System/System.IO/SearchPattern.cs
+++ b/mcs/class/System/System.IO/SearchPattern.cs
@@ -47,7 +47,7 @@ namespace System.IO {
Compile (pattern);
}
- // OSX has a retarded case-insensitive yet case-aware filesystem
+ // OSX has a case-insensitive yet case-aware filesystem
// so we need a overload in here for the Kqueue watcher
public bool IsMatch (string text, bool ignorecase)
{
@@ -55,20 +55,17 @@ namespace System.IO {
bool match = String.Compare (pattern, text, ignorecase) == 0;
if (match)
return true;
-
- // This is a special case for FSW. It needs to match e.g. subdir/file.txt
- // when the pattern is "file.txt"
- int idx = text.LastIndexOf ('/');
- if (idx == -1)
- return false;
- idx++;
- if (idx == text.Length)
- return false;
-
- return (String.Compare (pattern, text.Substring (idx), ignorecase) == 0);
}
+
+ // This is a special case for FSW. It needs to match e.g. subdir/file.txt
+ // when the pattern is "file.txt"
+ var fileName = Path.GetFileName (text);
+
+ if (!hasWildcard)
+ return (String.Compare (pattern, fileName, ignorecase) == 0);
+
- return Match (ops, text, 0);
+ return Match (ops, fileName, 0);
}
public bool IsMatch (string text)