committed by
GitHub
36 changed files with 1521 additions and 1576 deletions
@ -1,14 +1,13 @@ |
|||||
namespace LINGYUN.Abp.EventBus.CAP |
namespace LINGYUN.Abp.EventBus.CAP; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 过期消息清理配置项
|
||||
|
/// </summary>
|
||||
|
public class AbpCAPEventBusOptions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// 过期消息清理配置项
|
/// 发布消息处理失败通知
|
||||
|
/// default: false
|
||||
/// </summary>
|
/// </summary>
|
||||
public class AbpCAPEventBusOptions |
public bool NotifyFailedCallback { get; set; } = false; |
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 发布消息处理失败通知
|
|
||||
/// default: false
|
|
||||
/// </summary>
|
|
||||
public bool NotifyFailedCallback { get; set; } = false; |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,13 +1,12 @@ |
|||||
namespace LINGYUN.Abp.EventBus.CAP |
namespace LINGYUN.Abp.EventBus.CAP; |
||||
|
|
||||
|
public static class AbpCAPHeaders |
||||
{ |
{ |
||||
public static class AbpCAPHeaders |
public static string ClientId { get; set; } = "cap-abp-client-id"; |
||||
{ |
|
||||
public static string ClientId { get; set; } = "cap-abp-client-id"; |
|
||||
|
|
||||
public static string UserId { get; set; } = "cap-abp-user-id"; |
public static string UserId { get; set; } = "cap-abp-user-id"; |
||||
|
|
||||
public static string TenantId { get; set; } = "cap-abp-tenant-id"; |
public static string TenantId { get; set; } = "cap-abp-tenant-id"; |
||||
|
|
||||
public static string MessageId { get; set; } = "cap-abp-message-id"; |
public static string MessageId { get; set; } = "cap-abp-message-id"; |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,47 +1,46 @@ |
|||||
using DotNetCore.CAP.Messages; |
using DotNetCore.CAP.Messages; |
||||
using System; |
using System; |
||||
|
|
||||
namespace LINGYUN.Abp.EventBus.CAP |
namespace LINGYUN.Abp.EventBus.CAP; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// CAP消息扩展
|
||||
|
/// </summary>
|
||||
|
public static class AbpCAPMessageExtensions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// CAP消息扩展
|
/// 尝试获取消息标头中的租户标识
|
||||
/// </summary>
|
/// </summary>
|
||||
public static class AbpCAPMessageExtensions |
/// <param name="message"></param>
|
||||
|
/// <param name="tenantId"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public static bool TryGetTenantId( |
||||
|
this Message message, |
||||
|
out Guid? tenantId) |
||||
{ |
{ |
||||
/// <summary>
|
if (message.Headers.TryGetValue(AbpCAPHeaders.TenantId, out string tenantStr)) |
||||
/// 尝试获取消息标头中的租户标识
|
|
||||
/// </summary>
|
|
||||
/// <param name="message"></param>
|
|
||||
/// <param name="tenantId"></param>
|
|
||||
/// <returns></returns>
|
|
||||
public static bool TryGetTenantId( |
|
||||
this Message message, |
|
||||
out Guid? tenantId) |
|
||||
{ |
{ |
||||
if (message.Headers.TryGetValue(AbpCAPHeaders.TenantId, out string tenantStr)) |
if (Guid.TryParse(tenantStr, out Guid id)) |
||||
{ |
{ |
||||
if (Guid.TryParse(tenantStr, out Guid id)) |
tenantId = id; |
||||
{ |
return true; |
||||
tenantId = id; |
|
||||
return true; |
|
||||
} |
|
||||
} |
} |
||||
tenantId = null; |
|
||||
return false; |
|
||||
} |
} |
||||
/// <summary>
|
tenantId = null; |
||||
/// 获取消息标头中的租户标识
|
return false; |
||||
/// </summary>
|
} |
||||
/// <param name="message"></param>
|
/// <summary>
|
||||
/// <returns></returns>
|
/// 获取消息标头中的租户标识
|
||||
public static Guid? GetTenantIdOrNull( |
/// </summary>
|
||||
this Message message) |
/// <param name="message"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public static Guid? GetTenantIdOrNull( |
||||
|
this Message message) |
||||
|
{ |
||||
|
if (message.TryGetTenantId(out Guid? tenantId)) |
||||
{ |
{ |
||||
if (message.TryGetTenantId(out Guid? tenantId)) |
return tenantId; |
||||
{ |
|
||||
return tenantId; |
|
||||
} |
|
||||
return null; |
|
||||
} |
} |
||||
|
return null; |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,24 +1,23 @@ |
|||||
using System; |
using System; |
||||
using Volo.Abp.EventBus; |
using Volo.Abp.EventBus; |
||||
|
|
||||
namespace LINGYUN.Abp.EventBus.CAP |
namespace LINGYUN.Abp.EventBus.CAP; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 自定义事件订阅者
|
||||
|
/// </summary>
|
||||
|
public interface ICustomDistributedEventSubscriber |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// 自定义事件订阅者
|
/// 订阅事件
|
||||
|
/// </summary>
|
||||
|
/// <param name="eventType"></param>
|
||||
|
/// <param name="factory"></param>
|
||||
|
void Subscribe(Type eventType, IEventHandlerFactory factory); |
||||
|
/// <summary>
|
||||
|
/// 取消订阅
|
||||
/// </summary>
|
/// </summary>
|
||||
public interface ICustomDistributedEventSubscriber |
/// <param name="eventType"></param>
|
||||
{ |
/// <param name="factory"></param>
|
||||
/// <summary>
|
void UnSubscribe(Type eventType, IEventHandlerFactory factory); |
||||
/// 订阅事件
|
|
||||
/// </summary>
|
|
||||
/// <param name="eventType"></param>
|
|
||||
/// <param name="factory"></param>
|
|
||||
void Subscribe(Type eventType, IEventHandlerFactory factory); |
|
||||
/// <summary>
|
|
||||
/// 取消订阅
|
|
||||
/// </summary>
|
|
||||
/// <param name="eventType"></param>
|
|
||||
/// <param name="factory"></param>
|
|
||||
void UnSubscribe(Type eventType, IEventHandlerFactory factory); |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,9 +1,8 @@ |
|||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
|
|
||||
namespace LINGYUN.Abp.EventBus.CAP |
namespace LINGYUN.Abp.EventBus.CAP; |
||||
|
|
||||
|
public interface IFailedThresholdCallbackNotifier |
||||
{ |
{ |
||||
public interface IFailedThresholdCallbackNotifier |
Task NotifyAsync(AbpCAPExecutionFailedException exception); |
||||
{ |
|
||||
Task NotifyAsync(AbpCAPExecutionFailedException exception); |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,337 +1,329 @@ |
|||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
|
||||
|
#nullable enable |
||||
|
|
||||
using System; |
using System; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
using System.Linq.Expressions; |
using System.Linq.Expressions; |
||||
using System.Reflection; |
using System.Reflection; |
||||
|
|
||||
namespace LINGYUN.Abp.EventBus.CAP.Internal |
namespace LINGYUN.Abp.EventBus.CAP.Internal; |
||||
|
|
||||
|
internal class ObjectMethodExecutor |
||||
{ |
{ |
||||
internal class ObjectMethodExecutor |
private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor = |
||||
{ |
typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] |
||||
// ReSharper disable once InconsistentNaming
|
|
||||
private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor = |
|
||||
typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] |
|
||||
{ |
|
||||
typeof(object), // customAwaitable
|
|
||||
typeof(Func<object, object>), // getAwaiterMethod
|
|
||||
typeof(Func<object, bool>), // isCompletedMethod
|
|
||||
typeof(Func<object, object>), // getResultMethod
|
|
||||
typeof(Action<object, Action>), // onCompletedMethod
|
|
||||
typeof(Action<object, Action>) // unsafeOnCompletedMethod
|
|
||||
}); |
|
||||
|
|
||||
private readonly MethodExecutor _executor; |
|
||||
private readonly MethodExecutorAsync _executorAsync; |
|
||||
private readonly object[] _parameterDefaultValues; |
|
||||
|
|
||||
private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) |
|
||||
{ |
{ |
||||
if (methodInfo == null) |
typeof(object), // customAwaitable
|
||||
{ |
typeof(Func<object, object>), // getAwaiterMethod
|
||||
throw new ArgumentNullException(nameof(methodInfo)); |
typeof(Func<object, bool>), // isCompletedMethod
|
||||
} |
typeof(Func<object, object>), // getResultMethod
|
||||
|
typeof(Action<object, Action>), // onCompletedMethod
|
||||
|
typeof(Action<object, Action>) // unsafeOnCompletedMethod
|
||||
|
})!; |
||||
|
|
||||
|
private readonly MethodExecutor? _executor; |
||||
|
private readonly MethodExecutorAsync? _executorAsync; |
||||
|
private readonly object?[]? _parameterDefaultValues; |
||||
|
|
||||
|
private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[]? parameterDefaultValues) |
||||
|
{ |
||||
|
if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); |
||||
|
|
||||
MethodInfo = methodInfo; |
MethodInfo = methodInfo; |
||||
MethodParameters = methodInfo.GetParameters(); |
MethodParameters = methodInfo.GetParameters(); |
||||
TargetTypeInfo = targetTypeInfo; |
TargetTypeInfo = targetTypeInfo; |
||||
MethodReturnType = methodInfo.ReturnType; |
MethodReturnType = methodInfo.ReturnType; |
||||
|
|
||||
var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo); |
var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo); |
||||
|
|
||||
IsMethodAsync = isAwaitable; |
IsMethodAsync = isAwaitable; |
||||
AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null; |
AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null; |
||||
|
|
||||
// Upstream code may prefer to use the sync-executor even for async methods, because if it knows
|
// Upstream code may prefer to use the sync-executor even for async methods, because if it knows
|
||||
// that the result is a specific Task<T> where T is known, then it can directly cast to that type
|
// that the result is a specific Task<T> where T is known, then it can directly cast to that type
|
||||
// and await it without the extra heap allocations involved in the _executorAsync code path.
|
// and await it without the extra heap allocations involved in the _executorAsync code path.
|
||||
_executor = GetExecutor(methodInfo, targetTypeInfo); |
_executor = GetExecutor(methodInfo, targetTypeInfo); |
||||
|
|
||||
if (IsMethodAsync) |
if (IsMethodAsync) _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); |
||||
{ |
|
||||
_executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); |
|
||||
} |
|
||||
|
|
||||
_parameterDefaultValues = parameterDefaultValues; |
_parameterDefaultValues = parameterDefaultValues; |
||||
} |
} |
||||
|
|
||||
public MethodInfo MethodInfo { get; } |
public MethodInfo MethodInfo { get; } |
||||
|
|
||||
public ParameterInfo[] MethodParameters { get; } |
public ParameterInfo[] MethodParameters { get; } |
||||
|
|
||||
public TypeInfo TargetTypeInfo { get; } |
public TypeInfo TargetTypeInfo { get; } |
||||
|
|
||||
public Type AsyncResultType { get; } |
public Type? AsyncResultType { get; } |
||||
|
|
||||
// This field is made internal set because it is set in unit tests.
|
// This field is made internal set because it is set in unit tests.
|
||||
public Type MethodReturnType { get; internal set; } |
public Type MethodReturnType { get; internal set; } |
||||
|
|
||||
public bool IsMethodAsync { get; } |
public bool IsMethodAsync { get; } |
||||
|
|
||||
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) |
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) |
||||
{ |
{ |
||||
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); |
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); |
||||
} |
} |
||||
|
|
||||
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, |
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, |
||||
object[] parameterDefaultValues) |
object?[] parameterDefaultValues) |
||||
{ |
{ |
||||
if (parameterDefaultValues == null) |
if (parameterDefaultValues == null) throw new ArgumentNullException(nameof(parameterDefaultValues)); |
||||
{ |
|
||||
throw new ArgumentNullException(nameof(parameterDefaultValues)); |
|
||||
} |
|
||||
|
|
||||
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); |
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); |
||||
} |
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes the configured method on <paramref name="target" />. This can be used whether or not
|
||||
|
/// the configured method is asynchronous.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than
|
||||
|
/// ExecuteAsync if you know at compile time what the return type is, because then you can directly
|
||||
|
/// "await" that value (via a cast), and then the generated code will be able to reference the
|
||||
|
/// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated
|
||||
|
/// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at
|
||||
|
/// compile time what type it would be.
|
||||
|
/// </remarks>
|
||||
|
/// <param name="target">The object whose method is to be executed.</param>
|
||||
|
/// <param name="parameters">Parameters to pass to the method.</param>
|
||||
|
/// <returns>The method return value.</returns>
|
||||
|
public object? Execute(object target, object?[]? parameters) |
||||
|
{ |
||||
|
Debug.Assert(_executor != null, "Sync execution is not supported."); |
||||
|
return _executor(target, parameters); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes the configured method on <paramref name="target" />. This can only be used if the configured
|
||||
|
/// method is asynchronous.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync,
|
||||
|
/// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations
|
||||
|
/// as compared with using Execute and then using "await" on the result value typecasted to the known
|
||||
|
/// awaitable type. The possible extra heap allocations are for:
|
||||
|
/// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally
|
||||
|
/// it's a reference type, and you normally create a new instance per call).
|
||||
|
/// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance
|
||||
|
/// of it, and if it is, it will have to be boxed so the calling code can reference it as an object).
|
||||
|
/// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling
|
||||
|
/// code doesn't know what type it's going to be).
|
||||
|
/// </remarks>
|
||||
|
/// <param name="target">The object whose method is to be executed.</param>
|
||||
|
/// <param name="parameters">Parameters to pass to the method.</param>
|
||||
|
/// <returns>An object that you can "await" to get the method return value.</returns>
|
||||
|
public ObjectMethodExecutorAwaitable ExecuteAsync(object target, object?[]? parameters) |
||||
|
{ |
||||
|
Debug.Assert(_executorAsync != null, "Async execution is not supported."); |
||||
|
return _executorAsync(target, parameters); |
||||
|
} |
||||
|
|
||||
|
public object? GetDefaultValueForParameter(int index) |
||||
|
{ |
||||
|
if (_parameterDefaultValues == null) |
||||
|
throw new InvalidOperationException( |
||||
|
$"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); |
||||
|
|
||||
|
if (index < 0 || index > MethodParameters.Length - 1) throw new ArgumentOutOfRangeException(nameof(index)); |
||||
|
|
||||
/// <summary>
|
return _parameterDefaultValues[index]; |
||||
/// Executes the configured method on <paramref name="target" />. This can be used whether or not
|
} |
||||
/// the configured method is asynchronous.
|
|
||||
/// </summary>
|
private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) |
||||
/// <remarks>
|
{ |
||||
/// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than
|
// Parameters to executor
|
||||
/// ExecuteAsync if you know at compile time what the return type is, because then you can directly
|
var targetParameter = Expression.Parameter(typeof(object), "target"); |
||||
/// "await" that value (via a cast), and then the generated code will be able to reference the
|
var parametersParameter = Expression.Parameter(typeof(object?[]), "parameters"); |
||||
/// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated
|
|
||||
/// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at
|
// Build parameter list
|
||||
/// compile time what type it would be.
|
var paramInfos = methodInfo.GetParameters(); |
||||
/// </remarks>
|
var parameters = new List<Expression>(paramInfos.Length); |
||||
/// <param name="target">The object whose method is to be executed.</param>
|
for (var i = 0; i < paramInfos.Length; i++) |
||||
/// <param name="parameters">Parameters to pass to the method.</param>
|
|
||||
/// <returns>The method return value.</returns>
|
|
||||
public object Execute(object target, params object[] parameters) |
|
||||
{ |
{ |
||||
return _executor(target, parameters); |
var paramInfo = paramInfos[i]; |
||||
|
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); |
||||
|
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); |
||||
|
|
||||
|
// valueCast is "(Ti) parameters[i]"
|
||||
|
parameters.Add(valueCast); |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
// Call method
|
||||
/// Executes the configured method on <paramref name="target" />. This can only be used if the configured
|
var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); |
||||
/// method is asynchronous.
|
var methodCall = Expression.Call(instanceCast, methodInfo, parameters); |
||||
/// </summary>
|
|
||||
/// <remarks>
|
// methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)"
|
||||
/// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync,
|
// Create function
|
||||
/// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations
|
if (methodCall.Type == typeof(void)) |
||||
/// as compared with using Execute and then using "await" on the result value typecasted to the known
|
|
||||
/// awaitable type. The possible extra heap allocations are for:
|
|
||||
/// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally
|
|
||||
/// it's a reference type, and you normally create a new instance per call).
|
|
||||
/// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance
|
|
||||
/// of it, and if it is, it will have to be boxed so the calling code can reference it as an object).
|
|
||||
/// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling
|
|
||||
/// code doesn't know what type it's going to be).
|
|
||||
/// </remarks>
|
|
||||
/// <param name="target">The object whose method is to be executed.</param>
|
|
||||
/// <param name="parameters">Parameters to pass to the method.</param>
|
|
||||
/// <returns>An object that you can "await" to get the method return value.</returns>
|
|
||||
public ObjectMethodExecutorAwaitable ExecuteAsync(object target, params object[] parameters) |
|
||||
{ |
{ |
||||
return _executorAsync(target, parameters); |
var lambda = Expression.Lambda<VoidMethodExecutor>(methodCall, targetParameter, parametersParameter); |
||||
|
var voidExecutor = lambda.Compile(); |
||||
|
return WrapVoidMethod(voidExecutor); |
||||
} |
} |
||||
|
else |
||||
public object GetDefaultValueForParameter(int index) |
|
||||
{ |
{ |
||||
if (_parameterDefaultValues == null) |
// must coerce methodCall to match ActionExecutor signature
|
||||
{ |
var castMethodCall = Expression.Convert(methodCall, typeof(object)); |
||||
throw new InvalidOperationException( |
var lambda = Expression.Lambda<MethodExecutor>(castMethodCall, targetParameter, parametersParameter); |
||||
$"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); |
return lambda.Compile(); |
||||
} |
|
||||
|
|
||||
if (index < 0 || index > MethodParameters.Length - 1) |
|
||||
{ |
|
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
|
||||
} |
|
||||
|
|
||||
return _parameterDefaultValues[index]; |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) |
private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor) |
||||
|
{ |
||||
|
return delegate (object target, object?[]? parameters) |
||||
{ |
{ |
||||
// Parameters to executor
|
executor(target, parameters); |
||||
var targetParameter = Expression.Parameter(typeof(object), "target"); |
return null; |
||||
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); |
}; |
||||
|
} |
||||
// Build parameter list
|
|
||||
var parameters = new List<Expression>(); |
|
||||
var paramInfos = methodInfo.GetParameters(); |
|
||||
for (var i = 0; i < paramInfos.Length; i++) |
|
||||
{ |
|
||||
var paramInfo = paramInfos[i]; |
|
||||
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); |
|
||||
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); |
|
||||
|
|
||||
// valueCast is "(Ti) parameters[i]"
|
|
||||
parameters.Add(valueCast); |
|
||||
} |
|
||||
|
|
||||
// Call method
|
|
||||
var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); |
|
||||
var methodCall = Expression.Call(instanceCast, methodInfo, parameters); |
|
||||
|
|
||||
// methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)"
|
|
||||
// Create function
|
|
||||
if (methodCall.Type == typeof(void)) |
|
||||
{ |
|
||||
var lambda = Expression.Lambda<VoidMethodExecutor>(methodCall, targetParameter, parametersParameter); |
|
||||
var voidExecutor = lambda.Compile(); |
|
||||
return WrapVoidMethod(voidExecutor); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// must coerce methodCall to match ActionExecutor signature
|
|
||||
var castMethodCall = Expression.Convert(methodCall, typeof(object)); |
|
||||
var lambda = Expression.Lambda<MethodExecutor>(castMethodCall, targetParameter, parametersParameter); |
|
||||
return lambda.Compile(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor) |
private static MethodExecutorAsync GetExecutorAsync( |
||||
|
MethodInfo methodInfo, |
||||
|
TypeInfo targetTypeInfo, |
||||
|
CoercedAwaitableInfo coercedAwaitableInfo) |
||||
|
{ |
||||
|
// Parameters to executor
|
||||
|
var targetParameter = Expression.Parameter(typeof(object), "target"); |
||||
|
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); |
||||
|
|
||||
|
// Build parameter list
|
||||
|
var paramInfos = methodInfo.GetParameters(); |
||||
|
var parameters = new List<Expression>(paramInfos.Length); |
||||
|
for (var i = 0; i < paramInfos.Length; i++) |
||||
{ |
{ |
||||
return delegate (object target, object[] parameters) |
var paramInfo = paramInfos[i]; |
||||
{ |
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); |
||||
executor(target, parameters); |
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); |
||||
return null; |
|
||||
}; |
// valueCast is "(Ti) parameters[i]"
|
||||
|
parameters.Add(valueCast); |
||||
} |
} |
||||
|
|
||||
private static MethodExecutorAsync GetExecutorAsync( |
// Call method
|
||||
MethodInfo methodInfo, |
var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); |
||||
TypeInfo targetTypeInfo, |
var methodCall = Expression.Call(instanceCast, methodInfo, parameters); |
||||
CoercedAwaitableInfo coercedAwaitableInfo) |
|
||||
{ |
// Using the method return value, construct an ObjectMethodExecutorAwaitable based on
|
||||
// Parameters to executor
|
// the info we have about its implementation of the awaitable pattern. Note that all
|
||||
var targetParameter = Expression.Parameter(typeof(object), "target"); |
// the funcs/actions we construct here are precompiled, so that only one instance of
|
||||
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); |
// each is preserved throughout the lifetime of the ObjectMethodExecutor.
|
||||
|
|
||||
// Build parameter list
|
// var getAwaiterFunc = (object awaitable) =>
|
||||
var parameters = new List<Expression>(); |
// (object)((CustomAwaitableType)awaitable).GetAwaiter();
|
||||
var paramInfos = methodInfo.GetParameters(); |
var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable"); |
||||
for (var i = 0; i < paramInfos.Length; i++) |
var awaitableInfo = coercedAwaitableInfo.AwaitableInfo; |
||||
{ |
var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType; |
||||
var paramInfo = paramInfos[i]; |
var getAwaiterFunc = Expression.Lambda<Func<object, object>>( |
||||
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); |
Expression.Convert( |
||||
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); |
Expression.Call( |
||||
|
Expression.Convert(customAwaitableParam, postCoercionMethodReturnType), |
||||
// valueCast is "(Ti) parameters[i]"
|
awaitableInfo.GetAwaiterMethod), |
||||
parameters.Add(valueCast); |
typeof(object)), |
||||
} |
customAwaitableParam).Compile(); |
||||
|
|
||||
// Call method
|
// var isCompletedFunc = (object awaiter) =>
|
||||
var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); |
// ((CustomAwaiterType)awaiter).IsCompleted;
|
||||
var methodCall = Expression.Call(instanceCast, methodInfo, parameters); |
var isCompletedParam = Expression.Parameter(typeof(object), "awaiter"); |
||||
|
var isCompletedFunc = Expression.Lambda<Func<object, bool>>( |
||||
// Using the method return value, construct an ObjectMethodExecutorAwaitable based on
|
Expression.MakeMemberAccess( |
||||
// the info we have about its implementation of the awaitable pattern. Note that all
|
Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType), |
||||
// the funcs/actions we construct here are precompiled, so that only one instance of
|
awaitableInfo.AwaiterIsCompletedProperty), |
||||
// each is preserved throughout the lifetime of the ObjectMethodExecutor.
|
isCompletedParam).Compile(); |
||||
|
|
||||
// var getAwaiterFunc = (object awaitable) =>
|
var getResultParam = Expression.Parameter(typeof(object), "awaiter"); |
||||
// (object)((CustomAwaitableType)awaitable).GetAwaiter();
|
Func<object, object> getResultFunc; |
||||
var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable"); |
if (awaitableInfo.ResultType == typeof(void)) |
||||
var awaitableInfo = coercedAwaitableInfo.AwaitableInfo; |
// var getResultFunc = (object awaiter) =>
|
||||
var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType; |
// {
|
||||
var getAwaiterFunc = Expression.Lambda<Func<object, object>>( |
// ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions
|
||||
|
// return (object)null;
|
||||
|
// };
|
||||
|
getResultFunc = Expression.Lambda<Func<object, object>>( |
||||
|
Expression.Block( |
||||
|
Expression.Call( |
||||
|
Expression.Convert(getResultParam, awaitableInfo.AwaiterType), |
||||
|
awaitableInfo.AwaiterGetResultMethod), |
||||
|
Expression.Constant(null) |
||||
|
), |
||||
|
getResultParam).Compile(); |
||||
|
else |
||||
|
// var getResultFunc = (object awaiter) =>
|
||||
|
// (object)((CustomAwaiterType)awaiter).GetResult();
|
||||
|
getResultFunc = Expression.Lambda<Func<object, object>>( |
||||
Expression.Convert( |
Expression.Convert( |
||||
Expression.Call( |
Expression.Call( |
||||
Expression.Convert(customAwaitableParam, postCoercionMethodReturnType), |
Expression.Convert(getResultParam, awaitableInfo.AwaiterType), |
||||
awaitableInfo.GetAwaiterMethod), |
awaitableInfo.AwaiterGetResultMethod), |
||||
typeof(object)), |
typeof(object)), |
||||
customAwaitableParam).Compile(); |
getResultParam).Compile(); |
||||
|
|
||||
// var isCompletedFunc = (object awaiter) =>
|
// var onCompletedFunc = (object awaiter, Action continuation) => {
|
||||
// ((CustomAwaiterType)awaiter).IsCompleted;
|
// ((CustomAwaiterType)awaiter).OnCompleted(continuation);
|
||||
var isCompletedParam = Expression.Parameter(typeof(object), "awaiter"); |
// };
|
||||
var isCompletedFunc = Expression.Lambda<Func<object, bool>>( |
var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); |
||||
Expression.MakeMemberAccess( |
var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); |
||||
Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType), |
var onCompletedFunc = Expression.Lambda<Action<object, Action>>( |
||||
awaitableInfo.AwaiterIsCompletedProperty), |
Expression.Call( |
||||
isCompletedParam).Compile(); |
Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType), |
||||
|
awaitableInfo.AwaiterOnCompletedMethod, |
||||
var getResultParam = Expression.Parameter(typeof(object), "awaiter"); |
onCompletedParam2), |
||||
Func<object, object> getResultFunc; |
onCompletedParam1, |
||||
if (awaitableInfo.ResultType == typeof(void)) |
onCompletedParam2).Compile(); |
||||
{ |
|
||||
getResultFunc = Expression.Lambda<Func<object, object>>( |
Action<object, Action>? unsafeOnCompletedFunc = null; |
||||
Expression.Block( |
if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null) |
||||
Expression.Call( |
{ |
||||
Expression.Convert(getResultParam, awaitableInfo.AwaiterType), |
// var unsafeOnCompletedFunc = (object awaiter, Action continuation) => {
|
||||
awaitableInfo.AwaiterGetResultMethod), |
// ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation);
|
||||
Expression.Constant(null) |
|
||||
), |
|
||||
getResultParam).Compile(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
getResultFunc = Expression.Lambda<Func<object, object>>( |
|
||||
Expression.Convert( |
|
||||
Expression.Call( |
|
||||
Expression.Convert(getResultParam, awaitableInfo.AwaiterType), |
|
||||
awaitableInfo.AwaiterGetResultMethod), |
|
||||
typeof(object)), |
|
||||
getResultParam).Compile(); |
|
||||
} |
|
||||
|
|
||||
// var onCompletedFunc = (object awaiter, Action continuation) => {
|
|
||||
// ((CustomAwaiterType)awaiter).OnCompleted(continuation);
|
|
||||
// };
|
// };
|
||||
var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); |
var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); |
||||
var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); |
var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); |
||||
var onCompletedFunc = Expression.Lambda<Action<object, Action>>( |
unsafeOnCompletedFunc = Expression.Lambda<Action<object, Action>>( |
||||
Expression.Call( |
Expression.Call( |
||||
Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType), |
Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType), |
||||
awaitableInfo.AwaiterOnCompletedMethod, |
awaitableInfo.AwaiterUnsafeOnCompletedMethod, |
||||
onCompletedParam2), |
unsafeOnCompletedParam2), |
||||
onCompletedParam1, |
unsafeOnCompletedParam1, |
||||
onCompletedParam2).Compile(); |
unsafeOnCompletedParam2).Compile(); |
||||
|
|
||||
Action<object, Action> unsafeOnCompletedFunc = null; |
|
||||
if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null) |
|
||||
{ |
|
||||
// var unsafeOnCompletedFunc = (object awaiter, Action continuation) => {
|
|
||||
// ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation);
|
|
||||
// };
|
|
||||
var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); |
|
||||
var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); |
|
||||
unsafeOnCompletedFunc = Expression.Lambda<Action<object, Action>>( |
|
||||
Expression.Call( |
|
||||
Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType), |
|
||||
awaitableInfo.AwaiterUnsafeOnCompletedMethod, |
|
||||
unsafeOnCompletedParam2), |
|
||||
unsafeOnCompletedParam1, |
|
||||
unsafeOnCompletedParam2).Compile(); |
|
||||
} |
|
||||
|
|
||||
// If we need to pass the method call result through a coercer function to get an
|
|
||||
// awaitable, then do so.
|
|
||||
var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion |
|
||||
? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall) |
|
||||
: (Expression)methodCall; |
|
||||
|
|
||||
// return new ObjectMethodExecutorAwaitable(
|
|
||||
// (object)coercedMethodCall,
|
|
||||
// getAwaiterFunc,
|
|
||||
// isCompletedFunc,
|
|
||||
// getResultFunc,
|
|
||||
// onCompletedFunc,
|
|
||||
// unsafeOnCompletedFunc);
|
|
||||
var returnValueExpression = Expression.New( |
|
||||
_objectMethodExecutorAwaitableConstructor, |
|
||||
Expression.Convert(coercedMethodCall, typeof(object)), |
|
||||
Expression.Constant(getAwaiterFunc), |
|
||||
Expression.Constant(isCompletedFunc), |
|
||||
Expression.Constant(getResultFunc), |
|
||||
Expression.Constant(onCompletedFunc), |
|
||||
Expression.Constant(unsafeOnCompletedFunc, typeof(Action<object, Action>))); |
|
||||
|
|
||||
var lambda = |
|
||||
Expression.Lambda<MethodExecutorAsync>(returnValueExpression, targetParameter, parametersParameter); |
|
||||
return lambda.Compile(); |
|
||||
} |
} |
||||
|
|
||||
private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, params object[] parameters); |
// If we need to pass the method call result through a coercer function to get an
|
||||
|
// awaitable, then do so.
|
||||
|
var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion |
||||
|
? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall) |
||||
|
: (Expression)methodCall; |
||||
|
|
||||
|
// return new ObjectMethodExecutorAwaitable(
|
||||
|
// (object)coercedMethodCall,
|
||||
|
// getAwaiterFunc,
|
||||
|
// isCompletedFunc,
|
||||
|
// getResultFunc,
|
||||
|
// onCompletedFunc,
|
||||
|
// unsafeOnCompletedFunc);
|
||||
|
var returnValueExpression = Expression.New( |
||||
|
_objectMethodExecutorAwaitableConstructor, |
||||
|
Expression.Convert(coercedMethodCall, typeof(object)), |
||||
|
Expression.Constant(getAwaiterFunc), |
||||
|
Expression.Constant(isCompletedFunc), |
||||
|
Expression.Constant(getResultFunc), |
||||
|
Expression.Constant(onCompletedFunc), |
||||
|
Expression.Constant(unsafeOnCompletedFunc, typeof(Action<object, Action>))); |
||||
|
|
||||
|
var lambda = |
||||
|
Expression.Lambda<MethodExecutorAsync>(returnValueExpression, targetParameter, parametersParameter); |
||||
|
return lambda.Compile(); |
||||
|
} |
||||
|
|
||||
private delegate object MethodExecutor(object target, params object[] parameters); |
private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, object?[]? parameters); |
||||
|
|
||||
private delegate void VoidMethodExecutor(object target, object[] parameters); |
private delegate object? MethodExecutor(object target, object?[]? parameters); |
||||
} |
|
||||
|
private delegate void VoidMethodExecutor(object target, object?[]? parameters); |
||||
} |
} |
||||
|
|||||
@ -1,115 +1,114 @@ |
|||||
using System; |
using System; |
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
|
|
||||
namespace LINGYUN.Abp.EventBus.CAP.Internal |
namespace LINGYUN.Abp.EventBus.CAP.Internal; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides a common awaitable structure that <see cref="ObjectMethodExecutor.ExecuteAsync" /> can
|
||||
|
/// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an
|
||||
|
/// application-defined custom awaitable.
|
||||
|
/// </summary>
|
||||
|
internal readonly struct ObjectMethodExecutorAwaitable |
||||
{ |
{ |
||||
/// <summary>
|
private readonly object _customAwaitable; |
||||
/// Provides a common awaitable structure that <see cref="ObjectMethodExecutor.ExecuteAsync" /> can
|
private readonly Func<object, object> _getAwaiterMethod; |
||||
/// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an
|
private readonly Func<object, bool> _isCompletedMethod; |
||||
/// application-defined custom awaitable.
|
private readonly Func<object, object> _getResultMethod; |
||||
/// </summary>
|
private readonly Action<object, Action> _onCompletedMethod; |
||||
internal struct ObjectMethodExecutorAwaitable |
private readonly Action<object, Action> _unsafeOnCompletedMethod; |
||||
|
|
||||
|
// Perf note: since we're requiring the customAwaitable to be supplied here as an object,
|
||||
|
// this will trigger a further allocation if it was a value type (i.e., to box it). We can't
|
||||
|
// fix this by making the customAwaitable type generic, because the calling code typically
|
||||
|
// does not know the type of the awaitable/awaiter at compile-time anyway.
|
||||
|
//
|
||||
|
// However, we could fix it by not passing the customAwaitable here at all, and instead
|
||||
|
// passing a func that maps directly from the target object (e.g., controller instance),
|
||||
|
// target method (e.g., action method info), and params array to the custom awaiter in the
|
||||
|
// GetAwaiter() method below. In effect, by delaying the actual method call until the
|
||||
|
// upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance.
|
||||
|
// This optimization is not currently implemented because:
|
||||
|
// [1] It would make no difference when the awaitable was an object type, which is
|
||||
|
// by far the most common scenario (e.g., System.Task<T>).
|
||||
|
// [2] It would be complex - we'd need some kind of object pool to track all the parameter
|
||||
|
// arrays until we needed to use them in GetAwaiter().
|
||||
|
// We can reconsider this in the future if there's a need to optimize for ValueTask<T>
|
||||
|
// or other value-typed awaitables.
|
||||
|
|
||||
|
public ObjectMethodExecutorAwaitable( |
||||
|
object customAwaitable, |
||||
|
Func<object, object> getAwaiterMethod, |
||||
|
Func<object, bool> isCompletedMethod, |
||||
|
Func<object, object> getResultMethod, |
||||
|
Action<object, Action> onCompletedMethod, |
||||
|
Action<object, Action> unsafeOnCompletedMethod) |
||||
|
{ |
||||
|
_customAwaitable = customAwaitable; |
||||
|
_getAwaiterMethod = getAwaiterMethod; |
||||
|
_isCompletedMethod = isCompletedMethod; |
||||
|
_getResultMethod = getResultMethod; |
||||
|
_onCompletedMethod = onCompletedMethod; |
||||
|
_unsafeOnCompletedMethod = unsafeOnCompletedMethod; |
||||
|
} |
||||
|
|
||||
|
public Awaiter GetAwaiter() |
||||
|
{ |
||||
|
var customAwaiter = _getAwaiterMethod(_customAwaitable); |
||||
|
return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, |
||||
|
_unsafeOnCompletedMethod); |
||||
|
} |
||||
|
|
||||
|
public readonly struct Awaiter : ICriticalNotifyCompletion |
||||
{ |
{ |
||||
private readonly object _customAwaitable; |
private readonly object _customAwaiter; |
||||
private readonly Func<object, object> _getAwaiterMethod; |
|
||||
private readonly Func<object, bool> _isCompletedMethod; |
private readonly Func<object, bool> _isCompletedMethod; |
||||
private readonly Func<object, object> _getResultMethod; |
private readonly Func<object, object> _getResultMethod; |
||||
private readonly Action<object, Action> _onCompletedMethod; |
private readonly Action<object, Action> _onCompletedMethod; |
||||
private readonly Action<object, Action> _unsafeOnCompletedMethod; |
private readonly Action<object, Action> _unsafeOnCompletedMethod; |
||||
|
|
||||
// Perf note: since we're requiring the customAwaitable to be supplied here as an object,
|
public Awaiter( |
||||
// this will trigger a further allocation if it was a value type (i.e., to box it). We can't
|
object customAwaiter, |
||||
// fix this by making the customAwaitable type generic, because the calling code typically
|
|
||||
// does not know the type of the awaitable/awaiter at compile-time anyway.
|
|
||||
//
|
|
||||
// However, we could fix it by not passing the customAwaitable here at all, and instead
|
|
||||
// passing a func that maps directly from the target object (e.g., controller instance),
|
|
||||
// target method (e.g., action method info), and params array to the custom awaiter in the
|
|
||||
// GetAwaiter() method below. In effect, by delaying the actual method call until the
|
|
||||
// upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance.
|
|
||||
// This optimization is not currently implemented because:
|
|
||||
// [1] It would make no difference when the awaitable was an object type, which is
|
|
||||
// by far the most common scenario (e.g., System.Task<T>).
|
|
||||
// [2] It would be complex - we'd need some kind of object pool to track all the parameter
|
|
||||
// arrays until we needed to use them in GetAwaiter().
|
|
||||
// We can reconsider this in the future if there's a need to optimize for ValueTask<T>
|
|
||||
// or other value-typed awaitables.
|
|
||||
|
|
||||
public ObjectMethodExecutorAwaitable( |
|
||||
object customAwaitable, |
|
||||
Func<object, object> getAwaiterMethod, |
|
||||
Func<object, bool> isCompletedMethod, |
Func<object, bool> isCompletedMethod, |
||||
Func<object, object> getResultMethod, |
Func<object, object> getResultMethod, |
||||
Action<object, Action> onCompletedMethod, |
Action<object, Action> onCompletedMethod, |
||||
Action<object, Action> unsafeOnCompletedMethod) |
Action<object, Action> unsafeOnCompletedMethod) |
||||
{ |
{ |
||||
_customAwaitable = customAwaitable; |
_customAwaiter = customAwaiter; |
||||
_getAwaiterMethod = getAwaiterMethod; |
|
||||
_isCompletedMethod = isCompletedMethod; |
_isCompletedMethod = isCompletedMethod; |
||||
_getResultMethod = getResultMethod; |
_getResultMethod = getResultMethod; |
||||
_onCompletedMethod = onCompletedMethod; |
_onCompletedMethod = onCompletedMethod; |
||||
_unsafeOnCompletedMethod = unsafeOnCompletedMethod; |
_unsafeOnCompletedMethod = unsafeOnCompletedMethod; |
||||
} |
} |
||||
|
|
||||
public Awaiter GetAwaiter() |
public bool IsCompleted => _isCompletedMethod(_customAwaiter); |
||||
|
|
||||
|
public object GetResult() |
||||
{ |
{ |
||||
var customAwaiter = _getAwaiterMethod(_customAwaitable); |
return _getResultMethod(_customAwaiter); |
||||
return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, |
|
||||
_unsafeOnCompletedMethod); |
|
||||
} |
} |
||||
|
|
||||
public struct Awaiter : ICriticalNotifyCompletion |
public void OnCompleted(Action continuation) |
||||
{ |
{ |
||||
private readonly object _customAwaiter; |
_onCompletedMethod(_customAwaiter, continuation); |
||||
private readonly Func<object, bool> _isCompletedMethod; |
} |
||||
private readonly Func<object, object> _getResultMethod; |
|
||||
private readonly Action<object, Action> _onCompletedMethod; |
|
||||
private readonly Action<object, Action> _unsafeOnCompletedMethod; |
|
||||
|
|
||||
public Awaiter( |
|
||||
object customAwaiter, |
|
||||
Func<object, bool> isCompletedMethod, |
|
||||
Func<object, object> getResultMethod, |
|
||||
Action<object, Action> onCompletedMethod, |
|
||||
Action<object, Action> unsafeOnCompletedMethod) |
|
||||
{ |
|
||||
_customAwaiter = customAwaiter; |
|
||||
_isCompletedMethod = isCompletedMethod; |
|
||||
_getResultMethod = getResultMethod; |
|
||||
_onCompletedMethod = onCompletedMethod; |
|
||||
_unsafeOnCompletedMethod = unsafeOnCompletedMethod; |
|
||||
} |
|
||||
|
|
||||
public bool IsCompleted => _isCompletedMethod(_customAwaiter); |
|
||||
|
|
||||
public object GetResult() |
|
||||
{ |
|
||||
return _getResultMethod(_customAwaiter); |
|
||||
} |
|
||||
|
|
||||
public void OnCompleted(Action continuation) |
|
||||
{ |
|
||||
_onCompletedMethod(_customAwaiter, continuation); |
|
||||
} |
|
||||
|
|
||||
public void UnsafeOnCompleted(Action continuation) |
public void UnsafeOnCompleted(Action continuation) |
||||
{ |
{ |
||||
// If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted.
|
// If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted.
|
||||
// If not, fall back on using its OnCompleted.
|
// If not, fall back on using its OnCompleted.
|
||||
//
|
//
|
||||
// Why this is safe:
|
// Why this is safe:
|
||||
// - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it
|
// - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it
|
||||
// needs the execution context to be preserved (which it signals by calling OnCompleted), or
|
// needs the execution context to be preserved (which it signals by calling OnCompleted), or
|
||||
// that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not*
|
// that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not*
|
||||
// to preserve and restore the context, so we prefer that where possible.
|
// to preserve and restore the context, so we prefer that where possible.
|
||||
// - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted,
|
// - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted,
|
||||
// there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen
|
// there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen
|
||||
// if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to
|
// if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to
|
||||
// pass the call on to the underlying awaitable's OnCompleted method.
|
// pass the call on to the underlying awaitable's OnCompleted method.
|
||||
|
|
||||
var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod; |
var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod; |
||||
underlyingMethodToUse(_customAwaiter, continuation); |
underlyingMethodToUse(_customAwaiter, continuation); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
Loading…
Reference in new issue