-
-
Notifications
You must be signed in to change notification settings - Fork 968
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework ReturnValueValidator and ExecutionValidator #2114
base: master
Are you sure you want to change the base?
Conversation
8fd94c9
to
ce1b1cd
Compare
Deadlock in tests. I cannot reproduce it on local machine (Windows). I ran them few times, the deadlock happens every time. Github Actions: deadlock on Windows/Linux. MacOS is ok I need to execute a method via Reflection, await it (if the method is async: Task or ValueTask) and return result (if the Task is not void). MethodInfo benchmarkMethod = ...
var result = benchmarkMethod.Invoke(benchmarkClassInstance, arguments); // (arguments.Any() ? arguments : null) not helped
if (TryAwaitTask(result, out var taskResult))
result = taskResult;
// returns true if the method is async
private static bool TryAwaitTask(object task, out object result)
{
result = null;
if (task is null)
{
return false;
}
// ValueTask<T>
var returnType = task.GetType();
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(ValueTask<>))
{
var asTaskMethod = task.GetType().GetMethod("AsTask");
task = asTaskMethod.Invoke(task, null);
}
if (task is ValueTask valueTask)
task = valueTask.AsTask();
// Task or Task<T>
if (task is Task t)
{
if (TryGetTaskResult(t, out var taskResult))
result = taskResult;
return true;
}
return false;
}
// https://stackoverflow.com/a/52500763
private static bool TryGetTaskResult(Task task, out object result)
{
result = null;
var voidTaskType = typeof(Task<>).MakeGenericType(Type.GetType("System.Threading.Tasks.VoidTaskResult"));
if (voidTaskType.IsInstanceOfType(task))
{
task.GetAwaiter().GetResult();
return false;
}
var property = task.GetType().GetProperty("Result", BindingFlags.Public | BindingFlags.Instance);
if (property is null)
{
return false;
}
result = property.GetValue(task);
return true;
} The deadlock occurs on executing these async methods in parallel public class AsyncGenericValueTaskGlobalSetup
{
[GlobalSetup]
public async ValueTask<int> GlobalSetup()
{
await Task.Delay(1);
return 42;
}
}
public class AsyncSetupIsSupportedClass
{
[Benchmark]
public async Task<int> Foo()
{
await Task.Delay(1);
return 1;
}
} No one else uses the task.
@stephentoub, only You know what's going on here. Maybe it's a BLC bug? |
I don't see anything wrong with that code. But I just updated my private static bool TryAwaitTask(object task, out object result)
{
result = null;
if (task is null)
{
return false;
}
var getResultMethod = AwaitHelper.GetGetResultMethod(task.GetType());
if (getResultMethod is null)
{
return false;
}
result = getResultMethod.Invoke(null, new[] { task });
return true;
// Or
// return getResultMethod.ReturnType != typeof(void);
} |
Hey, Tim! I tried your solution, deadlock still exists. Changing The deadlock occurs on execution I deleted all other tests except these and all sync methods in these tests - the situation is the same. I think the problem is due to the version of .NET Framework (net461) or with XUnit. |
What I did... Summary:
Still deadlock on Windows/Linux What works:
I hope it's a xUnit issue. I will send it to them. |
I'm curious why the deadlock only happens with your changes, and not in the existing tests that do the same blocking wait (just without reading the result). By deduction, it must be some other change causing it, no? |
Currently, these validators don't await async methods. They simple call MethodInfo benchmarkMethod = ...;
benchmarkMethod.Invoke(null); Yep, it's a xUnit bug, msTest and nUnit are completed successfully. I'll send a bug report in a few hours. |
The bug is unlikely to be fixed anytime soon. |
You added validation here to make sure IterationSetup/Cleanup are not async. I actually added async support for them in #2111, and this seems likely to get merged first, so I'll have to remember to update the validator there. |
I wonder if that xUnit bug is the cause of the |
Okay, I'll keep an eye on it too. I hope your PRs will be accepted before the Unity moves to .NET Core. Just two years left to wait 😭 |
I didn't even think why some tests are flakiness. Hmm, it is a good idea to check it, it not hard (I don't want to do it 😄):
|
Yeah it would be nice to unblock async engine support, but what does it have to do with Core in Unity? |
I've observed similar CI issues in the past in other projects. What is unique about most CI machines is that they have only one or two cores, so getting into a deadlock is way easier. You could try to reproduce it locally by running the tests affinized to 1 or 2 cores. |
src/BenchmarkDotNet/Toolchains/InProcess.NoEmit/InProcessNoEmitRunner.cs
Show resolved
Hide resolved
Since you are writing a library for Unity, I thought it was related to Unity. Anyway, we need to support taskable object to support To do this, we need to slightly modify your |
d30865b
to
8fddc56
Compare
@YegorStepanov any plans for this? |
@snowfrogdev Yegor is MIA, feel free to take over here. |
ParamsSource
supportArguments
/ArgumentsSource
supportParams
/ParamsSource
beforeGlobalSetup
(fixesReturnValueValidator
does not runGlobalSetup
methods for eachParams
#2083 fixes ParamsSource attribute doesn't work as expected #848)GlobalSetup
/GlobalCleanup
were awaited)IterationSetup
/IterationCleanup
Each benchmark is run on the new instance.
ReturnValueValidator:
Task
value instead task objects itself