Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"); }

/// <summary>
/// Determines if an exception is retriable for performance counter operations.
/// Typically used for file access conflicts and transient system errors.
/// </summary>
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");
Expand All @@ -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);
}
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand All @@ -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
Expand All @@ -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))]
Expand All @@ -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

Expand Down Expand Up @@ -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));
}
Expand All @@ -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]
Expand Down
Loading