1600 lines · 54.1 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.Diagnostics.CodeAnalysis;using System.IO;using System.Linq;using System.Text;using System.Threading;using System.Timers;using GSF.Collections;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_alreadyProcessed;        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="alreadyProcessed">Flag indicating whether this file has been processed before.</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 alreadyProcessed, bool raisedByFileWatcher, Func<int> retryCounter)        {            m_fullPath = fullPath;            m_alreadyProcessed = alreadyProcessed;            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 file has been processed before.        /// </summary>        public bool AlreadyProcessed        {            get            {                return m_alreadyProcessed;            }        }         /// <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 ]             // Fields            private FileProcessor m_fileProcessor;            private SafeFileWatcher m_fileWatcher;            private FileEnumerator m_fileEnumerator;            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 bool IsEnumerating            {                get                {                    return m_fileEnumerator?.Active ?? false;                }            }             public List<string> ActivelyEnumeratedPaths            {                get                {                    return m_fileEnumerator.ActivelyVisitedPaths;                }            }             #endregion             #region [ Methods ]             public void EnumerateFiles(FileEnumerationStrategy fileEnumerationStrategy)            {                if (m_disposed)                    return;                 if (IsEnumerating)                    throw new InvalidOperationException("Enumeration is already in progress.");                 CancellationToken cancellationToken = new CancellationToken();                m_fileEnumerator = new FileEnumerator(Path, m_fileProcessor, cancellationToken);                m_fileEnumerator.Enumerate(fileEnumerationStrategy);            }             public void CancelEnumeration()            {                m_fileEnumerator?.Cancel();            }             public void CheckFileWatcher()            {                if (m_disposed)                    return;                 if (!m_fileWatcher.EnableRaisingEvents)                {                    DisposeFileWatcher();                    CreateFileWatcher();                }            }             /// <summary>            /// Releases all the resources used by the <see cref="TrackedDirectory"/> object.            /// </summary>            public void Dispose()            {                if (m_disposed)                    return;                 try                {                    CancelEnumeration();                    DisposeFileWatcher();                }                finally                {                    m_disposed = true;                }            }             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        }         private class FileEnumerator        {            #region [ Members ]             // Fields            private readonly FileProcessor m_fileProcessor;            private readonly CancellationToken m_cancellationToken;            private string m_lastVisitedPath;            private bool m_active;             #endregion             #region [ Constructors ]             public FileEnumerator(string path, FileProcessor fileProcessor, CancellationToken cancellationToken)            {                Path = path;                m_fileProcessor = fileProcessor;                m_cancellationToken = cancellationToken;            }             #endregion             #region [ Properties ]             public string Path { get; }            public FileEnumerator[] SubdirectoryEnumerators { get; private set; } = new FileEnumerator[0];             public bool Active            {                get                {                    if (m_active)                        return true;                     FileEnumerator[] subdirectoryEnumerators = SubdirectoryEnumerators;                    return m_active || subdirectoryEnumerators.Any(enumerator => enumerator.Active);                }            }             public List<string> ActivelyVisitedPaths            {                get                {                    List<string> activelyVisitedPaths = new List<string>();                    BuildActivelyVisitedPaths(activelyVisitedPaths);                    return activelyVisitedPaths;                }            }             #endregion             #region [ Methods ]             public void Enumerate(FileEnumerationStrategy enumerationStrategy)            {                if (Active)                    throw new InvalidOperationException("Enumeration is already in progress.");                 if ((object)LogicalThread.CurrentThread == null)                    throw new InvalidOperationException("Enumeration must be executed on a logical thread.");                 m_active = true;                 if (enumerationStrategy != FileEnumerationStrategy.ParallelSubdirectories)                {                    SubdirectoryEnumerators = new FileEnumerator[0];                     // If subdirectories do not need to be processed in parallel,                    // we can simply process a recursive call to enumerate all files                    IEnumerable<string> fileEnumerable = FilePath.EnumerateFiles(Path, exceptionHandler: m_fileProcessor.OnError);                    EnumerateNextFile(new EnumerableWrapper(fileEnumerable, m_cancellationToken));                }                else                {                    // To process subdirectories in parallel, we need to create a recursive structure                    // of enumerators to process each subdirectory on its own logical thread                    InitializeSubdirectoryEnumerators();                    EnumerateNextDirectory(0);                }            }             public void Cancel()            {                m_cancellationToken.Cancel();            }             // Initializes the FileEnumerators for subdirectories.            private void InitializeSubdirectoryEnumerators()            {                // Immediately enumerate all subdirectories so we can build the array of FileEnumerators.                // Using a fixed-size collection like an array greatly simplifies multithreaded access to the collection.                using (EnumerableWrapper wrapper = new EnumerableWrapper(EnumerateDirectories(), m_cancellationToken))                {                    List<string> subDirectories = new List<string>();                     while (wrapper.MoveNext())                        subDirectories.Add(wrapper.Current);                     SubdirectoryEnumerators = subDirectories                        .Select(path => new FileEnumerator(path, m_fileProcessor, m_cancellationToken))                        .ToArray();                }            }             // Enumerates the next subdirectory in the list of subdirectory enumerators.            private void EnumerateNextDirectory(int index)            {                try                {                    m_active = !m_cancellationToken.IsCancelled;                     if (!m_active)                        return;                     FileEnumerator[] subdirectoryEnumerators = SubdirectoryEnumerators;                     if (index >= subdirectoryEnumerators.Length)                    {                        EnumerateNextFile(new EnumerableWrapper(EnumerateFiles(), m_cancellationToken));                        return;                    }                     // Create a new thread for the subdirectory since this code is only invoked if the file                    // processor is using the enumeration strategy for processing subdirectories in parallel                    LogicalThread subdirectoryThread = m_fileProcessor.m_threadScheduler.CreateThread();                    subdirectoryThread.Push(() => subdirectoryEnumerators[index].Enumerate(FileEnumerationStrategy.ParallelSubdirectories));                     // Invoke the asynchronous call to enumerate the next directory                    LogicalThread.CurrentThread.Push(() => EnumerateNextDirectory(index + 1));                     m_lastVisitedPath = subdirectoryEnumerators[index].Path;                }                catch                {                    m_active = false;                    throw;                }            }             // Enumerates the next file in the given enumerable.            private void EnumerateNextFile(EnumerableWrapper wrapper)            {                try                {                    m_active = AdvanceToNextMatch(wrapper);                     if (!m_active)                        return;                     ProcessCurrentFileAndEnumerateNextFile(wrapper);                    m_lastVisitedPath = wrapper.Current;                }                catch                {                    m_active = false;                    wrapper.Dispose();                    throw;                }                finally                {                    if (!wrapper.LastMove)                    {                        m_active = false;                        wrapper.Dispose();                    }                }            }             // Advances enumeration until the next file that matches the filter.            private bool AdvanceToNextMatch(EnumerableWrapper wrapper)            {                List<string> skippedFiles = new List<string>();                 try                {                    while (true)                    {                        if (!wrapper.MoveNext())                            return false;                         if (m_fileProcessor.MatchesFilter(wrapper.Current))                            return true;                         skippedFiles.Add(wrapper.Current);                        m_lastVisitedPath = wrapper.Current;                    }                }                finally                {                    m_fileProcessor.m_processingThread.Push(1, () =>                    {                        foreach (string filePath in skippedFiles)                            m_fileProcessor.TouchAndSkip(filePath);                    });                }            }             // Processes the current file in the given enumerable.

Lines 1–500 of 1600.

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