Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
111 changes: 0 additions & 111 deletions src/Castle.Core.AsyncInterceptor/AsyncDeterminationInterceptor.cs

This file was deleted.

172 changes: 12 additions & 160 deletions src/Castle.Core.AsyncInterceptor/AsyncInterceptorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,183 +4,35 @@
namespace Castle.DynamicProxy
{
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading.Tasks;

/// <summary>
/// A base type for an <see cref="IAsyncInterceptor"/> to provided a simplified solution of method
/// <see cref="IInvocation"/> by enforcing only two types of interception, both asynchronous.
/// </summary>
public abstract class AsyncInterceptorBase : IAsyncInterceptor
{
#if !NETSTANDARD2_0
/// <summary>
/// A completed <see cref="Task"/>.
/// </summary>
private static readonly Task CompletedTask = Task.FromResult(0);
#endif

private static readonly MethodInfo InterceptSynchronousMethodInfo =
typeof(AsyncInterceptorBase)
.GetMethod(nameof(InterceptSynchronousResult), BindingFlags.Static | BindingFlags.NonPublic);

private static readonly ConcurrentDictionary<Type, GenericSynchronousHandler> GenericSynchronousHandlers =
new ConcurrentDictionary<Type, GenericSynchronousHandler>
{
[typeof(void)] = InterceptSynchronousVoid,
};

private delegate void GenericSynchronousHandler(AsyncInterceptorBase me, IInvocation invocation);

/// <summary>
/// Intercepts a synchronous method <paramref name="invocation"/>.
/// </summary>
/// <param name="invocation">The method invocation.</param>
void IAsyncInterceptor.InterceptSynchronous(IInvocation invocation)
{
Type returnType = invocation.Method.ReturnType;
GenericSynchronousHandler handler = GenericSynchronousHandlers.GetOrAdd(returnType, CreateHandler);
handler(this, invocation);
}

/// <summary>
/// Intercepts an asynchronous method <paramref name="invocation"/> with return type of <see cref="Task"/>.
/// </summary>
/// <param name="invocation">The method invocation.</param>
void IAsyncInterceptor.InterceptAsynchronous(IInvocation invocation)
{
invocation.ReturnValue = InterceptAsync(invocation, ProceedAsynchronous);
}

/// <summary>
/// Intercepts an asynchronous method <paramref name="invocation"/> with return type of <see cref="Task{T}"/>.
/// </summary>
/// <typeparam name="TResult">The type of the <see cref="Task{T}"/> <see cref="Task{T}.Result"/>.</typeparam>
/// <param name="invocation">The method invocation.</param>
void IAsyncInterceptor.InterceptAsynchronous<TResult>(IInvocation invocation)
{
invocation.ReturnValue = InterceptAsync(invocation, ProceedAsynchronous<TResult>);
}

/// <summary>
/// Override in derived classes to intercept method invocations.
/// </summary>
/// <param name="invocation">The method invocation.</param>
/// <param name="proceed">The function to proceed the <paramref name="invocation"/>.</param>
/// <returns>A <see cref="Task" /> object that represents the asynchronous operation.</returns>
protected abstract Task InterceptAsync(IInvocation invocation, Func<IInvocation, Task> proceed);

/// <summary>
/// Override in derived classes to intercept method invocations.
/// </summary>
/// <typeparam name="TResult">The type of the <see cref="Task{T}"/> <see cref="Task{T}.Result"/>.</typeparam>
/// <param name="invocation">The method invocation.</param>
/// <param name="proceed">The function to proceed the <paramref name="invocation"/>.</param>
/// <returns>A <see cref="Task" /> object that represents the asynchronous operation.</returns>
protected abstract Task<TResult> InterceptAsync<TResult>(
IInvocation invocation,
Func<IInvocation, Task<TResult>> proceed);

private static GenericSynchronousHandler CreateHandler(Type returnType)
{
MethodInfo method = InterceptSynchronousMethodInfo.MakeGenericMethod(returnType);
return (GenericSynchronousHandler)method.CreateDelegate(typeof(GenericSynchronousHandler));
}

private static void InterceptSynchronousVoid(AsyncInterceptorBase me, IInvocation invocation)
/// <inheritdoc cref="IAsyncInterceptor"/>
public virtual void InterceptAction(IActionInvocation invocation)
{
Task task = me.InterceptAsync(invocation, ProceedSynchronous);

// If the intercept task has yet to complete, wait for it.
if (!task.IsCompleted)
{
Task.Run(() => task).Wait();
}

if (task.IsFaulted)
{
throw task.Exception.InnerException;
}
}

private static void InterceptSynchronousResult<TResult>(AsyncInterceptorBase me, IInvocation invocation)
{
Task task = me.InterceptAsync(invocation, ProceedSynchronous<TResult>);

// If the intercept task has yet to complete, wait for it.
if (!task.IsCompleted)
{
Task.Run(() => task).Wait();
}

if (task.IsFaulted)
{
throw task.Exception.InnerException;
}
}

private static Task ProceedSynchronous(IInvocation invocation)
{
try
{
invocation.Proceed();
#if NETSTANDARD2_0
return Task.CompletedTask;
#else
return CompletedTask;
#endif
}
catch (Exception e)
{
#if NETSTANDARD2_0
return Task.FromException(e);
#else
var tcs = new TaskCompletionSource<int>();
tcs.SetException(e);
return tcs.Task;
#endif
}
invocation.Proceed();
}

private static Task<TResult> ProceedSynchronous<TResult>(IInvocation invocation)
/// <inheritdoc cref="IAsyncInterceptor"/>
public virtual TResult InterceptFunction<TResult>(IFunctionInvocation<TResult> invocation)
{
try
{
invocation.Proceed();
return Task.FromResult((TResult)invocation.ReturnValue);
}
catch (Exception e)
{
#if NETSTANDARD2_0
return Task.FromException<TResult>(e);
#else
var tcs = new TaskCompletionSource<TResult>();
tcs.SetException(e);
return tcs.Task;
#endif
}
return invocation.Proceed();
}

private static async Task ProceedAsynchronous(IInvocation invocation)
/// <inheritdoc cref="IAsyncInterceptor"/>
public virtual Task<TResult> InterceptAsyncFunction<TResult>(IAsyncFunctionInvocation<TResult> invocation)
{
invocation.Proceed();

// Get the task to await.
var originalReturnValue = (Task)invocation.ReturnValue;

await originalReturnValue.ConfigureAwait(false);
return invocation.Proceed();
}

private static async Task<TResult> ProceedAsynchronous<TResult>(IInvocation invocation)
/// <inheritdoc cref="IAsyncInterceptor"/>
public virtual Task InterceptAsyncAction(IAsyncActionInvocation invocation)
{
invocation.Proceed();

// Get the task to await.
var originalReturnValue = (Task<TResult>)invocation.ReturnValue;

TResult result = await originalReturnValue.ConfigureAwait(false);
return result;
return invocation.Proceed();
}
}
}
8 changes: 4 additions & 4 deletions src/Castle.Core.AsyncInterceptor/AsyncTimingInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public abstract class AsyncTimingInterceptor : ProcessingAsyncInterceptor<Stopwa
/// </summary>
/// <param name="invocation">The method invocation.</param>
/// <returns>The <see cref="Stopwatch"/> to time the method <paramref name="invocation"/>.</returns>
protected sealed override Stopwatch StartingInvocation(IInvocation invocation)
protected override sealed Stopwatch StartingInvocation(IAsyncInvocation invocation)
{
StartingTiming(invocation);
var stopwatch = new Stopwatch();
Expand All @@ -32,7 +32,7 @@ protected sealed override Stopwatch StartingInvocation(IInvocation invocation)
/// <param name="invocation">The method invocation.</param>
/// <param name="stopwatch">The <see cref="Stopwatch"/> returned by <see cref="StartingInvocation"/> to time
/// the method <paramref name="invocation"/>.</param>
protected sealed override void CompletedInvocation(IInvocation invocation, Stopwatch stopwatch)
protected override sealed void CompletedInvocation(IAsyncInvocation invocation, Stopwatch stopwatch)
{
stopwatch.Stop();
CompletedTiming(invocation, stopwatch);
Expand All @@ -42,14 +42,14 @@ protected sealed override void CompletedInvocation(IInvocation invocation, Stopw
/// Override in derived classes to receive signals prior method <paramref name="invocation"/> timing.
/// </summary>
/// <param name="invocation">The method invocation.</param>
protected abstract void StartingTiming(IInvocation invocation);
protected abstract void StartingTiming(IAsyncInvocation invocation);

/// <summary>
/// Override in derived classes to receive signals after method <paramref name="invocation"/> timing.
/// </summary>
/// <param name="invocation">The method invocation.</param>
/// <param name="stopwatch">A <see cref="Stopwatch"/> used to time the method <paramref name="invocation"/>.
/// </param>
protected abstract void CompletedTiming(IInvocation invocation, Stopwatch stopwatch);
protected abstract void CompletedTiming(IAsyncInvocation invocation, Stopwatch stopwatch);
}
}
23 changes: 23 additions & 0 deletions src/Castle.Core.AsyncInterceptor/IActionInvocation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2016 James Skimming. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

namespace Castle.DynamicProxy
{
using System;

/// <summary>
/// Interface to handle synchronous action
/// </summary>
public interface IActionInvocation : IAsyncInvocation
{
/// <summary>
/// Proceeds the call to the next interceptor in line, and ultimately to the target method.
/// </summary>
/// <remarks>
/// Since interface proxies without a target don't have the target implementation to proceed to,
/// it is important, that the last interceptor does not call this method, otherwise a
/// <see cref = "NotImplementedException" /> will be thrown.
/// </remarks>
void Proceed();
}
}
25 changes: 25 additions & 0 deletions src/Castle.Core.AsyncInterceptor/IAsyncActionInvocation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2016 James Skimming. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

namespace Castle.DynamicProxy
{
using System;
using System.Threading.Tasks;

/// <summary>
/// Interface to handle synchronous function
/// </summary>
public interface IAsyncActionInvocation : IAsyncInvocation
{
/// <summary>
/// Proceeds the call to the next interceptor in line, and ultimately to the target method.
/// </summary>
/// <remarks>
/// Since interface proxies without a target don't have the target implementation to proceed to,
/// it is important, that the last interceptor does not call this method, otherwise a
/// <see cref = "NotImplementedException" /> will be thrown.
/// </remarks>
/// <returns>The asynchronous task</returns>
Task Proceed();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about naming ProceedAsync?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I don't like to have the Async suffix.

But it is important to express intent and since the synchronous version is called Proceed is a good idea to change to ProceedAsync to be extra clear.

}
}
Loading