internal static IEnumerable<IThreadPoolWorkItem> EnumerateQueuedWorkItems(ThreadPoolWorkQueue.WorkStealingQueue[] wsQueues, ThreadPoolWorkQueue.QueueSegment globalQueueTail) { if (wsQueues != null) { foreach (ThreadPoolWorkQueue.WorkStealingQueue iteratorVariable0 in wsQueues) { if ((iteratorVariable0 != null) && (iteratorVariable0.m_array != null)) { foreach (IThreadPoolWorkItem iteratorVariable3 in iteratorVariable0.m_array) { if (iteratorVariable3 != null) { yield return iteratorVariable3; } } } } } if (globalQueueTail != null) { for (ThreadPoolWorkQueue.QueueSegment iteratorVariable4 = globalQueueTail; iteratorVariable4 != null; iteratorVariable4 = iteratorVariable4.Next) { foreach (IThreadPoolWorkItem iteratorVariable7 in iteratorVariable4.nodes) { if (iteratorVariable7 != null) { yield return iteratorVariable7; } } } } }
/// <summary> /// Finds all the rules of the given type under a given directory /// </summary> /// <param name="Directory">Directory to search</param> /// <param name="Type">Type of rules to return</param> /// <returns>List of rules files of the given type</returns> private static IReadOnlyList <FileReference> FindAllRulesFiles(DirectoryReference Directory, RulesFileType Type) { // Check to see if we've already cached source files for this folder RulesFileCache Cache; if (!RootFolderToRulesFileCache.TryGetValue(Directory, out Cache)) { Cache = new RulesFileCache(); using (ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue()) { DirectoryItem BaseDirectory = DirectoryItem.GetItemByDirectoryReference(Directory); Queue.Enqueue(() => FindAllRulesFilesRecursively(BaseDirectory, Cache, Queue)); } Cache.ModuleRules.Sort((A, B) => A.FullName.CompareTo(B.FullName)); Cache.TargetRules.Sort((A, B) => A.FullName.CompareTo(B.FullName)); Cache.AutomationModules.Sort((A, B) => A.FullName.CompareTo(B.FullName)); RootFolderToRulesFileCache[Directory] = Cache; } // Get the list of files of the type we're looking for if (Type == RulesCompiler.RulesFileType.Module) { return(Cache.ModuleRules); } else if (Type == RulesCompiler.RulesFileType.Target) { return(Cache.TargetRules); } else if (Type == RulesCompiler.RulesFileType.AutomationModule) { return(Cache.AutomationModules); } else { throw new BuildException("Unhandled rules type: {0}", Type); } }
/// <summary> /// Find paths to all the plugins under a given parent directory (recursively) /// </summary> /// <param name="ParentDirectory">Parent directory to look in. Plugins will be found in any *subfolders* of this directory.</param> public static IEnumerable <FileReference> EnumeratePlugins(DirectoryReference ParentDirectory) { List <FileReference> FileNames; if (!PluginFileCache.TryGetValue(ParentDirectory, out FileNames)) { FileNames = new List <FileReference>(); DirectoryItem ParentDirectoryItem = DirectoryItem.GetItemByDirectoryReference(ParentDirectory); if (ParentDirectoryItem.Exists) { using (ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue()) { EnumeratePluginsInternal(ParentDirectoryItem, FileNames, Queue); } } // Sort the filenames to ensure that the plugin order is deterministic; otherwise response files will change with each build. FileNames = FileNames.OrderBy(x => x.FullName, StringComparer.OrdinalIgnoreCase).ToList(); PluginFileCache.Add(ParentDirectory, FileNames); } return(FileNames); }
public void DoSetup() { mQueue = new ThreadPoolWorkQueue(); mThreadID = Thread.CurrentThread.ManagedThreadId; }
private static void WorkerThreadStart() { Thread.CurrentThread.SetThreadPoolWorkerThreadName(); PortableThreadPool threadPoolInstance = ThreadPoolInstance; if (PortableThreadPoolEventSource.Log.IsEnabled()) { PortableThreadPoolEventSource.Log.ThreadPoolWorkerThreadStart( (uint)threadPoolInstance._separated.counts.VolatileRead().NumExistingThreads); } LowLevelLock hillClimbingThreadAdjustmentLock = threadPoolInstance._hillClimbingThreadAdjustmentLock; LowLevelLifoSemaphore semaphore = s_semaphore; while (true) { bool spinWait = true; while (semaphore.Wait(ThreadPoolThreadTimeoutMs, spinWait)) { bool alreadyRemovedWorkingWorker = false; while (TakeActiveRequest(threadPoolInstance)) { Volatile.Write(ref threadPoolInstance._separated.lastDequeueTime, Environment.TickCount); if (!ThreadPoolWorkQueue.Dispatch()) { // ShouldStopProcessingWorkNow() caused the thread to stop processing work, and it would have // already removed this working worker in the counts. This typically happens when hill climbing // decreases the worker thread count goal. alreadyRemovedWorkingWorker = true; break; } } // Don't spin-wait on the semaphore next time if the thread was actively stopped from processing work, // as it's unlikely that the worker thread count goal would be increased again so soon afterwards that // the semaphore would be released within the spin-wait window spinWait = !alreadyRemovedWorkingWorker; if (!alreadyRemovedWorkingWorker) { // If we woke up but couldn't find a request, or ran out of work items to process, we need to update // the number of working workers to reflect that we are done working for now RemoveWorkingWorker(threadPoolInstance); } } hillClimbingThreadAdjustmentLock.Acquire(); try { // At this point, the thread's wait timed out. We are shutting down this thread. // We are going to decrement the number of exisiting threads to no longer include this one // and then change the max number of threads in the thread pool to reflect that we don't need as many // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. ThreadCounts counts = threadPoolInstance._separated.counts.VolatileRead(); while (true) { // Since this thread is currently registered as an existing thread, if more work comes in meanwhile, // this thread would be expected to satisfy the new work. Ensure that NumExistingThreads is not // decreased below NumProcessingWork, as that would be indicative of such a case. short numExistingThreads = counts.NumExistingThreads; if (numExistingThreads <= counts.NumProcessingWork) { // In this case, enough work came in that this thread should not time out and should go back to work. break; } ThreadCounts newCounts = counts; newCounts.SubtractNumExistingThreads(1); short newNumExistingThreads = (short)(numExistingThreads - 1); short newNumThreadsGoal = Math.Max(threadPoolInstance._minThreads, Math.Min(newNumExistingThreads, newCounts.NumThreadsGoal)); newCounts.NumThreadsGoal = newNumThreadsGoal; ThreadCounts oldCounts = threadPoolInstance._separated.counts.InterlockedCompareExchange(newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange(newNumThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); if (PortableThreadPoolEventSource.Log.IsEnabled()) { PortableThreadPoolEventSource.Log.ThreadPoolWorkerThreadStop((uint)newNumExistingThreads); } return; } counts = oldCounts; } } finally { hillClimbingThreadAdjustmentLock.Release(); } } }
/// <summary> /// Finds all the source files under a directory that contain reflection markup /// </summary> /// <param name="Directory">The directory to search</param> /// <param name="MetadataCache">Cache of source file metadata</param> /// <param name="ExcludedFolderNames">Set of folder names to ignore when recursing the directory tree</param> /// <param name="FilesWithMarkup">Receives the set of files which contain reflection markup</param> /// <param name="Queue">Queue to add sub-tasks to</param> static void FindFilesWithMarkup(DirectoryItem Directory, SourceFileMetadataCache MetadataCache, ReadOnlyHashSet <string> ExcludedFolderNames, ConcurrentBag <FileItem> FilesWithMarkup, ThreadPoolWorkQueue Queue) { // Search through all the subfolders foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { if (!ExcludedFolderNames.Contains(SubDirectory.Name)) { Queue.Enqueue(() => FindFilesWithMarkup(SubDirectory, MetadataCache, ExcludedFolderNames, FilesWithMarkup, Queue)); } } // Check for all the headers in this folder foreach (FileItem File in Directory.EnumerateFiles()) { if (File.HasExtension(".h") && MetadataCache.ContainsReflectionMarkup(File)) { FilesWithMarkup.Add(File); } } }
/// <summary> /// Checks if the makefile is valid for the current set of source files. This is done separately to the Load() method to allow pre-build steps to modify source files. /// </summary> /// <param name="Makefile">The makefile that has been loaded</param> /// <param name="ProjectFile">Path to the project file</param> /// <param name="Platform">The platform being built</param> /// <param name="WorkingSet">The current working set of source files</param> /// <param name="ReasonNotLoaded">If the makefile is not valid, is set to a message describing why</param> /// <returns>True if the makefile is valid, false otherwise</returns> public static bool IsValidForSourceFiles(TargetMakefile Makefile, FileReference ProjectFile, UnrealTargetPlatform Platform, ISourceFileWorkingSet WorkingSet, out string ReasonNotLoaded) { using (Timeline.ScopeEvent("TargetMakefile.IsValidForSourceFiles()")) { // Get the list of excluded folder names for this platform ReadOnlyHashSet <string> ExcludedFolderNames = UEBuildPlatform.GetBuildPlatform(Platform).GetExcludedFolderNames(); // Check if any source files have been added or removed foreach (KeyValuePair <DirectoryItem, FileItem[]> Pair in Makefile.DirectoryToSourceFiles) { DirectoryItem InputDirectory = Pair.Key; if (!InputDirectory.Exists || InputDirectory.LastWriteTimeUtc > Makefile.CreateTimeUtc) { FileItem[] SourceFiles = UEBuildModuleCPP.GetSourceFiles(InputDirectory); if (SourceFiles.Length < Pair.Value.Length) { ReasonNotLoaded = "source file removed"; return(false); } else if (SourceFiles.Length > Pair.Value.Length) { ReasonNotLoaded = "source file added"; return(false); } else if (SourceFiles.Intersect(Pair.Value).Count() != SourceFiles.Length) { ReasonNotLoaded = "source file modified"; return(false); } foreach (DirectoryItem Directory in InputDirectory.EnumerateDirectories()) { if (!Makefile.DirectoryToSourceFiles.ContainsKey(Directory) && ContainsSourceFiles(Directory, ExcludedFolderNames)) { ReasonNotLoaded = "directory added"; return(false); } } } } // Check if any external dependencies have changed. These comparisons are done against the makefile creation time. foreach (FileItem ExternalDependency in Makefile.ExternalDependencies) { if (!ExternalDependency.Exists) { Log.TraceLog("{0} has been deleted since makefile was built.", ExternalDependency.Location); ReasonNotLoaded = string.Format("{0} deleted", ExternalDependency.Location.GetFileName()); return(false); } if (ExternalDependency.LastWriteTimeUtc > Makefile.CreateTimeUtc) { Log.TraceLog("{0} has been modified since makefile was built.", ExternalDependency.Location); ReasonNotLoaded = string.Format("{0} modified", ExternalDependency.Location.GetFileName()); return(false); } } // Check if any internal dependencies has changed. These comparisons are done against the makefile modified time. foreach (FileItem InternalDependency in Makefile.InternalDependencies) { if (!InternalDependency.Exists) { Log.TraceLog("{0} has been deleted since makefile was written.", InternalDependency.Location); ReasonNotLoaded = string.Format("{0} deleted", InternalDependency.Location.GetFileName()); return(false); } if (InternalDependency.LastWriteTimeUtc > Makefile.ModifiedTimeUtc) { Log.TraceLog("{0} has been modified since makefile was written.", InternalDependency.Location); ReasonNotLoaded = string.Format("{0} modified", InternalDependency.Location.GetFileName()); return(false); } } // Check that no new plugins have been added foreach (FileReference PluginFile in Plugins.EnumeratePlugins(ProjectFile)) { FileItem PluginFileItem = FileItem.GetItemByFileReference(PluginFile); if (!Makefile.PluginFiles.Contains(PluginFileItem)) { Log.TraceLog("{0} has been added", PluginFile.GetFileName()); ReasonNotLoaded = string.Format("{0} has been added", PluginFile.GetFileName()); return(false); } } // Load the metadata cache SourceFileMetadataCache MetadataCache = SourceFileMetadataCache.CreateHierarchy(ProjectFile); // Find the set of files that contain reflection markup ConcurrentBag <FileItem> NewFilesWithMarkupBag = new ConcurrentBag <FileItem>(); using (ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue()) { foreach (DirectoryItem SourceDirectory in Makefile.SourceDirectories) { Queue.Enqueue(() => FindFilesWithMarkup(SourceDirectory, MetadataCache, ExcludedFolderNames, NewFilesWithMarkupBag, Queue)); } } // Check whether the list has changed List <FileItem> PrevFilesWithMarkup = Makefile.UObjectModuleHeaders.Where(x => !x.bUsePrecompiled).SelectMany(x => x.HeaderFiles).ToList(); List <FileItem> NextFilesWithMarkup = NewFilesWithMarkupBag.ToList(); if (NextFilesWithMarkup.Count != PrevFilesWithMarkup.Count || NextFilesWithMarkup.Intersect(PrevFilesWithMarkup).Count() != PrevFilesWithMarkup.Count) { ReasonNotLoaded = "UHT files changed"; return(false); } // If adaptive unity build is enabled, do a check to see if there are any source files that became part of the // working set since the Makefile was created (or, source files were removed from the working set.) If anything // changed, then we'll force a new Makefile to be created so that we have fresh unity build blobs. We always // want to make sure that source files in the working set are excluded from those unity blobs (for fastest possible // iteration times.) // Check if any source files in the working set no longer belong in it foreach (FileItem SourceFile in Makefile.WorkingSet) { if (!WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc) { Log.TraceLog("{0} was part of source working set and now is not; invalidating makefile", SourceFile.AbsolutePath); ReasonNotLoaded = string.Format("working set of source files changed"); return(false); } } // Check if any source files that are eligible for being in the working set have been modified foreach (FileItem SourceFile in Makefile.CandidatesForWorkingSet) { if (WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc) { Log.TraceLog("{0} was part of source working set and now is not", SourceFile.AbsolutePath); ReasonNotLoaded = string.Format("working set of source files changed"); return(false); } } } ReasonNotLoaded = null; return(true); }
/// <summary> /// Find paths to all the plugins under a given parent directory (recursively) /// </summary> /// <param name="ParentDirectory">Parent directory to look in. Plugins will be found in any *subfolders* of this directory.</param> /// <param name="FileNames">List of filenames. Will have all the discovered .uplugin files appended to it.</param> /// <param name="Queue">Queue for tasks to be executed</param> static void EnumeratePluginsInternal(DirectoryItem ParentDirectory, List <FileReference> FileNames, ThreadPoolWorkQueue Queue) { foreach (DirectoryItem ChildDirectory in ParentDirectory.EnumerateDirectories()) { bool bSearchSubDirectories = true; foreach (FileItem PluginFile in ChildDirectory.EnumerateFiles()) { if (PluginFile.HasExtension(".uplugin")) { lock (FileNames) { FileNames.Add(PluginFile.Location); } bSearchSubDirectories = false; } } if (bSearchSubDirectories) { Queue.Enqueue(() => EnumeratePluginsInternal(ChildDirectory, FileNames, Queue)); } } }
private void Refresh(DirectoryInfo Info, ConcurrentBag <FileInfo> FilesToDelete, ConcurrentBag <DirectoryInfo> DirectoriesToDelete, ThreadPoolWorkQueue Queue) { // Recurse through subdirectories Dictionary <string, WorkspaceDirectoryInfo> NewNameToSubDirectory = new Dictionary <string, WorkspaceDirectoryInfo>(NameToSubDirectory.Count, NameToSubDirectory.Comparer); foreach (DirectoryInfo SubDirectoryInfo in Info.EnumerateDirectories()) { WorkspaceDirectoryInfo SubDirectory; if (NameToSubDirectory.TryGetValue(SubDirectoryInfo.Name, out SubDirectory)) { NewNameToSubDirectory.Add(SubDirectory.Name, SubDirectory); Queue.Enqueue(() => SubDirectory.Refresh(SubDirectoryInfo, FilesToDelete, DirectoriesToDelete, Queue)); } else { DirectoriesToDelete.Add(SubDirectoryInfo); } } NameToSubDirectory = NewNameToSubDirectory; // Figure out which files have changed. Dictionary <string, WorkspaceFileInfo> NewNameToFile = new Dictionary <string, WorkspaceFileInfo>(NameToFile.Count, NameToFile.Comparer); foreach (FileInfo File in Info.EnumerateFiles()) { WorkspaceFileInfo StagedFile; if (NameToFile.TryGetValue(File.Name, out StagedFile) && StagedFile.MatchesAttributes(File)) { NewNameToFile.Add(StagedFile.Name, StagedFile); } else { FilesToDelete.Add(File); } } NameToFile = NewNameToFile; }
public DedicatedThreadPool(DedicatedThreadPoolSettings settings) { _workQueue = new ThreadPoolWorkQueue(); Settings = settings; _workers = Enumerable.Range(1, settings.NumThreads).Select(workerId => new PoolWorker(this, workerId)).ToArray(); // Note: // The DedicatedThreadPoolSupervisor was removed because aborting thread could lead to unexpected behavior // If a new implementation is done, it should spawn a new thread when a worker is not making progress and // try to keep {settings.NumThreads} active threads. }
private static void WorkerThreadStart() { PortableThreadPoolEventSource log = PortableThreadPoolEventSource.Log; if (log.IsEnabled()) { log.WorkerThreadStart(ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts).numExistingThreads); } while (true) { while (WaitForRequest()) { if (TakeActiveRequest()) { Volatile.Write(ref ThreadPoolInstance._separated.lastDequeueTime, Environment.TickCount); if (ThreadPoolWorkQueue.Dispatch()) { // If the queue runs out of work for us, we need to update the number of working workers to reflect that we are done working for now RemoveWorkingWorker(); } } else { // If we woke up but couldn't find a request, we need to update the number of working workers to reflect that we are done working for now RemoveWorkingWorker(); } } ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Acquire(); try { // At this point, the thread's wait timed out. We are shutting down this thread. // We are going to decrement the number of exisiting threads to no longer include this one // and then change the max number of threads in the thread pool to reflect that we don't need as many // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); while (true) { if (counts.numExistingThreads == counts.numProcessingWork) { // In this case, enough work came in that this thread should not time out and should go back to work. break; } ThreadCounts newCounts = counts; newCounts.numExistingThreads--; newCounts.numThreadsGoal = Math.Max(ThreadPoolInstance._minThreads, Math.Min(newCounts.numExistingThreads, newCounts.numThreadsGoal)); ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange(newCounts.numThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); if (log.IsEnabled()) { log.WorkerThreadStop(newCounts.numExistingThreads); } return; } } } finally { ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Release(); } } }
void MergeDirectory(WorkspaceDirectoryInfo WorkspaceDir, WorkspaceDirectoryInfo NewWorkspaceDir, StreamDirectoryInfo StreamDir, ThreadPoolWorkQueue Queue) { // Make sure the directory exists Directory.CreateDirectory(WorkspaceDir.GetFullName()); // Update all the subdirectories foreach (StreamDirectoryInfo StreamSubDir in StreamDir.NameToSubDirectory.Values) { WorkspaceDirectoryInfo WorkspaceSubDir; if (WorkspaceDir.NameToSubDirectory.TryGetValue(StreamSubDir.Name, out WorkspaceSubDir)) { MergeSubDirectory(WorkspaceSubDir, StreamSubDir, NewWorkspaceDir, Queue); } else { AddSubDirectory(NewWorkspaceDir, StreamSubDir, Queue); } } // Move files into this folder foreach (StreamFileInfo StreamFile in StreamDir.NameToFile.Values) { WorkspaceFileInfo WorkspaceFile; if (WorkspaceDir.NameToFile.TryGetValue(StreamFile.Name, out WorkspaceFile)) { NewWorkspaceDir.NameToFile.Add(WorkspaceFile.Name, WorkspaceFile); } else { AddFile(NewWorkspaceDir, StreamFile); } } }
void AddSubDirectory(WorkspaceDirectoryInfo NewWorkspaceDir, StreamDirectoryInfo StreamSubDir, ThreadPoolWorkQueue Queue) { WorkspaceDirectoryInfo NewWorkspaceSubDir = new WorkspaceDirectoryInfo(NewWorkspaceDir, StreamSubDir.Name); NewWorkspaceDir.NameToSubDirectory.Add(StreamSubDir.Name, NewWorkspaceSubDir); Queue.Enqueue(() => AddDirectory(NewWorkspaceSubDir, StreamSubDir, Queue)); }
void AddDirectory(WorkspaceDirectoryInfo NewWorkspaceDir, StreamDirectoryInfo StreamDir, ThreadPoolWorkQueue Queue) { // Make sure the directory exists Directory.CreateDirectory(NewWorkspaceDir.GetFullName()); // Add all the sub directories and files foreach (StreamDirectoryInfo StreamSubDir in StreamDir.NameToSubDirectory.Values) { AddSubDirectory(NewWorkspaceDir, StreamSubDir, Queue); } foreach (StreamFileInfo StreamFile in StreamDir.NameToFile.Values) { AddFile(NewWorkspaceDir, StreamFile); } }
private void FindDifferences(DirectoryInfo Directory, string Path, ConcurrentBag <string> Paths, ThreadPoolWorkQueue Queue) { // Recurse through subdirectories HashSet <string> RemainingSubDirectoryNames = new HashSet <string>(NameToSubDirectory.Keys); foreach (DirectoryInfo SubDirectory in Directory.EnumerateDirectories()) { WorkspaceDirectoryInfo StagedSubDirectory; if (NameToSubDirectory.TryGetValue(SubDirectory.Name, out StagedSubDirectory)) { RemainingSubDirectoryNames.Remove(SubDirectory.Name); Queue.Enqueue(() => StagedSubDirectory.FindDifferences(SubDirectory, String.Format("{0}{1}/", Path, SubDirectory.Name), Paths, Queue)); continue; } Paths.Add(String.Format("+{0}{1}/...", Path, SubDirectory.Name)); } foreach (string RemainingSubDirectoryName in RemainingSubDirectoryNames) { Paths.Add(String.Format("-{0}{1}/...", Path, RemainingSubDirectoryName)); } // Search through files HashSet <string> RemainingFileNames = new HashSet <string>(NameToFile.Keys); foreach (FileInfo File in Directory.EnumerateFiles()) { WorkspaceFileInfo StagedFile; if (!NameToFile.TryGetValue(File.Name, out StagedFile)) { Paths.Add(String.Format("+{0}{1}", Path, File.Name)); } else if (!StagedFile.MatchesAttributes(File)) { Paths.Add(String.Format("!{0}{1}", Path, File.Name)); RemainingFileNames.Remove(File.Name); } else { RemainingFileNames.Remove(File.Name); } } foreach (string RemainingFileName in RemainingFileNames) { Paths.Add(String.Format("-{0}{1}", Path, RemainingFileName)); } }
/// <summary> /// Search through a directory tree for any rules files /// </summary> /// <param name="Directory">The root directory to search from</param> /// <param name="Cache">Receives all the discovered rules files</param> /// <param name="Queue">Queue for adding additional tasks to</param> private static void FindAllRulesFilesRecursively(DirectoryItem Directory, RulesFileCache Cache, ThreadPoolWorkQueue Queue) { // Scan all the files in this directory bool bSearchSubFolders = true; foreach (FileItem File in Directory.EnumerateFiles()) { if (File.HasExtension(".build.cs")) { lock (Cache.ModuleRules) { Cache.ModuleRules.Add(File.Location); } bSearchSubFolders = false; } else if (File.HasExtension(".target.cs")) { lock (Cache.TargetRules) { Cache.TargetRules.Add(File.Location); } } else if (File.HasExtension(".automation.csproj")) { lock (Cache.AutomationModules) { Cache.AutomationModules.Add(File.Location); } bSearchSubFolders = false; } } // If we didn't find anything to stop the search, search all the subdirectories too if (bSearchSubFolders) { foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { Queue.Enqueue(() => FindAllRulesFilesRecursively(SubDirectory, Cache, Queue)); } } }
void Merge(WorkspaceDirectoryInfo WorkspaceDir, WorkspaceDirectoryInfo NewWorkspaceDir, StreamDirectoryInfo StreamDir, ThreadPoolWorkQueue Queue) { // Update all the subdirectories foreach (WorkspaceDirectoryInfo WorkspaceSubDir in WorkspaceDir.NameToSubDirectory.Values) { StreamDirectoryInfo StreamSubDir; if (StreamDir.NameToSubDirectory.TryGetValue(WorkspaceSubDir.Name, out StreamSubDir)) { MergeSubDirectory(NewWorkspaceDir, WorkspaceSubDir, StreamSubDir, Queue); } else { RemoveDirectory(WorkspaceSubDir, Queue); } } // Update the staged files foreach (WorkspaceFileInfo WorkspaceFile in WorkspaceDir.NameToFile.Values) { StreamFileInfo StreamFile; if (StreamDir != null && StreamDir.NameToFile.TryGetValue(WorkspaceFile.Name, out StreamFile) && StreamFile.ContentId.Equals(WorkspaceFile.ContentId)) { NewWorkspaceDir.NameToFile.Add(WorkspaceFile.Name, WorkspaceFile); } else { RemoveFile(WorkspaceFile); } } }