1142 lines · 38.8 KB · C#
RawDownload
//******************************************************************************************************//  FileTracker.cs - Gbtc////  Copyright © 2014, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  04/29/2014 - Stephen C. Wills//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Threading;using System.Threading.Tasks;using System.Timers;using GSF.Threading;using CancellationToken = GSF.Threading.CancellationToken;using Timer = System.Timers.Timer; namespace GSF.IO{    /// <summary>    /// Defines strategies for enumerating    /// files in the watch directories.    /// </summary>    public enum FileEnumerationStrategy    {        /// <summary>        /// Enumerate all files sequentially.        /// </summary>        Sequential,         /// <summary>        /// Enumerates the watch directories in parallel,        /// but subdirectories are processed sequentially.        /// </summary>        ParallelWatchDirectories,         /// <summary>        /// Enumerates every directory, including subdirectories, in parallel.        /// </summary>        ParallelSubdirectories,         /// <summary>        /// Does not enumerate directories, relying only on        /// the file watcher to handle file processing events.        /// </summary>        None    }     /// <summary>    /// Arguments to events triggered by the <see cref="FileProcessor"/>.    /// </summary>    public class FileProcessorEventArgs : EventArgs    {        #region [ Members ]         // Fields        private readonly string m_fullPath;        private readonly bool m_raisedByFileWatcher;        private readonly Func<int> m_retryCounter;        private bool m_requeue;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="FileProcessorEventArgs"/> class.        /// </summary>        /// <param name="fullPath">The full path to the file to be processed.</param>        /// <param name="raisedByFileWatcher">Flag indicating whether this event was raised by the file watcher.</param>        /// <param name="retryCounter">The function that provides the value for <see cref="RetryCount"/>.</param>        public FileProcessorEventArgs(string fullPath, bool raisedByFileWatcher, Func<int> retryCounter)        {            m_fullPath = fullPath;            m_raisedByFileWatcher = raisedByFileWatcher;            m_retryCounter = retryCounter;        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets the full path to the file to be processed.        /// </summary>        public string FullPath        {            get            {                return m_fullPath;            }        }         /// <summary>        /// Gets the flag that indicates whether this event was raised by the file watcher.        /// </summary>        public bool RaisedByFileWatcher        {            get            {                return m_raisedByFileWatcher;            }        }         /// <summary>        /// Gets the number of file processing attempts.        /// </summary>        public int RetryCount        {            get            {                return m_retryCounter();            }        }         /// <summary>        /// Gets or sets a flag that indicates whether the        /// file should be requeued by the file processor.        /// </summary>        public bool Requeue        {            get            {                return m_requeue;            }            set            {                m_requeue = value;            }        }         #endregion    }     /// <summary>    /// Tracks files processed in a list of directories, and    /// notifies when new files become available to be processed.    /// </summary>    public sealed class FileProcessor : IDisposable    {        #region [ Members ]         // Nested Types        private sealed class TrackedDirectory : IDisposable        {            #region [ Members ]             // Events            private event EventHandler<EventArgs<List<string>>> ActivelyVisitedPathsRequested;             // Fields            private readonly FileProcessor m_fileProcessor;            private SafeFileWatcher m_fileWatcher;            private CancellationToken m_cancellationToken;            private bool m_disposed;             #endregion             #region [ Constructors ]             public TrackedDirectory(FileProcessor fileProcessor, string path)            {                m_fileProcessor = fileProcessor;                Path = path;                CreateFileWatcher();            }             #endregion             #region [ Properties ]             public string Path { get; }             public List<string> ActivelyEnumeratedPaths            {                get                {                    List<string> activelyVisitedPaths = new List<string>();                    EventArgs<List<string>> args = new EventArgs<List<string>>(activelyVisitedPaths);                    ActivelyVisitedPathsRequested?.Invoke(this, args);                    return activelyVisitedPaths;                }            }             #endregion             #region [ Methods ]             public async Task EnumerateAsync(FileEnumerationStrategy fileEnumerationStrategy)            {                if (m_disposed)                    return;                 CancellationToken cancellationToken = new CancellationToken();                Interlocked.Exchange(ref m_cancellationToken, cancellationToken);                DirectoryInfo directory = new DirectoryInfo(Path);                await EnumerateDirectoryAsync(directory, fileEnumerationStrategy, cancellationToken);            }             public void CancelEnumeration()            {                CancellationToken cancellationToken = Interlocked.CompareExchange(ref m_cancellationToken, null, null);                cancellationToken?.Cancel();            }             public void CheckFileWatcher()            {                if (m_disposed)                    return;                 if (!m_fileWatcher.EnableRaisingEvents)                {                    DisposeFileWatcher();                    CreateFileWatcher();                }            }             public void Dispose()            {                if (m_disposed)                    return;                 try                {                    CancelEnumeration();                    DisposeFileWatcher();                }                finally                {                    m_disposed = true;                }            }             private async Task EnumerateDirectoryAsync(DirectoryInfo directory, FileEnumerationStrategy fileEnumerationStrategy, CancellationToken cancellationToken)            {                bool parallelSubdirectories =                    fileEnumerationStrategy == FileEnumerationStrategy.ParallelSubdirectories;                 LogicalThread thread = LogicalThread.CurrentThread;                 if (parallelSubdirectories)                {                    LogicalThreadScheduler scheduler = m_fileProcessor.m_threadScheduler;                    thread = scheduler.CreateThread();                    await thread.Join();                }                 async Task ForEach(IEnumerable<Task> tasks)                {                    foreach (Task task in tasks)                    {                        if (cancellationToken.IsCancelled)                            return;                         await task;                        await thread.Yield();                    }                }                 LogicalThread processingThread = m_fileProcessor.m_processingThread;                string activePath = null;                 async Task VisitSubdirectoryAsync(DirectoryInfo subdirectory)                {                    activePath = subdirectory.FullName;                     if (cancellationToken.IsCancelled)                        return;                     if (m_fileProcessor.MatchesFolderExclusion(subdirectory.FullName))                        return;                     await EnumerateDirectoryAsync(subdirectory, fileEnumerationStrategy, cancellationToken);                }                 async Task<Task> VisitFileAsync(FileInfo file)                {                    activePath = file.FullName;                     if (cancellationToken.IsCancelled)                        return Task.CompletedTask;                     async Task InvokeOnProcessingThread(Action action)                    {                        await processingThread.Join();                        action();                    }                     DateTime lastWriteTime = file.LastWriteTimeUtc;                    void Process() => m_fileProcessor.TouchAndProcess(file.FullName, lastWriteTime, false);                    void Skip() => m_fileProcessor.TouchAndSkip(file.FullName, lastWriteTime);                     if (!m_fileProcessor.MatchesFilter(file.FullName))                        return InvokeOnProcessingThread(Skip);                     Task processTask = InvokeOnProcessingThread(Process);                    await thread.Yield();                    return processTask;                }                 async Task EnumerateSubdirectoriesAsync()                {                    Func<IEnumerable<Task>, Task> whenAll =                        parallelSubdirectories ? Task.WhenAll : ForEach;                     IEnumerable<Task> subdirectoryTasks = FilePath                        .EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly, m_fileProcessor.OnError)                        .Select(VisitSubdirectoryAsync);                     await whenAll(subdirectoryTasks);                }                 async Task EnumerateFilesAsync()                {                    IEnumerable<Task<Task>> fileTasks = FilePath                        .EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly)                        .Select(VisitFileAsync);                     List<Task> processTasks = new List<Task>();                     foreach (Task<Task> fileTask in fileTasks)                        processTasks.Add(await fileTask);                     await Task.WhenAll(processTasks);                }                 EventHandler<EventArgs<List<string>>> handler = (sender, args) =>                {                    if (!(activePath is null))                        args.Argument.Add(activePath);                };                 try                {                    ActivelyVisitedPathsRequested += handler;                     if (parallelSubdirectories)                    {                        Task subdirectoriesTask = EnumerateSubdirectoriesAsync();                        await EnumerateFilesAsync();                        ActivelyVisitedPathsRequested -= handler;                        await subdirectoriesTask;                    }                    else                    {                        await EnumerateFilesAsync();                        await EnumerateSubdirectoriesAsync();                    }                }                finally                {                    ActivelyVisitedPathsRequested -= handler;                }            }             private void CreateFileWatcher()            {                m_fileWatcher = new SafeFileWatcher(Path);                m_fileWatcher.InternalBufferSize = m_fileProcessor.InternalBufferSize;                m_fileWatcher.IncludeSubdirectories = true;                 m_fileWatcher.Created += m_fileProcessor.Watcher_Created;                m_fileWatcher.Changed += m_fileProcessor.Watcher_Changed;                m_fileWatcher.Renamed += m_fileProcessor.Watcher_Renamed;                m_fileWatcher.Deleted += m_fileProcessor.Watcher_Deleted;                m_fileWatcher.Error += m_fileProcessor.Watcher_Error;                 m_fileWatcher.EnableRaisingEvents = true;            }             private void DisposeFileWatcher()            {                m_fileWatcher.Created -= m_fileProcessor.Watcher_Created;                m_fileWatcher.Changed -= m_fileProcessor.Watcher_Changed;                m_fileWatcher.Renamed -= m_fileProcessor.Watcher_Renamed;                m_fileWatcher.Deleted -= m_fileProcessor.Watcher_Deleted;                m_fileWatcher.Error -= m_fileProcessor.Watcher_Error;                m_fileWatcher.Dispose();            }             #endregion        }         // Constants         /// <summary>        /// Default value for the <see cref="Filter"/> property.        /// </summary>        public const string DefaultFilter = @"**\*";         /// <summary>        /// Default value for the <see cref="FolderExclusion"/> property.        /// </summary>        public const string DefaultFolderExclusion = "";         /// <summary>        /// Default value for the <see cref="TrackChanges"/> property.        /// </summary>        public const bool DefaultTrackChanges = false;         /// <summary>        /// Default value for the <see cref="InternalBufferSize"/> property.        /// </summary>        public const int DefaultInternalBufferSize = 8192;         /// <summary>        /// Default value for the <see cref="EnumerationStrategy"/> property.        /// </summary>        public const FileEnumerationStrategy DefaultEnumerationStrategy = FileEnumerationStrategy.ParallelSubdirectories;         // Events         /// <summary>        /// Event triggered when a file is to be processed.        /// </summary>        public event EventHandler<FileProcessorEventArgs> Processing;         /// <summary>        /// Event triggered when an unexpected error occurs during        /// normal operation of the <see cref="FileProcessor"/>.        /// </summary>        public event EventHandler<ErrorEventArgs> Error;         // Fields        private string m_filter;        private string m_folderExclusion;        private bool m_trackChanges;        private int m_internalBufferSize;        private FileEnumerationStrategy m_enumerationStrategy;        private bool m_orderedEnumeration;         private readonly object m_trackedDirectoriesLock;        private readonly List<TrackedDirectory> m_trackedDirectories;        private readonly TaskSynchronizedOperation m_enumerationOperation;         private readonly LogicalThreadScheduler m_threadScheduler;        private readonly LogicalThread m_processingThread;        private readonly LogicalThread m_watcherThread;        private readonly LogicalThread m_sequentialEnumerationThread;        private readonly Timer m_fileWatchTimer;        private readonly ManagedCancellationTokenSource m_requeueTokenSource;         private readonly Dictionary<string, DateTime> m_touchedFiles;         private int m_processedFileCount;        private int m_skippedFileCount;        private int m_requeuedFileCount;         private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="FileProcessor"/> class.        /// </summary>        public FileProcessor()        {            m_filter = DefaultFilter;            m_folderExclusion = DefaultFolderExclusion;            m_trackChanges = DefaultTrackChanges;            m_internalBufferSize = DefaultInternalBufferSize;            m_enumerationStrategy = DefaultEnumerationStrategy;             m_trackedDirectoriesLock = new object();            m_trackedDirectories = new List<TrackedDirectory>();            m_enumerationOperation = new TaskSynchronizedOperation(EnumerateWatchDirectoriesAsync, OnError);             m_threadScheduler = new LogicalThreadScheduler(2);            m_threadScheduler.UnhandledException += (sender, args) => OnError(args.Argument);            m_processingThread = m_threadScheduler.CreateThread();            m_watcherThread = m_threadScheduler.CreateThread();            m_sequentialEnumerationThread = m_threadScheduler.CreateThread();            m_fileWatchTimer = new Timer(15000);            m_fileWatchTimer.Elapsed += FileWatchTimer_Elapsed;            m_requeueTokenSource = new ManagedCancellationTokenSource();             m_touchedFiles = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);        }         #endregion 

Lines 1–500 of 1142.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t