Skip to content

Commit f9c6342

Browse files
committed
Changed AwaitHelper to static.
1 parent 4b3d20a commit f9c6342

File tree

9 files changed

+46
-110
lines changed

9 files changed

+46
-110
lines changed

src/BenchmarkDotNet/Code/DeclarationsProvider.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private string GetMethodName(MethodInfo method)
6363
(method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>) ||
6464
method.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>))))
6565
{
66-
return $"() => awaitHelper.GetResult({method.Name}())";
66+
return $"() => BenchmarkDotNet.Helpers.AwaitHelper.GetResult({method.Name}())";
6767
}
6868

6969
return method.Name;
@@ -150,9 +150,9 @@ internal class TaskDeclarationsProvider : VoidDeclarationsProvider
150150
public TaskDeclarationsProvider(Descriptor descriptor) : base(descriptor) { }
151151

152152
public override string WorkloadMethodDelegate(string passArguments)
153-
=> $"({passArguments}) => {{ awaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
153+
=> $"({passArguments}) => {{ BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
154154

155-
public override string GetWorkloadMethodCall(string passArguments) => $"awaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
155+
public override string GetWorkloadMethodCall(string passArguments) => $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
156156

157157
protected override Type WorkloadMethodReturnType => typeof(void);
158158
}
@@ -167,8 +167,8 @@ public GenericTaskDeclarationsProvider(Descriptor descriptor) : base(descriptor)
167167
protected override Type WorkloadMethodReturnType => Descriptor.WorkloadMethod.ReturnType.GetTypeInfo().GetGenericArguments().Single();
168168

169169
public override string WorkloadMethodDelegate(string passArguments)
170-
=> $"({passArguments}) => {{ return awaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
170+
=> $"({passArguments}) => {{ return BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments})); }}";
171171

172-
public override string GetWorkloadMethodCall(string passArguments) => $"awaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
172+
public override string GetWorkloadMethodCall(string passArguments) => $"BenchmarkDotNet.Helpers.AwaitHelper.GetResult({Descriptor.WorkloadMethod.Name}({passArguments}))";
173173
}
174174
}

src/BenchmarkDotNet/Helpers/AwaitHelper.cs

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace BenchmarkDotNet.Helpers
88
{
9-
public class AwaitHelper
9+
public static class AwaitHelper
1010
{
1111
private class ValueTaskWaiter
1212
{
@@ -28,10 +28,8 @@ private void AwaiterCallback()
2828
}
2929

3030
// Hook up a callback instead of converting to Task to prevent extra allocations on each benchmark run.
31-
internal void GetResult(ValueTask task)
31+
internal void Wait(ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter awaiter)
3232
{
33-
// Don't continue on the captured context, as that may result in a deadlock if the user runs this in-process.
34-
var awaiter = task.ConfigureAwait(false).GetAwaiter();
3533
if (!awaiter.IsCompleted)
3634
{
3735
lock (this)
@@ -45,13 +43,10 @@ internal void GetResult(ValueTask task)
4543
}
4644
}
4745
}
48-
awaiter.GetResult();
4946
}
5047

51-
internal T GetResult<T>(ValueTask<T> task)
48+
internal void Wait<T>(ConfiguredValueTaskAwaitable<T>.ConfiguredValueTaskAwaiter awaiter)
5249
{
53-
// Don't continue on the captured context, as that may result in a deadlock if the user runs this in-process.
54-
var awaiter = task.ConfigureAwait(false).GetAwaiter();
5550
if (!awaiter.IsCompleted)
5651
{
5752
lock (this)
@@ -65,43 +60,49 @@ internal T GetResult<T>(ValueTask<T> task)
6560
}
6661
}
6762
}
68-
return awaiter.GetResult();
6963
}
7064
}
7165

7266
// We use thread static field so that multiple threads can use individual lock object and callback.
7367
[ThreadStatic]
7468
private static ValueTaskWaiter ts_valueTaskWaiter;
75-
76-
private ValueTaskWaiter CurrentValueTaskWaiter
77-
{
78-
get
79-
{
80-
if (ts_valueTaskWaiter == null)
81-
{
82-
ts_valueTaskWaiter = new ValueTaskWaiter();
83-
}
84-
return ts_valueTaskWaiter;
85-
}
86-
}
69+
private static ValueTaskWaiter CurrentValueTaskWaiter => ts_valueTaskWaiter ??= new ValueTaskWaiter();
8770

8871
// we use GetAwaiter().GetResult() because it's fastest way to obtain the result in blocking way,
8972
// and will eventually throw actual exception, not aggregated one
90-
public void GetResult(Task task) => task.GetAwaiter().GetResult();
73+
public static void GetResult(Task task) => task.GetAwaiter().GetResult();
9174

92-
public T GetResult<T>(Task<T> task) => task.GetAwaiter().GetResult();
75+
public static T GetResult<T>(Task<T> task) => task.GetAwaiter().GetResult();
9376

9477
// ValueTask can be backed by an IValueTaskSource that only supports asynchronous awaits, so we have to hook up a callback instead of calling .GetAwaiter().GetResult() like we do for Task.
9578
// The alternative is to convert it to Task using .AsTask(), but that causes allocations which we must avoid for memory diagnoser.
96-
public void GetResult(ValueTask task) => CurrentValueTaskWaiter.GetResult(task);
79+
public static void GetResult(ValueTask task)
80+
{
81+
// Don't continue on the captured context, as that may result in a deadlock if the user runs this in-process.
82+
var awaiter = task.ConfigureAwait(false).GetAwaiter();
83+
if (!awaiter.IsCompleted)
84+
{
85+
CurrentValueTaskWaiter.Wait(awaiter);
86+
}
87+
awaiter.GetResult();
88+
}
9789

98-
public T GetResult<T>(ValueTask<T> task) => CurrentValueTaskWaiter.GetResult(task);
90+
public static T GetResult<T>(ValueTask<T> task)
91+
{
92+
// Don't continue on the captured context, as that may result in a deadlock if the user runs this in-process.
93+
var awaiter = task.ConfigureAwait(false).GetAwaiter();
94+
if (!awaiter.IsCompleted)
95+
{
96+
CurrentValueTaskWaiter.Wait(awaiter);
97+
}
98+
return awaiter.GetResult();
99+
}
99100

100101
internal static MethodInfo GetGetResultMethod(Type taskType)
101102
{
102103
if (!taskType.IsGenericType)
103104
{
104-
return typeof(AwaitHelper).GetMethod(nameof(AwaitHelper.GetResult), BindingFlags.Public | BindingFlags.Instance, null, new Type[1] { taskType }, null);
105+
return typeof(AwaitHelper).GetMethod(nameof(AwaitHelper.GetResult), BindingFlags.Public | BindingFlags.Static, null, new Type[1] { taskType }, null);
105106
}
106107

107108
Type compareType = taskType.GetGenericTypeDefinition() == typeof(ValueTask<>) ? typeof(ValueTask<>)
@@ -116,7 +117,7 @@ internal static MethodInfo GetGetResultMethod(Type taskType)
116117
.ReturnType
117118
.GetMethod(nameof(TaskAwaiter.GetResult), BindingFlags.Public | BindingFlags.Instance)
118119
.ReturnType;
119-
return typeof(AwaitHelper).GetMethods(BindingFlags.Public | BindingFlags.Instance)
120+
return typeof(AwaitHelper).GetMethods(BindingFlags.Public | BindingFlags.Static)
120121
.First(m =>
121122
{
122123
if (m.Name != nameof(AwaitHelper.GetResult)) return false;

src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorStatementExtensions.cs

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,37 +42,6 @@ public static void EmitVoidReturn(this ILGenerator ilBuilder, MethodBuilder meth
4242
ilBuilder.Emit(OpCodes.Ret);
4343
}
4444

45-
public static void EmitSetFieldToNewInstance(
46-
this ILGenerator ilBuilder,
47-
FieldBuilder field,
48-
Type instanceType)
49-
{
50-
if (field.IsStatic)
51-
throw new ArgumentException("The field should be instance field", nameof(field));
52-
53-
if (instanceType != null)
54-
{
55-
/*
56-
IL_0006: ldarg.0
57-
IL_0007: newobj instance void BenchmarkDotNet.Helpers.AwaitHelper::.ctor()
58-
IL_000c: stfld class BenchmarkDotNet.Helpers.AwaitHelper BenchmarkDotNet.Autogenerated.Runnable_0::awaitHelper
59-
*/
60-
var ctor = instanceType.GetConstructor(Array.Empty<Type>());
61-
if (ctor == null)
62-
throw new InvalidOperationException($"Bug: instanceType {instanceType.Name} does not have a 0-parameter accessible constructor.");
63-
64-
ilBuilder.Emit(OpCodes.Ldarg_0);
65-
ilBuilder.Emit(OpCodes.Newobj, ctor);
66-
ilBuilder.Emit(OpCodes.Stfld, field);
67-
}
68-
else
69-
{
70-
ilBuilder.Emit(OpCodes.Ldarg_0);
71-
ilBuilder.Emit(OpCodes.Ldnull);
72-
ilBuilder.Emit(OpCodes.Stfld, field);
73-
}
74-
}
75-
7645
public static void EmitSetDelegateToThisField(
7746
this ILGenerator ilBuilder,
7847
FieldBuilder delegateField,

src/BenchmarkDotNet/Templates/BenchmarkType.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@
5757

5858
public Runnable_$ID$()
5959
{
60-
awaitHelper = new BenchmarkDotNet.Helpers.AwaitHelper();
61-
6260
globalSetupAction = $GlobalSetupMethodName$;
6361
globalCleanupAction = $GlobalCleanupMethodName$;
6462
iterationSetupAction = $IterationSetupMethodName$;
@@ -68,8 +66,6 @@
6866
$InitializeArgumentFields$
6967
}
7068

71-
private readonly BenchmarkDotNet.Helpers.AwaitHelper awaitHelper;
72-
7369
private System.Action globalSetupAction;
7470
private System.Action globalCleanupAction;
7571
private System.Action iterationSetupAction;

src/BenchmarkDotNet/Toolchains/InProcess.Emit.Implementation/Emitters/RunnableEmitter.cs

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,6 @@ private static void EmitNoArgsMethodCallPopReturn(
246246
private ConsumableTypeInfo consumableInfo;
247247
private ConsumeEmitter consumeEmitter;
248248

249-
private FieldBuilder awaitHelperField;
250249
private FieldBuilder globalSetupActionField;
251250
private FieldBuilder globalCleanupActionField;
252251
private FieldBuilder iterationSetupActionField;
@@ -413,8 +412,6 @@ private Type EmitWorkloadDelegateType()
413412

414413
private void DefineFields()
415414
{
416-
awaitHelperField =
417-
runnableBuilder.DefineField(AwaitHelperFieldName, typeof(Helpers.AwaitHelper), FieldAttributes.Private | FieldAttributes.InitOnly);
418415
globalSetupActionField =
419416
runnableBuilder.DefineField(GlobalSetupActionFieldName, typeof(Action), FieldAttributes.Private);
420417
globalCleanupActionField =
@@ -587,13 +584,6 @@ private MethodInfo EmitWorkloadImplementation(string methodName)
587584

588585
var ilBuilder = methodBuilder.GetILGenerator();
589586

590-
/*
591-
IL_0007: ldarg.0
592-
IL_0008: ldfld class BenchmarkDotNet.Helpers.AwaitHelper BenchmarkDotNet.Helpers.Runnable_0::awaitHelper
593-
*/
594-
ilBuilder.Emit(OpCodes.Ldarg_0);
595-
ilBuilder.Emit(OpCodes.Ldfld, awaitHelperField);
596-
597587
/*
598588
IL_0026: ldarg.0
599589
IL_0027: ldloc.0
@@ -608,11 +598,11 @@ private MethodInfo EmitWorkloadImplementation(string methodName)
608598
ilBuilder.Emit(OpCodes.Call, Descriptor.WorkloadMethod);
609599

610600
/*
611-
// awaitHelper.GetResult(...);
612-
IL_000e: callvirt instance void BenchmarkDotNet.Helpers.AwaitHelper::GetResult(class [System.Private.CoreLib]System.Threading.Tasks.Task)
601+
// BenchmarkDotNet.Helpers.AwaitHelper.GetResult(...);
602+
IL_000e: call !!0 BenchmarkDotNet.Helpers.AwaitHelper::GetResult<int32>(valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<!!0>)
613603
*/
614604

615-
ilBuilder.Emit(OpCodes.Callvirt, consumableInfo.GetResultMethod);
605+
ilBuilder.Emit(OpCodes.Call, consumableInfo.GetResultMethod);
616606

617607
/*
618608
IL_0014: ret
@@ -850,16 +840,6 @@ .locals init (
850840
*/
851841
EmitLoadArgFieldsToLocals(ilBuilder, argLocals, skipFirstArg);
852842

853-
if (consumableInfo.IsAwaitable)
854-
{
855-
/*
856-
IL_0026: ldarg.0
857-
IL_0027: ldfld class BenchmarkDotNet.Helpers.AwaitHelper BenchmarkDotNet.Helpers.Runnable_0::awaitHelper
858-
*/
859-
ilBuilder.Emit(OpCodes.Ldarg_0);
860-
ilBuilder.Emit(OpCodes.Ldfld, awaitHelperField);
861-
}
862-
863843
/*
864844
IL_0026: ldarg.0
865845
IL_0027: ldloc.0
@@ -878,10 +858,10 @@ .locals init (
878858
if (consumableInfo.IsAwaitable)
879859
{
880860
/*
881-
// awaitHelper.GetResult(...);
882-
IL_0036: callvirt instance void BenchmarkDotNet.Helpers.AwaitHelper::GetResult(class [System.Private.CoreLib]System.Threading.Tasks.Task)
861+
// BenchmarkDotNet.Helpers.AwaitHelper.GetResult(...);
862+
IL_000e: call !!0 BenchmarkDotNet.Helpers.AwaitHelper::GetResult<int32>(valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<!!0>)
883863
*/
884-
ilBuilder.Emit(OpCodes.Callvirt, consumableInfo.GetResultMethod);
864+
ilBuilder.Emit(OpCodes.Call, consumableInfo.GetResultMethod);
885865
}
886866

887867
/*
@@ -944,7 +924,6 @@ private void EmitCtorBody()
944924

945925
consumeEmitter.OnEmitCtorBody(ctorMethod, ilBuilder);
946926

947-
ilBuilder.EmitSetFieldToNewInstance(awaitHelperField, typeof(Helpers.AwaitHelper));
948927
ilBuilder.EmitSetDelegateToThisField(globalSetupActionField, globalSetupMethod);
949928
ilBuilder.EmitSetDelegateToThisField(globalCleanupActionField, globalCleanupMethod);
950929
ilBuilder.EmitSetDelegateToThisField(iterationSetupActionField, iterationSetupMethod);

src/BenchmarkDotNet/Toolchains/InProcess.Emit.Implementation/Runnable/RunnableConstants.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public class RunnableConstants
1616
public const string ArgFieldPrefix = "__argField";
1717
public const string ArgParamPrefix = "arg";
1818

19-
public const string AwaitHelperFieldName = "awaitHelper";
2019
public const string GlobalSetupActionFieldName = "globalSetupAction";
2120
public const string GlobalCleanupActionFieldName = "globalCleanupAction";
2221
public const string IterationSetupActionFieldName = "iterationSetupAction";

src/BenchmarkDotNet/Toolchains/InProcess.NoEmit/BenchmarkActionFactory_Implementations.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ private void InvokeMultipleHardcoded(long repeatCount)
6969

7070
internal class BenchmarkActionTask : BenchmarkActionBase
7171
{
72-
private readonly Helpers.AwaitHelper awaitHelper = new Helpers.AwaitHelper();
7372
private readonly Func<Task> startTaskCallback;
7473
private readonly Action callback;
7574
private readonly Action unrolledCallback;
@@ -98,7 +97,7 @@ public BenchmarkActionTask(object instance, MethodInfo method, int unrollFactor)
9897
private void Overhead() { }
9998

10099
// must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate
101-
private void ExecuteBlocking() => awaitHelper.GetResult(startTaskCallback.Invoke());
100+
private void ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke());
102101

103102
private void InvokeMultipleHardcoded(long repeatCount)
104103
{
@@ -109,7 +108,6 @@ private void InvokeMultipleHardcoded(long repeatCount)
109108

110109
internal class BenchmarkActionTask<T> : BenchmarkActionBase
111110
{
112-
private readonly Helpers.AwaitHelper awaitHelper = new Helpers.AwaitHelper();
113111
private readonly Func<Task<T>> startTaskCallback;
114112
private readonly Func<T> callback;
115113
private readonly Func<T> unrolledCallback;
@@ -137,7 +135,7 @@ public BenchmarkActionTask(object instance, MethodInfo method, int unrollFactor)
137135
private T Overhead() => default;
138136

139137
// must be kept in sync with GenericTaskDeclarationsProvider.TargetMethodDelegate
140-
private T ExecuteBlocking() => awaitHelper.GetResult(startTaskCallback.Invoke());
138+
private T ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke());
141139

142140
private void InvokeSingleHardcoded() => result = callback();
143141

@@ -152,7 +150,6 @@ private void InvokeMultipleHardcoded(long repeatCount)
152150

153151
internal class BenchmarkActionValueTask<T> : BenchmarkActionBase
154152
{
155-
private readonly Helpers.AwaitHelper awaitHelper = new Helpers.AwaitHelper();
156153
private readonly Func<ValueTask<T>> startTaskCallback;
157154
private readonly Func<T> callback;
158155
private readonly Func<T> unrolledCallback;
@@ -181,7 +178,7 @@ public BenchmarkActionValueTask(object instance, MethodInfo method, int unrollFa
181178
private T Overhead() => default;
182179

183180
// must be kept in sync with GenericTaskDeclarationsProvider.TargetMethodDelegate
184-
private T ExecuteBlocking() => awaitHelper.GetResult(startTaskCallback.Invoke());
181+
private T ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke());
185182

186183
private void InvokeSingleHardcoded() => result = callback();
187184

src/BenchmarkDotNet/Toolchains/InProcess/BenchmarkActionFactory_Implementations.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ private void InvokeMultipleHardcoded(long repeatCount)
8383

8484
internal class BenchmarkActionTask : BenchmarkActionBase
8585
{
86-
private readonly Helpers.AwaitHelper awaitHelper = new Helpers.AwaitHelper();
8786
private readonly Func<Task> startTaskCallback;
8887
private readonly Action callback;
8988
private readonly Action unrolledCallback;
@@ -118,7 +117,7 @@ public BenchmarkActionTask(object instance, MethodInfo method, BenchmarkActionCo
118117
private void Overhead() { }
119118

120119
// must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate
121-
private void ExecuteBlocking() => awaitHelper.GetResult(startTaskCallback.Invoke());
120+
private void ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke());
122121

123122
private void InvokeMultipleHardcoded(long repeatCount)
124123
{
@@ -129,7 +128,6 @@ private void InvokeMultipleHardcoded(long repeatCount)
129128

130129
internal class BenchmarkActionTask<T> : BenchmarkActionBase
131130
{
132-
private readonly Helpers.AwaitHelper awaitHelper = new Helpers.AwaitHelper();
133131
private readonly Func<Task<T>> startTaskCallback;
134132
private readonly Func<T> callback;
135133
private readonly Func<T> unrolledCallback;
@@ -164,7 +162,7 @@ public BenchmarkActionTask(object instance, MethodInfo method, BenchmarkActionCo
164162
private T Overhead() => default;
165163

166164
// must be kept in sync with GenericTaskDeclarationsProvider.TargetMethodDelegate
167-
private T ExecuteBlocking() => awaitHelper.GetResult(startTaskCallback());
165+
private T ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback());
168166

169167
private void InvokeSingleHardcoded() => result = callback();
170168

@@ -179,7 +177,6 @@ private void InvokeMultipleHardcoded(long repeatCount)
179177

180178
internal class BenchmarkActionValueTask<T> : BenchmarkActionBase
181179
{
182-
private readonly Helpers.AwaitHelper awaitHelper = new Helpers.AwaitHelper();
183180
private readonly Func<ValueTask<T>> startTaskCallback;
184181
private readonly Func<T> callback;
185182
private readonly Func<T> unrolledCallback;
@@ -214,7 +211,7 @@ public BenchmarkActionValueTask(object instance, MethodInfo method, BenchmarkAct
214211
private T Overhead() => default;
215212

216213
// must be kept in sync with GenericTaskDeclarationsProvider.TargetMethodDelegate
217-
private T ExecuteBlocking() => awaitHelper.GetResult(startTaskCallback());
214+
private T ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback());
218215

219216
private void InvokeSingleHardcoded() => result = callback();
220217

0 commit comments

Comments
 (0)