| File: FileAccessRetrier.cs | Web Access |
| Project: src\sdk\src\Cli\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj (Microsoft.DotNet.Cli.Utils) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace Microsoft.DotNet.Cli.Utils; public static class FileAccessRetrier { public static async Task<T> RetryOnFileAccessFailure<T>( Func<T> func, string errorMessage, int maxRetries = 3000, TimeSpan sleepDuration = default) { var attemptsLeft = maxRetries; if (sleepDuration == default) { sleepDuration = TimeSpan.FromMilliseconds(10); } while (true) { if (attemptsLeft < 1) { throw new InvalidOperationException(errorMessage); } attemptsLeft--; try { return func(); } catch (UnauthorizedAccessException) { // This can occur when the file is being deleted // Or when an admin user has locked the file await Task.Delay(sleepDuration); continue; } catch (IOException) { await Task.Delay(sleepDuration); continue; } } } /// <summary> /// Run Directory.Move and File.Move in Windows has a chance to get IOException with /// HResult 0x80070005 due to Indexer. But this error is transient. /// </summary> internal static void RetryOnMoveAccessFailure(Action action) { const int ERROR_HRESULT_ACCESS_DENIED = unchecked((int)0x80070005); int nextWaitTime = 10; int remainRetry = 10; while (true) { try { action(); break; } catch (IOException e) when (e.HResult == ERROR_HRESULT_ACCESS_DENIED) { Thread.Sleep(nextWaitTime); nextWaitTime *= 2; remainRetry--; if (remainRetry == 0) { throw; } } } } internal static void RetryOnIOException(Action action) { int nextWaitTime = 10; int remainRetry = 10; while (true) { try { action(); break; } catch (IOException) { Task.Run(() => Task.Delay(nextWaitTime)).Wait(); nextWaitTime *= 2; remainRetry--; if (remainRetry == 0) { throw; } } } } }