View | Details | Raw Unified | Return to bug 206593 | Differences between
and this patch

Collapse All | Expand All

(-)b/mcs/class/System/System.IO/KeventWatcher.cs (-21 / +46 lines)
Lines 150-156 public void Dispose () Link Here
150
	[StructLayout(LayoutKind.Sequential)]
150
	[StructLayout(LayoutKind.Sequential)]
151
	struct timespec {
151
	struct timespec {
152
		public IntPtr tv_sec;
152
		public IntPtr tv_sec;
153
		public IntPtr tv_usec;
153
		public IntPtr tv_nsec;
154
	}
154
	}
155
155
156
	class PathData
156
	class PathData
Lines 223-230 public void Stop () Link Here
223
					conn = -1;
223
					conn = -1;
224
				}
224
				}
225
225
226
				if (!thread.Join (2000))
226
				while (!thread.Join (2000))
227
					thread.Abort ();
227
					thread.Interrupt ();
228
228
229
				requestStop = false;
229
				requestStop = false;
230
				started = false;
230
				started = false;
Lines 291-301 void Setup () Link Here
291
			else
291
			else
292
				fullPathNoLastSlash = fsw.FullPath;
292
				fullPathNoLastSlash = fsw.FullPath;
293
				
293
				
294
			// GetFilenameFromFd() returns the *realpath* which can be different than fsw.FullPath because symlinks.
294
			// realpath() returns the *realpath* which can be different than fsw.FullPath because symlinks.
295
			// If so, introduce a fixup step.
295
			// If so, introduce a fixup step.
296
			int fd = open (fullPathNoLastSlash, O_EVTONLY, 0);
296
			var sb = new StringBuilder (__DARWIN_MAXPATHLEN);
297
			var resolvedFullPath = GetFilenameFromFd (fd);
297
			var resolvedFullPath = (realpath(fsw.FullPath, sb) == IntPtr.Zero) ? "" : sb.ToString();
298
			close (fd);
299
298
300
			if (resolvedFullPath != fullPathNoLastSlash)
299
			if (resolvedFullPath != fullPathNoLastSlash)
301
				fixupPath = resolvedFullPath;
300
				fixupPath = resolvedFullPath;
Lines 304-317 void Setup () Link Here
304
303
305
			Scan (fullPathNoLastSlash, false, ref initialFds);
304
			Scan (fullPathNoLastSlash, false, ref initialFds);
306
305
307
			var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)0 };
306
			var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_nsec = (IntPtr)0 };
308
			var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point
307
			var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point
309
			var changes = CreateChangeList (ref initialFds);
308
			var changes = CreateChangeList (ref initialFds);
310
309
311
			int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout);
310
			int numEvents;
311
			int errno = 0;
312
			do {
313
				numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout);
314
				if (numEvents == -1) {
315
					errno = Marshal.GetLastWin32Error ();
316
				}
317
			} while (numEvents == -1 && errno == EINTR);
312
318
313
			if (numEvents == -1) {
319
			if (numEvents == -1) {
314
				var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", Marshal.GetLastWin32Error ());
320
				var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", errno);
315
				throw new IOException (errMsg);
321
				throw new IOException (errMsg);
316
			}
322
			}
317
		}
323
		}
Lines 361-369 void Monitor () Link Here
361
					// Stop () signals us to stop by closing the connection
367
					// Stop () signals us to stop by closing the connection
362
					if (requestStop)
368
					if (requestStop)
363
						break;
369
						break;
364
					if (++retries == 3)
370
					int errno = Marshal.GetLastWin32Error ();
371
					if (errno != EINTR && ++retries == 3)
365
						throw new IOException (String.Format (
372
						throw new IOException (String.Format (
366
							"persistent kevent() error, error code = '{0}'", Marshal.GetLastWin32Error ()));
373
							"persistent kevent() error, error code = '{0}'", errno));
367
374
368
					continue;
375
					continue;
369
				}
376
				}
Lines 395-402 void Monitor () Link Here
395
					}
402
					}
396
403
397
					if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) {
404
					if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) {
398
							UpdatePath (pathData);
405
						/* We can simply remove the entire subtree here, as
399
					} 
406
						   the move will trigger a directory update and thus
407
						   a re-scan at the new location, which will cause any
408
						   children to be re-added. */
409
						removeQueue.Add (pathData);
410
						if (pathData.IsDirectory) {
411
							var prefix = pathData.Path + Path.DirectorySeparatorChar;
412
							foreach (var path in pathsDict.Keys)
413
								if (path.StartsWith (prefix)) {
414
									removeQueue.Add (pathsDict [path]);
415
								}
416
						}
417
						PostEvent (FileAction.RenamedOldName, pathData.Path);
418
					}
400
419
401
					if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) {
420
					if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) {
402
						if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET
421
						if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET
Lines 584-598 void PostEvent (FileAction action, string path, string newPath = null) Link Here
584
				return;
603
				return;
585
604
586
			// e.Name
605
			// e.Name
587
			string name = path.Substring (fullPathNoLastSlash.Length + 1); 
606
			string name = (path.Length > fullPathNoLastSlash.Length) ? path.Substring (fullPathNoLastSlash.Length + 1) : String.Empty;
588
607
589
			// only post events that match filter pattern. check both old and new paths for renames
608
			// only post events that match filter pattern. check both old and new paths for renames
590
			if (!fsw.Pattern.IsMatch (path) && (newPath == null || !fsw.Pattern.IsMatch (newPath)))
609
			if (!fsw.Pattern.IsMatch (path) && (newPath == null || !fsw.Pattern.IsMatch (newPath)))
591
				return;
610
				return;
592
				
611
				
593
			if (action == FileAction.RenamedNewName) {
612
			if (action == FileAction.RenamedNewName) {
594
				string newName = newPath.Substring (fullPathNoLastSlash.Length + 1);
613
				string newName = (newPath.Length > fullPathNoLastSlash.Length) ? newPath.Substring (fullPathNoLastSlash.Length + 1) : String.Empty;
595
				renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, fsw.Path, newName, name);
614
				renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, fsw.Path, newName, name);
615
			} else if (action == FileAction.RenamedOldName) {
616
				renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, fsw.Path, null, name);
596
			}
617
			}
597
				
618
				
598
			fsw.DispatchEvents (action, name, ref renamed);
619
			fsw.DispatchEvents (action, name, ref renamed);
Lines 624-629 private string GetFilenameFromFd (int fd) Link Here
624
		const int O_EVTONLY = 0x8000;
645
		const int O_EVTONLY = 0x8000;
625
		const int F_GETPATH = 50;
646
		const int F_GETPATH = 50;
626
		const int __DARWIN_MAXPATHLEN = 1024;
647
		const int __DARWIN_MAXPATHLEN = 1024;
648
		const int EINTR = 4;
627
		static readonly kevent[] emptyEventList = new System.IO.kevent[0];
649
		static readonly kevent[] emptyEventList = new System.IO.kevent[0];
628
		const int maxFds = 200;
650
		const int maxFds = 200;
629
651
Lines 643-664 private string GetFilenameFromFd (int fd) Link Here
643
		string fixupPath = null;
665
		string fixupPath = null;
644
		string fullPathNoLastSlash = null;
666
		string fullPathNoLastSlash = null;
645
667
646
		[DllImport ("libc", EntryPoint="fcntl", CharSet=CharSet.Auto, SetLastError=true)]
668
		[DllImport ("libc", CharSet=CharSet.Auto, SetLastError=true)]
647
		static extern int fcntl (int file_names_by_descriptor, int cmd, StringBuilder sb);
669
		static extern int fcntl (int file_names_by_descriptor, int cmd, StringBuilder sb);
648
670
649
		[DllImport ("libc")]
671
		[DllImport ("libc", SetLastError=true)]
650
		extern static int open (string path, int flags, int mode_t);
672
		extern static int open (string path, int flags, int mode_t);
651
673
674
		[DllImport ("libc", CharSet=CharSet.Auto, SetLastError=true)]
675
		static extern IntPtr realpath (string pathname, StringBuilder sb);
676
652
		[DllImport ("libc")]
677
		[DllImport ("libc")]
653
		extern static int close (int fd);
678
		extern static int close (int fd);
654
679
655
		[DllImport ("libc")]
680
		[DllImport ("libc", SetLastError=true)]
656
		extern static int kqueue ();
681
		extern static int kqueue ();
657
682
658
		[DllImport ("libc")]
683
		[DllImport ("libc", SetLastError=true)]
659
		extern static int kevent (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, [In] ref timespec time);
684
		extern static int kevent (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, [In] ref timespec time);
660
685
661
		[DllImport ("libc", EntryPoint="kevent")]
686
		[DllImport ("libc", EntryPoint="kevent", SetLastError=true)]
662
		extern static int kevent_notimeout (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, IntPtr ptr);
687
		extern static int kevent_notimeout (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, IntPtr ptr);
663
	}
688
	}
664
689

Return to bug 206593