Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit daba0cc

Browse files
committed
Eliminate closure allocation in FileSystemWatcher
1 parent 211499a commit daba0cc

File tree

1 file changed

+23
-16
lines changed

1 file changed

+23
-16
lines changed

src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Text;
88
using System.Threading;
99
using System.Threading.Tasks;
10-
using System.Runtime.InteropServices;
1110

1211
namespace System.IO
1312
{
@@ -299,7 +298,10 @@ private WatchedDirectory AddDirectoryWatchUnlocked(WatchedDirectory parent, stri
299298

300299
// Add a watch for the full path. If the path is already being watched, this will return
301300
// the existing descriptor. This works even in the case of a rename.
302-
int wd = (int)SysCall(fd => Interop.libc.inotify_add_watch(fd, fullPath, (uint)_notifyFilters));
301+
int wd = (int)SysCall(
302+
(fd, path, thisRef) => Interop.libc.inotify_add_watch(fd, path, (uint)thisRef._notifyFilters),
303+
fullPath,
304+
this);
303305

304306
// Then store the path information into our map.
305307
WatchedDirectory directoryEntry;
@@ -401,15 +403,15 @@ private void RemoveWatchedDirectoryUnlocked(WatchedDirectory directoryEntry, boo
401403
// And if the caller has requested, remove the associated inotify watch.
402404
if (removeInotify)
403405
{
404-
SysCall(fd =>
406+
SysCall((fd, dirEntry, _) =>
405407
{
406408
// Remove the inotify watch. This could fail if our state has become inconsistent
407409
// with the state of the world (e.g. due to lost events). So we don't want failures
408410
// to throw exceptions, but we do assert to detect coding problems during debugging.
409-
long result = Interop.libc.inotify_rm_watch(fd, directoryEntry.WatchDescriptor);
411+
long result = Interop.libc.inotify_rm_watch(fd, dirEntry.WatchDescriptor);
410412
Debug.Assert(result >= 0);
411413
return 0;
412-
});
414+
}, directoryEntry, 0);
413415
}
414416
}
415417

@@ -423,14 +425,14 @@ private void CancellationCallback()
423425
{
424426
// Remove all watches (inotiy_rm_watch) and clear out the map.
425427
// No additional watches will be added after this point.
426-
SysCall(fd => {
427-
foreach (int wd in _wdToPathMap.Keys)
428+
SysCall((fd, thisRef, _) => {
429+
foreach (int wd in thisRef._wdToPathMap.Keys)
428430
{
429431
int result = Interop.libc.inotify_rm_watch(fd, wd);
430432
Debug.Assert(result >= 0); // ignore errors; they're non-fatal, but they also shouldn't happen
431433
}
432434
return 0;
433-
});
435+
}, this, 0);
434436
_wdToPathMap.Clear();
435437
}
436438
}
@@ -628,15 +630,15 @@ private bool TryReadEvent(out NotifyEvent notifyEvent)
628630
{
629631
try
630632
{
631-
_bufferAvailable = (int)SysCall(fd => {
633+
_bufferAvailable = (int)SysCall((fd, thisRef, _) => {
632634
long result;
633-
fixed (byte* buf = _buffer)
635+
fixed (byte* buf = thisRef._buffer)
634636
{
635-
result = (long)Interop.libc.read(fd, buf, (IntPtr)_buffer.Length);
637+
result = (long)Interop.libc.read(fd, buf, (IntPtr)thisRef._buffer.Length);
636638
}
637-
Debug.Assert(result <= _buffer.Length);
639+
Debug.Assert(result <= thisRef._buffer.Length);
638640
return result;
639-
});
641+
}, this, 0);
640642
}
641643
catch (ArgumentException)
642644
{
@@ -710,10 +712,15 @@ private string ReadName(int position, int nameLength)
710712
/// and less than zero on failure. In the case of failure, errno is expected to
711713
/// be set to the relevant error code.
712714
/// </summary>
713-
/// <param name="handle">The SafeFileHandle that wraps the file descriptor to use with the system call.</param>
714715
/// <param name="sysCall">A delegate that invokes the system call. It's passed the associated file descriptor and should return the result.</param>
716+
/// <param name="arg1">The first argument to be passed to the system call, after the file descriptor.</param>
717+
/// <param name="arg2">The second argument to be passed to the system call.</param>
715718
/// <returns>The return value of the system call.</returns>
716-
private long SysCall(Func<int, long> sysCall)
719+
/// <remarks>
720+
/// Arguments are expected to be passed via <paramref name="arg1"/> and <paramref name="arg2"/>
721+
/// so as to avoid delegate and closure allocations at the call sites.
722+
/// </remarks>
723+
private long SysCall<TArg1, TArg2>(Func<int, TArg1, TArg2, long> sysCall, TArg1 arg1, TArg2 arg2)
717724
{
718725
bool gotRefOnHandle = false;
719726
try
@@ -726,7 +733,7 @@ private long SysCall(Func<int, long> sysCall)
726733
Debug.Assert(fd >= 0);
727734

728735
long result;
729-
while (Interop.CheckIo(result = sysCall(fd), isDirectory: true)) ;
736+
while (Interop.CheckIo(result = sysCall(fd, arg1, arg2), isDirectory: true));
730737
return result;
731738
}
732739
finally

0 commit comments

Comments
 (0)