/// <nodoc /> internal CacheInitializationTask( LoggingContext loggingContext, DateTime initializationStart, Task <Possible <CacheInitializer> > initializationTask, CancellationToken cancellationToken) { m_loggingContext = loggingContext; m_initializationStart = initializationStart; // initializationTask might be done already; safe to call ContinueWith now since we initialized everything else. m_initializationTask = initializationTask.ContinueWith( t => { if (t.IsCanceled) { return(new Failure <string>("Cache initialization has been cancelled")); } // Abstractly, there are two orders possible: // {m_initializationStart -> [firstAwaited} -> completionTime] (some sync wait time) // {m_initializationStart -> completionTime} -> firstAwaited (no sync wait time) // [ ] part is sync wait time - in the second case it is zero (timeWaitedMs below) // { } part was overlapped with other processing (i.e., initialization before first wait). // Since we arrive here at task completion time, we only care about the first case; // if an await hasn't happened by completionTime, we pretend that firstAwaited == completionTime. DateTime completionTime = DateTime.UtcNow; long firstWaitTimeTicksOrNegativeOne = Volatile.Read(ref m_firstAwaitTimeTicks); DateTime firstAwaitTime = firstWaitTimeTicksOrNegativeOne == -1 ? completionTime : new DateTime(firstWaitTimeTicksOrNegativeOne, DateTimeKind.Utc); if (firstAwaitTime > completionTime) { firstAwaitTime = completionTime; } // If an await hasn't happened yet, timeWaitedMs is zero (completionTime == firstAwaitTime; see above) int timeWaitedMs = (int)Math.Round(Math.Max(0, (completionTime - firstAwaitTime).TotalMilliseconds)); Contract.Assert(timeWaitedMs >= 0); InitializationTime = completionTime - m_initializationStart; if (InitializationTime < TimeSpan.Zero) { InitializationTime = TimeSpan.Zero; } int overlappedInitializationMs = (int)Math.Round(Math.Max(0, InitializationTime.TotalMilliseconds - timeWaitedMs)); Tracing.Logger.Log.SynchronouslyWaitedForCache(loggingContext, timeWaitedMs, overlappedInitializationMs); LoggingHelpers.LogCategorizedStatistic(m_loggingContext, "CacheInitialization", "TimeWaitedMs", timeWaitedMs); LoggingHelpers.LogCategorizedStatistic(m_loggingContext, "CacheInitialization", "OverlappedInitializationMs", overlappedInitializationMs); return(t.Result); }); // Timer will start if someone actually waiting on the task (called GetAwaiter()). m_cacheInitWatchdog = new Timer(o => CheckIfCacheIsStillInitializing(cancellationToken)); }
/// <summary> /// Cleans output files and directories. /// </summary> public static bool DeleteOutputs( LoggingContext loggingContext, Func <DirectoryArtifact, bool> isOutputDir, IList <FileOrDirectoryArtifact> filesOrDirectoriesToDelete, PathTable pathTable, ITempCleaner tempDirectoryCleaner = null) { int fileFailCount = 0; int fileSuccessCount = 0; int directoryFailCount = 0; int directorySuccessCount = 0; using (PerformanceMeasurement.Start( loggingContext, Category, Tracing.Logger.Log.CleaningStarted, localLoggingContext => { Tracing.Logger.Log.CleaningFinished(loggingContext, fileSuccessCount, fileFailCount); LoggingHelpers.LogCategorizedStatistic(loggingContext, Category, "FilesDeleted", fileSuccessCount); LoggingHelpers.LogCategorizedStatistic(loggingContext, Category, "FilesFailed", fileFailCount); LoggingHelpers.LogCategorizedStatistic(loggingContext, Category, "DirectoriesDeleted", directorySuccessCount); LoggingHelpers.LogCategorizedStatistic(loggingContext, Category, "DirectoriesFailed", directoryFailCount); })) { // Note: filesOrDirectoriesToDelete better be an IList<...> in order to get good Parallel.ForEach performance Parallel.ForEach( filesOrDirectoriesToDelete, fileOrDirectory => { string path = fileOrDirectory.Path.ToString(pathTable); Tracing.Logger.Log.CleaningOutputFile(loggingContext, path); try { if (fileOrDirectory.IsFile) { Contract.Assume(fileOrDirectory.FileArtifact.IsOutputFile, "Encountered non-output file"); if (FileUtilities.FileExistsNoFollow(path)) { FileUtilities.DeleteFile(path, waitUntilDeletionFinished: true, tempDirectoryCleaner: tempDirectoryCleaner); Interlocked.Increment(ref fileSuccessCount); } } else { if (FileUtilities.DirectoryExistsNoFollow(path)) { // TODO:1011977 this is a hacky fix for a bug where we delete SourceSealDirectories in /cleanonly mode // The bug stems from the fact that FilterOutputs() returns SourceSealDirectories, which aren't inputs. // Once properly addressed, this check should remain in here as a safety precaution and turn into // a Contract.Assume() like the check above if (isOutputDir(fileOrDirectory.DirectoryArtifact)) { FileUtilities.DeleteDirectoryContents(path, deleteRootDirectory: false, tempDirectoryCleaner: tempDirectoryCleaner); Interlocked.Increment(ref directorySuccessCount); } } } } catch (BuildXLException ex) { if (fileOrDirectory.IsFile) { Interlocked.Increment(ref fileFailCount); Tracing.Logger.Log.CleaningFileFailed(loggingContext, path, ex.LogEventMessage); } else { Interlocked.Increment(ref directoryFailCount); Tracing.Logger.Log.CleaningDirectoryFailed(loggingContext, path, ex.LogEventMessage); } } }); } return(fileFailCount + directoryFailCount == 0); }