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

Collapse All | Expand All

(-)mcs/class/System/System.IO/KeventWatcher.cs (-35 / +47 lines)
Lines 232-239 Link Here
232
					conn = -1;
232
					conn = -1;
233
				}
233
				}
234
234
235
				if (!thread.Join (2000))
235
				while (!thread.Join (2000))
236
					thread.Abort ();
236
					thread.Interrupt ();
237
237
238
				requestStop = false;
238
				requestStop = false;
239
				started = false;
239
				started = false;
Lines 300-310 Link Here
300
			else
300
			else
301
				fullPathNoLastSlash = fsw.FullPath;
301
				fullPathNoLastSlash = fsw.FullPath;
302
				
302
				
303
			// GetFilenameFromFd() returns the *realpath* which can be different than fsw.FullPath because symlinks.
303
			// realpath() returns the *realpath* which can be different than fsw.FullPath because symlinks.
304
			// If so, introduce a fixup step.
304
			// If so, introduce a fixup step.
305
			int fd = open (fullPathNoLastSlash, O_EVTONLY, 0);
305
			var sb = new StringBuilder (__DARWIN_MAXPATHLEN);
306
			var resolvedFullPath = GetFilenameFromFd (fd);
306
			var resolvedFullPath = (realpath(fsw.FullPath, sb) == IntPtr.Zero) ? "" : sb.ToString();
307
			close (fd);
308
307
309
			if (resolvedFullPath != fullPathNoLastSlash)
308
			if (resolvedFullPath != fullPathNoLastSlash)
310
				fixupPath = resolvedFullPath;
309
				fixupPath = resolvedFullPath;
Lines 317-326 Link Here
317
			var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point
316
			var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point
318
			var changes = CreateChangeList (ref initialFds);
317
			var changes = CreateChangeList (ref initialFds);
319
318
320
			int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout);
319
			int numEvents;
320
			int errno = 0;
321
			do {
322
				numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout);
323
				if (numEvents == -1) {
324
					errno = Marshal.GetLastWin32Error ();
325
				}
326
			} while (numEvents == -1 && errno == EINTR);
321
327
322
			if (numEvents == -1) {
328
			if (numEvents == -1) {
323
				var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", Marshal.GetLastWin32Error ());
329
				var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", errno);
324
				throw new IOException (errMsg);
330
				throw new IOException (errMsg);
325
			}
331
			}
326
		}
332
		}
Lines 364-395 Link Here
364
			while (!requestStop) {
370
			while (!requestStop) {
365
				var changes = CreateChangeList (ref newFds);
371
				var changes = CreateChangeList (ref newFds);
366
372
367
				// We are calling an icall, so have to marshal manually
373
				int numEvents = kevent_notimeout (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, IntPtr.Zero);
368
				// Marshal in
369
				int ksize = Marshal.SizeOf<kevent> ();
370
				var changesNative = Marshal.AllocHGlobal (ksize * changes.Length);
371
				for (int i = 0; i < changes.Length; ++i)
372
					Marshal.StructureToPtr (changes [i], changesNative + (i * ksize), false);
373
				var eventBufferNative = Marshal.AllocHGlobal (ksize * eventBuffer.Length);
374
375
				int numEvents = kevent_notimeout (ref conn, changesNative, changes.Length, eventBufferNative, eventBuffer.Length);
376
377
				// Marshal out
378
				Marshal.FreeHGlobal (changesNative);
379
				for (int i = 0; i < numEvents; ++i)
380
					eventBuffer [i] = Marshal.PtrToStructure<kevent> (eventBufferNative + (i * ksize));
381
				Marshal.FreeHGlobal (eventBufferNative);
382
374
383
				if (numEvents == -1) {
375
				if (numEvents == -1) {
384
					// Stop () signals us to stop by closing the connection
376
					// Stop () signals us to stop by closing the connection
385
					if (requestStop)
377
					if (requestStop)
386
						break;
378
						break;
387
					if (++retries == 3)
379
					int errno = Marshal.GetLastWin32Error ();
380
					if (errno != EINTR && ++retries == 3)
388
						throw new IOException (String.Format (
381
						throw new IOException (String.Format (
389
							"persistent kevent() error, error code = '{0}'", Marshal.GetLastWin32Error ()));
382
							"persistent kevent() error, error code = '{0}'", errno));
390
383
391
					continue;
384
					continue;
392
				}
385
				}
386
393
				retries = 0;
387
				retries = 0;
394
388
395
				for (var i = 0; i < numEvents; i++) {
389
				for (var i = 0; i < numEvents; i++) {
Lines 417-424 Link Here
417
					}
411
					}
418
412
419
					if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) {
413
					if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) {
420
							UpdatePath (pathData);
414
						/* We can simply remove the entire subtree here, as
421
					} 
415
						   the move will trigger a directory update and thus
416
						   a re-scan at the new location, which will cause any
417
						   children to be re-added. */
418
						removeQueue.Add (pathData);
419
						if (pathData.IsDirectory) {
420
							var prefix = pathData.Path + Path.DirectorySeparatorChar;
421
							foreach (var path in pathsDict.Keys)
422
								if (path.StartsWith (prefix)) {
423
									removeQueue.Add (pathsDict [path]);
424
								}
425
						}
426
						PostEvent (FileAction.RenamedOldName, pathData.Path);
427
					}
422
428
423
					if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) {
429
					if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) {
424
						if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET
430
						if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET
Lines 606-620 Link Here
606
				return;
612
				return;
607
613
608
			// e.Name
614
			// e.Name
609
			string name = path.Substring (fullPathNoLastSlash.Length + 1); 
615
			string name = (path.Length > fullPathNoLastSlash.Length) ? path.Substring (fullPathNoLastSlash.Length + 1) : String.Empty;
610
616
611
			// only post events that match filter pattern. check both old and new paths for renames
617
			// only post events that match filter pattern. check both old and new paths for renames
612
			if (!fsw.Pattern.IsMatch (path) && (newPath == null || !fsw.Pattern.IsMatch (newPath)))
618
			if (!fsw.Pattern.IsMatch (path) && (newPath == null || !fsw.Pattern.IsMatch (newPath)))
613
				return;
619
				return;
614
				
620
				
615
			if (action == FileAction.RenamedNewName) {
621
			if (action == FileAction.RenamedNewName) {
616
				string newName = newPath.Substring (fullPathNoLastSlash.Length + 1);
622
				string newName = (newPath.Length > fullPathNoLastSlash.Length) ? newPath.Substring (fullPathNoLastSlash.Length + 1) : String.Empty;
617
				renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, fsw.Path, newName, name);
623
				renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, fsw.Path, newName, name);
624
			} else if (action == FileAction.RenamedOldName) {
625
				renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, fsw.Path, null, name);
618
			}
626
			}
619
				
627
				
620
			fsw.DispatchEvents (action, name, ref renamed);
628
			fsw.DispatchEvents (action, name, ref renamed);
Lines 646-651 Link Here
646
		const int O_EVTONLY = 0x8000;
654
		const int O_EVTONLY = 0x8000;
647
		const int F_GETPATH = 50;
655
		const int F_GETPATH = 50;
648
		const int __DARWIN_MAXPATHLEN = 1024;
656
		const int __DARWIN_MAXPATHLEN = 1024;
657
		const int EINTR = 4;
649
		static readonly kevent[] emptyEventList = new System.IO.kevent[0];
658
		static readonly kevent[] emptyEventList = new System.IO.kevent[0];
650
		int maxFds = Int32.MaxValue;
659
		int maxFds = Int32.MaxValue;
651
660
Lines 665-687 Link Here
665
		string fixupPath = null;
674
		string fixupPath = null;
666
		string fullPathNoLastSlash = null;
675
		string fullPathNoLastSlash = null;
667
676
668
		[DllImport ("libc", EntryPoint="fcntl", CharSet=CharSet.Auto, SetLastError=true)]
677
		[DllImport ("libc", CharSet=CharSet.Auto, SetLastError=true)]
669
		static extern int fcntl (int file_names_by_descriptor, int cmd, StringBuilder sb);
678
		static extern int fcntl (int file_names_by_descriptor, int cmd, StringBuilder sb);
670
679
671
		[DllImport ("libc")]
680
		[DllImport ("libc", SetLastError=true)]
672
		extern static int open (string path, int flags, int mode_t);
681
		extern static int open (string path, int flags, int mode_t);
673
682
683
		[DllImport ("libc", CharSet=CharSet.Auto, SetLastError=true)]
684
		static extern IntPtr realpath (string pathname, StringBuilder sb);
685
674
		[DllImport ("libc")]
686
		[DllImport ("libc")]
675
		extern static int close (int fd);
687
		extern static int close (int fd);
676
688
677
		[DllImport ("libc")]
689
		[DllImport ("libc", SetLastError=true)]
678
		extern static int kqueue ();
690
		extern static int kqueue ();
679
691
680
		[DllImport ("libc")]
692
		[DllImport ("libc", SetLastError=true)]
681
		extern static int kevent (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, [In] ref timespec time);
693
		extern static int kevent (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, [In] ref timespec time);
682
694
683
		[MethodImplAttribute(MethodImplOptions.InternalCall)]
695
		[DllImport ("libc", EntryPoint="kevent", SetLastError=true)]
684
		extern static int kevent_notimeout (ref int kq, IntPtr ev, int nchanges, IntPtr evtlist, int nevents);
696
		extern static int kevent_notimeout (int kq, [In]kevent[] ev, int nchanges, [Out]kevent[] evtlist, int nevents, IntPtr ptr);
685
	}
697
	}
686
698
687
	class KeventWatcher : IFileWatcher
699
	class KeventWatcher : IFileWatcher

Return to bug 206593