diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/Helpers.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/Helpers.cs index 0e2407302a2ff7..1110cd6fb775e8 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/tests/Helpers.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/Helpers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; using System.Threading; +using System.ComponentModel; using Xunit; // Implementation is not robust with respect to modifying counter categories @@ -17,6 +18,36 @@ internal class Helpers public static bool CanWriteToPerfCounters { get => PlatformDetection.IsNotWindowsNanoServer; } public static bool CanReadNetPerfCounters { get => File.Exists(Environment.SystemDirectory + Path.DirectorySeparatorChar + "netfxperf.dll"); } + /// + /// Determines if an exception is retriable for performance counter operations. + /// Typically used for file access conflicts and transient system errors. + /// + internal static bool IsRetriableException(Exception ex) + { + // Handle Win32Exception with specific error codes for file access conflicts + if (ex is Win32Exception win32Ex) + { + // ERROR_SHARING_VIOLATION (32) - The process cannot access the file because it is being used by another process + // ERROR_ACCESS_DENIED (5) - Access is denied + // ERROR_LOCK_VIOLATION (33) - The process cannot access the file because another process has locked a portion of the file + return win32Ex.NativeErrorCode == 32 || win32Ex.NativeErrorCode == 5 || win32Ex.NativeErrorCode == 33; + } + + // Handle IOException for file access issues + if (ex is IOException) + { + return true; + } + + // Handle UnauthorizedAccessException + if (ex is UnauthorizedAccessException) + { + return true; + } + + return false; + } + public static void CreateCategory(string categoryName, PerformanceCounterCategoryType categoryType) { string counterName = categoryName.Replace("_Category", "_Counter"); @@ -30,7 +61,12 @@ public static void CreateCategory(string categoryName, string counterName, Perfo // If the category already exists, delete it, then create it. DeleteCategory(categoryName); - PerformanceCounterCategory.Create(categoryName, "description", categoryType, counterName, "counter description"); + + // Retry the Create operation to handle file access conflicts + RetryHelper.Execute(() => + { + PerformanceCounterCategory.Create(categoryName, "description", categoryType, counterName, "counter description"); + }, maxAttempts: 10, retryWhen: IsRetriableException); VerifyPerformanceCounterCategoryCreated(categoryName); } @@ -53,7 +89,11 @@ public static void DeleteCategory(string categoryName) Assert.EndsWith("_Category", categoryName); if (PerformanceCounterCategory.Exists(categoryName)) { - PerformanceCounterCategory.Delete(categoryName); + // Retry the Delete operation to handle file access conflicts + RetryHelper.Execute(() => + { + PerformanceCounterCategory.Delete(categoryName); + }, maxAttempts: 10, retryWhen: IsRetriableException); } int tries = 0; diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterCategoryTests.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterCategoryTests.cs index 2a164b91ef2aee..dc6d61ad29f49e 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterCategoryTests.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterCategoryTests.cs @@ -87,7 +87,7 @@ public static void PerformanceCounterCategory_CategoryType_MultiInstance() PerformanceCounterCategory pcc = new PerformanceCounterCategory(categoryName); Assert.Equal(PerformanceCounterCategoryType.MultiInstance, Helpers.RetryOnAllPlatformsWithClosingResources(() => pcc.CategoryType)); - PerformanceCounterCategory.Delete(categoryName); + Helpers.DeleteCategory(categoryName); } [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteAndReadNetPerfCounters))] @@ -100,7 +100,7 @@ public static void PerformanceCounterCategory_CategoryType_SingleInstance() PerformanceCounterCategory pcc = new PerformanceCounterCategory(categoryName); Assert.Equal(PerformanceCounterCategoryType.SingleInstance, Helpers.RetryOnAllPlatformsWithClosingResources(() => pcc.CategoryType)); - PerformanceCounterCategory.Delete(categoryName); + Helpers.DeleteCategory(categoryName); } #pragma warning disable 0618 // obsolete warning @@ -112,10 +112,13 @@ public static void PerformanceCounterCategory_Create_Obsolete() Helpers.DeleteCategory(categoryName); - PerformanceCounterCategory.Create(categoryName, "category help", counterName, "counter help"); + RetryHelper.Execute(() => + { + PerformanceCounterCategory.Create(categoryName, "category help", counterName, "counter help"); + }, maxAttempts: 10, retryWhen: Helpers.IsRetriableException); Assert.True(PerformanceCounterCategory.Exists(categoryName)); - PerformanceCounterCategory.Delete(categoryName); + Helpers.DeleteCategory(categoryName); } [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] @@ -129,10 +132,13 @@ public static void PerformanceCounterCategory_Create_Obsolete_CCD() Helpers.DeleteCategory(categoryName); - PerformanceCounterCategory.Create(categoryName, "category help", ccdc); + RetryHelper.Execute(() => + { + PerformanceCounterCategory.Create(categoryName, "category help", ccdc); + }, maxAttempts: 10, retryWhen: Helpers.IsRetriableException); Assert.True(PerformanceCounterCategory.Exists(categoryName)); - PerformanceCounterCategory.Delete(categoryName); + Helpers.DeleteCategory(categoryName); } #pragma warning restore 0618 @@ -209,7 +215,7 @@ public static void PerformanceCounterCategory_DeleteCategory() string categoryName = nameof(PerformanceCounterCategory_DeleteCategory) + "_Category"; Helpers.CreateCategory(categoryName, PerformanceCounterCategoryType.SingleInstance); - PerformanceCounterCategory.Delete(categoryName); + Helpers.DeleteCategory(categoryName); Assert.False(PerformanceCounterCategory.Exists(categoryName)); } @@ -232,7 +238,7 @@ public static void PerformanceCounterCategory_GetCounters() PerformanceCounter[] counters = pcc.GetCounters(); Assert.True(counters.Length > 0); - PerformanceCounterCategory.Delete(categoryName); + Helpers.DeleteCategory(categoryName); } [Fact]