From 93f022829bd6cc3b682f9543b3a732330f49542b Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Sat, 14 Aug 2021 20:51:35 +0300 Subject: [PATCH 1/7] Annotate src --- .../src/IPollingChangeToken.cs | 2 +- .../src/Internal/FileSystemInfoHelper.cs | 2 +- .../src/Internal/PhysicalDirectoryContents.cs | 4 ++- ...t.Extensions.FileProviders.Physical.csproj | 1 + .../src/PhysicalFileProvider.cs | 21 ++++++++------ .../src/PhysicalFilesWatcher.cs | 28 +++++++++++-------- .../src/PollingFileChangeToken.cs | 24 ++++++++-------- .../src/PollingWildCardChangeToken.cs | 22 ++++++++------- 8 files changed, 60 insertions(+), 44 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/IPollingChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/IPollingChangeToken.cs index 8806586c29748..51e699f51e3e9 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/IPollingChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/IPollingChangeToken.cs @@ -8,6 +8,6 @@ namespace Microsoft.Extensions.FileProviders { internal interface IPollingChangeToken : IChangeToken { - CancellationTokenSource CancellationTokenSource { get; } + CancellationTokenSource? CancellationTokenSource { get; } } } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs index bc02bf46c1ef9..267115845beb2 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs @@ -53,7 +53,7 @@ public static bool IsExcluded(FileSystemInfo fileSystemInfo, ExclusionFilters fi { try { - FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); + FileSystemInfo? targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); if (targetInfo != null && targetInfo.Exists) { return targetInfo.LastWriteTimeUtc; diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/PhysicalDirectoryContents.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/PhysicalDirectoryContents.cs index f42804d65f90e..3fa750bb40765 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/PhysicalDirectoryContents.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/PhysicalDirectoryContents.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using Microsoft.Extensions.FileProviders.Physical; @@ -15,7 +16,7 @@ namespace Microsoft.Extensions.FileProviders.Internal /// public class PhysicalDirectoryContents : IDirectoryContents { - private IEnumerable _entries; + private IEnumerable? _entries; private readonly string _directory; private readonly ExclusionFilters _filters; @@ -54,6 +55,7 @@ IEnumerator IEnumerable.GetEnumerator() return _entries.GetEnumerator(); } + [MemberNotNull(nameof(_entries))] private void EnsureInitialized() { try diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj index 242f042fb3262..c0790fbf1e3aa 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj @@ -3,6 +3,7 @@ Microsoft.Extensions.FileProviders $(NetCoreAppCurrent);netstandard2.0;net461 + enable true true File provider for physical files for Microsoft.Extensions.FileProviders. diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs index de58daa3906d4..8f1ca136a1da4 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using Microsoft.Extensions.FileProviders.Internal; @@ -28,7 +29,7 @@ public class PhysicalFileProvider : IFileProvider, IDisposable private readonly ExclusionFilters _filters; private readonly Func _fileWatcherFactory; - private PhysicalFilesWatcher _fileWatcher; + private PhysicalFilesWatcher? _fileWatcher; private bool _fileWatcherInitialized; private object _fileWatcherLock = new object(); @@ -145,7 +146,7 @@ internal PhysicalFilesWatcher FileWatcher ref _fileWatcher, ref _fileWatcherInitialized, ref _fileWatcherLock, - _fileWatcherFactory); + _fileWatcherFactory) ?? throw new NullReferenceException(nameof(_fileWatcher)); } set { @@ -160,7 +161,7 @@ internal PhysicalFilesWatcher CreateFileWatcher() { string root = PathUtils.EnsureTrailingSlash(Path.GetFullPath(Root)); - FileSystemWatcher watcher; + FileSystemWatcher? watcher; #if NETCOREAPP // For browser we will proactively fallback to polling since FileSystemWatcher is not supported. if (OperatingSystem.IsBrowser()) @@ -182,9 +183,11 @@ internal PhysicalFilesWatcher CreateFileWatcher() }; } + [MemberNotNull(nameof(_usePollingFileWatcher))] + [MemberNotNull(nameof(_useActivePolling))] private void ReadPollingEnvironmentVariables() { - string environmentValue = Environment.GetEnvironmentVariable(PollingEnvironmentKey); + string? environmentValue = Environment.GetEnvironmentVariable(PollingEnvironmentKey); bool pollForChanges = string.Equals(environmentValue, "1", StringComparison.Ordinal) || string.Equals(environmentValue, "true", StringComparison.OrdinalIgnoreCase); @@ -222,7 +225,7 @@ protected virtual void Dispose(bool disposing) /// public string Root { get; } - private string GetFullPath(string path) + private string? GetFullPath(string path) { if (PathUtils.PathNavigatesAboveRoot(path)) { @@ -273,7 +276,7 @@ public IFileInfo GetFileInfo(string subpath) return new NotFoundFileInfo(subpath); } - string fullPath = GetFullPath(subpath); + string? fullPath = GetFullPath(subpath); if (fullPath == null) { return new NotFoundFileInfo(subpath); @@ -297,7 +300,7 @@ public IFileInfo GetFileInfo(string subpath) /// is absolute, if the directory does not exist, or has invalid /// characters. /// - public IDirectoryContents GetDirectoryContents(string subpath) + public IDirectoryContents GetDirectoryContents(string? subpath) { try { @@ -315,7 +318,7 @@ public IDirectoryContents GetDirectoryContents(string subpath) return NotFoundDirectoryContents.Singleton; } - string fullPath = GetFullPath(subpath); + string? fullPath = GetFullPath(subpath); if (fullPath == null || !Directory.Exists(fullPath)) { return NotFoundDirectoryContents.Singleton; @@ -346,7 +349,7 @@ public IDirectoryContents GetDirectoryContents(string subpath) /// characters or if is an absolute path or outside the root directory specified in the /// constructor . /// - public IChangeToken Watch(string filter) + public IChangeToken Watch(string? filter) { if (filter == null || PathUtils.HasInvalidFilterChars(filter)) { diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs index 81df7c51532bf..142aed674ae27 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs @@ -25,7 +25,7 @@ namespace Microsoft.Extensions.FileProviders.Physical /// public class PhysicalFilesWatcher : IDisposable { - private static readonly Action _cancelTokenSource = state => ((CancellationTokenSource)state).Cancel(); + private static readonly Action _cancelTokenSource = state => ((CancellationTokenSource?)state)?.Cancel(); internal static TimeSpan DefaultPollingInterval = TimeSpan.FromSeconds(4); @@ -34,12 +34,12 @@ public class PhysicalFilesWatcher : IDisposable private readonly ConcurrentDictionary _wildcardTokenLookup = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private readonly FileSystemWatcher _fileWatcher; + private readonly FileSystemWatcher? _fileWatcher; private readonly object _fileWatcherLock = new object(); private readonly string _root; private readonly ExclusionFilters _filters; - private Timer _timer; + private Timer? _timer; private bool _timerInitialzed; private object _timerLock = new object(); private Func _timerFactory; @@ -57,7 +57,7 @@ public class PhysicalFilesWatcher : IDisposable /// public PhysicalFilesWatcher( string root, - FileSystemWatcher fileSystemWatcher, + FileSystemWatcher? fileSystemWatcher, bool pollForChanges) : this(root, fileSystemWatcher, pollForChanges, ExclusionFilters.Sensitive) { @@ -76,7 +76,7 @@ public PhysicalFilesWatcher( /// Specifies which files or directories are excluded. Notifications of changes to are not raised to these. public PhysicalFilesWatcher( string root, - FileSystemWatcher fileSystemWatcher, + FileSystemWatcher? fileSystemWatcher, bool pollForChanges, ExclusionFilters filters) { @@ -369,8 +369,9 @@ private void ReportChangeForMatchedEntries(string path) foreach (System.Collections.Generic.KeyValuePair wildCardEntry in _wildcardTokenLookup) { - PatternMatchingResult matchResult = wildCardEntry.Value.Matcher.Match(path); - if (matchResult.HasMatches && + PatternMatchingResult? matchResult = wildCardEntry.Value.Matcher?.Match(path); + if (matchResult != null && + matchResult.HasMatches && _wildcardTokenLookup.TryRemove(wildCardEntry.Key, out matchInfo)) { CancelToken(matchInfo); @@ -443,8 +444,13 @@ private static void CancelToken(ChangeTokenInfo matchInfo) TaskScheduler.Default); } - internal static void RaiseChangeEvents(object state) + internal static void RaiseChangeEvents(object? state) { + if (state == null) + { + throw new ArgumentNullException(nameof(state)); + } + // Iterating over a concurrent bag gives us a point in time snapshot making it safe // to remove items from it. var changeTokens = (ConcurrentDictionary)state; @@ -466,7 +472,7 @@ internal static void RaiseChangeEvents(object state) // We're already on a background thread, don't need to spawn a background Task to cancel the CTS try { - token.CancellationTokenSource.Cancel(); + token.CancellationTokenSource?.Cancel(); } catch { @@ -487,7 +493,7 @@ public ChangeTokenInfo( public ChangeTokenInfo( CancellationTokenSource tokenSource, CancellationChangeToken changeToken, - Matcher matcher) + Matcher? matcher) { TokenSource = tokenSource; ChangeToken = changeToken; @@ -498,7 +504,7 @@ public ChangeTokenInfo( public CancellationChangeToken ChangeToken { get; } - public Matcher Matcher { get; } + public Matcher? Matcher { get; } } } } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index f49eacb4cbdb5..20be017ae3341 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using Microsoft.Extensions.Primitives; @@ -25,11 +26,11 @@ namespace Microsoft.Extensions.FileProviders.Physical public class PollingFileChangeToken : IPollingChangeToken { private readonly FileInfo _fileInfo; - private DateTime _previousWriteTimeUtc; + private DateTime? _previousWriteTimeUtc; private DateTime _lastCheckedTimeUtc; private bool _hasChanged; - private CancellationTokenSource _tokenSource; - private CancellationChangeToken _changeToken; + private CancellationTokenSource? _tokenSource; + private CancellationChangeToken? _changeToken; /// /// Initializes a new instance of that polls the specified file for changes as @@ -45,7 +46,7 @@ public PollingFileChangeToken(FileInfo fileInfo) // Internal for unit testing internal static TimeSpan PollingInterval { get; set; } = PhysicalFilesWatcher.DefaultPollingInterval; - private DateTime GetLastWriteTimeUtc() + private DateTime? GetLastWriteTimeUtc() { _fileInfo.Refresh(); @@ -65,7 +66,7 @@ private DateTime GetLastWriteTimeUtc() catch (IOException) { } // https://github.com/dotnet/runtime/issues/57221 } - return lastWriteTimeUtc.Value; + return lastWriteTimeUtc; } /// @@ -73,7 +74,8 @@ private DateTime GetLastWriteTimeUtc() /// public bool ActiveChangeCallbacks { get; internal set; } - internal CancellationTokenSource CancellationTokenSource + [DisallowNull] + internal CancellationTokenSource? CancellationTokenSource { get => _tokenSource; set @@ -85,7 +87,7 @@ internal CancellationTokenSource CancellationTokenSource } } - CancellationTokenSource IPollingChangeToken.CancellationTokenSource => CancellationTokenSource; + CancellationTokenSource? IPollingChangeToken.CancellationTokenSource => CancellationTokenSource; /// /// True when the file has changed since the change token was created. Once the file changes, this value is always true @@ -109,8 +111,8 @@ public bool HasChanged return _hasChanged; } - DateTime lastWriteTimeUtc = GetLastWriteTimeUtc(); - if (_previousWriteTimeUtc != lastWriteTimeUtc) + DateTime? lastWriteTimeUtc = GetLastWriteTimeUtc(); + if (_previousWriteTimeUtc != null && _previousWriteTimeUtc != lastWriteTimeUtc) { _previousWriteTimeUtc = lastWriteTimeUtc; _hasChanged = true; @@ -127,9 +129,9 @@ public bool HasChanged /// This parameter is ignored /// This parameter is ignored /// A disposable object that noops when disposed - public IDisposable RegisterChangeCallback(Action callback, object state) + public IDisposable RegisterChangeCallback(Action callback, object? state) { - if (!ActiveChangeCallbacks) + if (!ActiveChangeCallbacks || _changeToken == null) { return EmptyDisposable.Instance; } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs index 28148d2315b96..adbb436a87f4f 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -20,15 +21,15 @@ namespace Microsoft.Extensions.FileProviders.Physical public class PollingWildCardChangeToken : IPollingChangeToken { private static readonly byte[] Separator = Encoding.Unicode.GetBytes("|"); - private readonly object _enumerationLock = new object(); + private readonly object _enumerationLock = new(); private readonly DirectoryInfoBase _directoryInfo; private readonly Matcher _matcher; private bool _changed; private DateTime? _lastScanTimeUtc; - private byte[] _byteBuffer; - private byte[] _previousHash; - private CancellationTokenSource _tokenSource; - private CancellationChangeToken _changeToken; + private byte[]? _byteBuffer; + private byte[]? _previousHash; + private CancellationTokenSource? _tokenSource; + private CancellationChangeToken? _changeToken; /// /// Initializes a new instance of . @@ -65,7 +66,8 @@ internal PollingWildCardChangeToken( // Internal for unit testing. internal TimeSpan PollingInterval { get; set; } = PhysicalFilesWatcher.DefaultPollingInterval; - internal CancellationTokenSource CancellationTokenSource + [DisallowNull] + internal CancellationTokenSource? CancellationTokenSource { get => _tokenSource; set @@ -77,7 +79,7 @@ internal CancellationTokenSource CancellationTokenSource } } - CancellationTokenSource IPollingChangeToken.CancellationTokenSource => CancellationTokenSource; + CancellationTokenSource? IPollingChangeToken.CancellationTokenSource => CancellationTokenSource; private IClock Clock { get; } @@ -147,7 +149,7 @@ protected virtual DateTime GetLastWriteUtc(string path) return FileSystemInfoHelper.GetFileLinkTargetLastWriteTimeUtc(filePath) ?? File.GetLastWriteTimeUtc(filePath); } - private static bool ArrayEquals(byte[] previousHash, byte[] currentHash) + private static bool ArrayEquals(byte[]? previousHash, byte[] currentHash) { if (previousHash == null) { @@ -191,9 +193,9 @@ private void ComputeHash(IncrementalHash sha256, string path, DateTime lastChang sha256.AppendData(Separator, 0, Separator.Length); } - IDisposable IChangeToken.RegisterChangeCallback(Action callback, object state) + IDisposable IChangeToken.RegisterChangeCallback(Action callback, object? state) { - if (!ActiveChangeCallbacks) + if (!ActiveChangeCallbacks || _changeToken == null) { return EmptyDisposable.Instance; } From dacbde2e259b1b5820bfbffc310e6437db3de5cb Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Sat, 14 Aug 2021 20:59:11 +0300 Subject: [PATCH 2/7] Annotate ref --- .../Microsoft.Extensions.FileProviders.Physical.cs | 12 ++++++------ ...icrosoft.Extensions.FileProviders.Physical.csproj | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs index 658a47bbeb6f5..b194b5bf5357e 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs @@ -16,9 +16,9 @@ public PhysicalFileProvider(string root, Microsoft.Extensions.FileProviders.Phys public void Dispose() { } protected virtual void Dispose(bool disposing) { } ~PhysicalFileProvider() { } - public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string? subpath) { throw null; } public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } - public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string? filter) { throw null; } } } namespace Microsoft.Extensions.FileProviders.Internal @@ -67,8 +67,8 @@ public PhysicalFileInfo(System.IO.FileInfo info) { } } public partial class PhysicalFilesWatcher : System.IDisposable { - public PhysicalFilesWatcher(string root, System.IO.FileSystemWatcher fileSystemWatcher, bool pollForChanges) { } - public PhysicalFilesWatcher(string root, System.IO.FileSystemWatcher fileSystemWatcher, bool pollForChanges, Microsoft.Extensions.FileProviders.Physical.ExclusionFilters filters) { } + public PhysicalFilesWatcher(string root, System.IO.FileSystemWatcher? fileSystemWatcher, bool pollForChanges) { } + public PhysicalFilesWatcher(string root, System.IO.FileSystemWatcher? fileSystemWatcher, bool pollForChanges, Microsoft.Extensions.FileProviders.Physical.ExclusionFilters filters) { } public Microsoft.Extensions.Primitives.IChangeToken CreateFileChangeToken(string filter) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } @@ -79,7 +79,7 @@ public partial class PollingFileChangeToken : Microsoft.Extensions.Primitives.IC public PollingFileChangeToken(System.IO.FileInfo fileInfo) { } public bool ActiveChangeCallbacks { get { throw null; } } public bool HasChanged { get { throw null; } } - public System.IDisposable RegisterChangeCallback(System.Action callback, object state) { throw null; } + public System.IDisposable RegisterChangeCallback(System.Action callback, object? state) { throw null; } } public partial class PollingWildCardChangeToken : Microsoft.Extensions.Primitives.IChangeToken { @@ -87,6 +87,6 @@ public PollingWildCardChangeToken(string root, string pattern) { } public bool ActiveChangeCallbacks { get { throw null; } } public bool HasChanged { get { throw null; } } protected virtual System.DateTime GetLastWriteUtc(string path) { throw null; } - System.IDisposable Microsoft.Extensions.Primitives.IChangeToken.RegisterChangeCallback(System.Action callback, object state) { throw null; } + System.IDisposable Microsoft.Extensions.Primitives.IChangeToken.RegisterChangeCallback(System.Action callback, object? state) { throw null; } } } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj index 0f7dc64cb84a2..be7871ffa187c 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj @@ -1,6 +1,7 @@ $(NetCoreAppCurrent);netstandard2.0;net461 + enable From 5f23dca10d3244d4b2f7b29e052d00f682a765d1 Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Sat, 14 Aug 2021 23:03:06 +0300 Subject: [PATCH 3/7] Update PollingFileChangeToken.cs --- .../src/PollingFileChangeToken.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index 20be017ae3341..0075a3127e462 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -112,7 +112,7 @@ public bool HasChanged } DateTime? lastWriteTimeUtc = GetLastWriteTimeUtc(); - if (_previousWriteTimeUtc != null && _previousWriteTimeUtc != lastWriteTimeUtc) + if (lastWriteTimeUtc != null && _previousWriteTimeUtc != lastWriteTimeUtc) { _previousWriteTimeUtc = lastWriteTimeUtc; _hasChanged = true; From 2139b6cab87d83568e313dcde7101613029f54cf Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Sun, 15 Aug 2021 04:26:23 +0300 Subject: [PATCH 4/7] Update PollingFileChangeToken.cs --- .../src/PollingFileChangeToken.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index 6d8f70b47f7d0..ae219e86246d2 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using Microsoft.Extensions.Primitives; @@ -28,8 +29,8 @@ public class PollingFileChangeToken : IPollingChangeToken private DateTime _previousWriteTimeUtc; private DateTime _lastCheckedTimeUtc; private bool _hasChanged; - private CancellationTokenSource _tokenSource; - private CancellationChangeToken _changeToken; + private CancellationTokenSource? _tokenSource; + private CancellationChangeToken? _changeToken; /// /// Initializes a new instance of that polls the specified file for changes as @@ -73,7 +74,8 @@ private DateTime GetLastWriteTimeUtc() /// public bool ActiveChangeCallbacks { get; internal set; } - internal CancellationTokenSource CancellationTokenSource + [DisallowNull] + internal CancellationTokenSource? CancellationTokenSource { get => _tokenSource; set @@ -85,7 +87,7 @@ internal CancellationTokenSource CancellationTokenSource } } - CancellationTokenSource IPollingChangeToken.CancellationTokenSource => CancellationTokenSource; + CancellationTokenSource? IPollingChangeToken.CancellationTokenSource => CancellationTokenSource; /// /// True when the file has changed since the change token was created. Once the file changes, this value is always true @@ -127,9 +129,9 @@ public bool HasChanged /// This parameter is ignored /// This parameter is ignored /// A disposable object that noops when disposed - public IDisposable RegisterChangeCallback(Action callback, object state) + public IDisposable RegisterChangeCallback(Action callback, object? state) { - if (!ActiveChangeCallbacks) + if (!ActiveChangeCallbacks || _changeToken == null) { return EmptyDisposable.Instance; } From 0f10cd613c6880aace9a11f540d2b2bdcfa01bc6 Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Mon, 16 Aug 2021 21:43:11 +0300 Subject: [PATCH 5/7] Avoid product changes Issue to remove/review "!": https://github.com/dotnet/runtime/issues/57518 --- .../src/PhysicalFileProvider.cs | 8 +++--- .../src/PhysicalFilesWatcher.cs | 26 +++++++------------ .../src/PollingFileChangeToken.cs | 4 +-- .../src/PollingWildCardChangeToken.cs | 4 +-- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs index 8f1ca136a1da4..9813a4c903045 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFileProvider.cs @@ -31,7 +31,7 @@ public class PhysicalFileProvider : IFileProvider, IDisposable private readonly Func _fileWatcherFactory; private PhysicalFilesWatcher? _fileWatcher; private bool _fileWatcherInitialized; - private object _fileWatcherLock = new object(); + private object _fileWatcherLock = new(); private bool? _usePollingFileWatcher; private bool? _useActivePolling; @@ -146,7 +146,7 @@ internal PhysicalFilesWatcher FileWatcher ref _fileWatcher, ref _fileWatcherInitialized, ref _fileWatcherLock, - _fileWatcherFactory) ?? throw new NullReferenceException(nameof(_fileWatcher)); + _fileWatcherFactory)!; } set { @@ -300,7 +300,7 @@ public IFileInfo GetFileInfo(string subpath) /// is absolute, if the directory does not exist, or has invalid /// characters. /// - public IDirectoryContents GetDirectoryContents(string? subpath) + public IDirectoryContents GetDirectoryContents(string subpath) { try { @@ -349,7 +349,7 @@ public IDirectoryContents GetDirectoryContents(string? subpath) /// characters or if is an absolute path or outside the root directory specified in the /// constructor . /// - public IChangeToken Watch(string? filter) + public IChangeToken Watch(string filter) { if (filter == null || PathUtils.HasInvalidFilterChars(filter)) { diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs index 142aed674ae27..56f076b6b4d8d 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs @@ -25,23 +25,21 @@ namespace Microsoft.Extensions.FileProviders.Physical /// public class PhysicalFilesWatcher : IDisposable { - private static readonly Action _cancelTokenSource = state => ((CancellationTokenSource?)state)?.Cancel(); + private static readonly Action _cancelTokenSource = state => ((CancellationTokenSource?)state)!.Cancel(); internal static TimeSpan DefaultPollingInterval = TimeSpan.FromSeconds(4); - private readonly ConcurrentDictionary _filePathTokenLookup = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private readonly ConcurrentDictionary _wildcardTokenLookup = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _filePathTokenLookup = new(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _wildcardTokenLookup = new(StringComparer.OrdinalIgnoreCase); private readonly FileSystemWatcher? _fileWatcher; - private readonly object _fileWatcherLock = new object(); + private readonly object _fileWatcherLock = new(); private readonly string _root; private readonly ExclusionFilters _filters; private Timer? _timer; private bool _timerInitialzed; - private object _timerLock = new object(); + private object _timerLock = new(); private Func _timerFactory; private bool _disposed; @@ -369,9 +367,8 @@ private void ReportChangeForMatchedEntries(string path) foreach (System.Collections.Generic.KeyValuePair wildCardEntry in _wildcardTokenLookup) { - PatternMatchingResult? matchResult = wildCardEntry.Value.Matcher?.Match(path); - if (matchResult != null && - matchResult.HasMatches && + PatternMatchingResult matchResult = wildCardEntry.Value.Matcher!.Match(path); + if (matchResult.HasMatches && _wildcardTokenLookup.TryRemove(wildCardEntry.Key, out matchInfo)) { CancelToken(matchInfo); @@ -446,14 +443,9 @@ private static void CancelToken(ChangeTokenInfo matchInfo) internal static void RaiseChangeEvents(object? state) { - if (state == null) - { - throw new ArgumentNullException(nameof(state)); - } - // Iterating over a concurrent bag gives us a point in time snapshot making it safe // to remove items from it. - var changeTokens = (ConcurrentDictionary)state; + var changeTokens = (ConcurrentDictionary)state!; foreach (System.Collections.Generic.KeyValuePair item in changeTokens) { IPollingChangeToken token = item.Key; @@ -472,7 +464,7 @@ internal static void RaiseChangeEvents(object? state) // We're already on a background thread, don't need to spawn a background Task to cancel the CTS try { - token.CancellationTokenSource?.Cancel(); + token.CancellationTokenSource!.Cancel(); } catch { diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index ae219e86246d2..fbfe247cbc2cc 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -131,12 +131,12 @@ public bool HasChanged /// A disposable object that noops when disposed public IDisposable RegisterChangeCallback(Action callback, object? state) { - if (!ActiveChangeCallbacks || _changeToken == null) + if (!ActiveChangeCallbacks) { return EmptyDisposable.Instance; } - return _changeToken.RegisterChangeCallback(callback, state); + return _changeToken!.RegisterChangeCallback(callback, state); } } } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs index adbb436a87f4f..df2b598b86136 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs @@ -195,12 +195,12 @@ private void ComputeHash(IncrementalHash sha256, string path, DateTime lastChang IDisposable IChangeToken.RegisterChangeCallback(Action callback, object? state) { - if (!ActiveChangeCallbacks || _changeToken == null) + if (!ActiveChangeCallbacks) { return EmptyDisposable.Instance; } - return _changeToken.RegisterChangeCallback(callback, state); + return _changeToken!.RegisterChangeCallback(callback, state); } } } From 67461920a79336caf997a6e94a14d0c17ea6186f Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Mon, 16 Aug 2021 21:47:37 +0300 Subject: [PATCH 6/7] Update ref --- .../ref/Microsoft.Extensions.FileProviders.Physical.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs index b194b5bf5357e..7b5c0b3433e0d 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.cs @@ -16,9 +16,9 @@ public PhysicalFileProvider(string root, Microsoft.Extensions.FileProviders.Phys public void Dispose() { } protected virtual void Dispose(bool disposing) { } ~PhysicalFileProvider() { } - public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string? subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } - public Microsoft.Extensions.Primitives.IChangeToken Watch(string? filter) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; } } } namespace Microsoft.Extensions.FileProviders.Internal From a5aded461d96a7775daf7c46d09800ba704fc4ff Mon Sep 17 00:00:00 2001 From: Maksym Koshovyi Date: Tue, 17 Aug 2021 11:18:26 +0300 Subject: [PATCH 7/7] Debug.Assert --- .../src/PhysicalFilesWatcher.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs index 56f076b6b4d8d..fb179091e8df0 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PhysicalFilesWatcher.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; +using System.Diagnostics; using System.IO; using System.Runtime.Versioning; using System.Security; @@ -443,9 +444,11 @@ private static void CancelToken(ChangeTokenInfo matchInfo) internal static void RaiseChangeEvents(object? state) { + Debug.Assert(state != null); + // Iterating over a concurrent bag gives us a point in time snapshot making it safe // to remove items from it. - var changeTokens = (ConcurrentDictionary)state!; + var changeTokens = (ConcurrentDictionary)state; foreach (System.Collections.Generic.KeyValuePair item in changeTokens) { IPollingChangeToken token = item.Key;