// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable enable using System; using System.Runtime.CompilerServices; namespace Microsoft.CodeAnalysis.PooledObjects { /// <summary> /// Provides pooled delegate instances to help avoid closure allocations for delegates that require a state argument /// with APIs that do not provide appropriate overloads with state arguments. /// </summary> internal static class PooledDelegates { private static class DefaultDelegatePool<T> where T : class, new() { public static readonly ObjectPool<T> Instance = new(() => new T(), 20); } private static Releaser GetPooledDelegate<TPooled, TArg, TUnboundDelegate, TBoundDelegate>(TUnboundDelegate unboundDelegate, TArg argument, out TBoundDelegate boundDelegate) where TPooled : AbstractDelegateWithBoundArgument<TPooled, TArg, TUnboundDelegate, TBoundDelegate>, new() where TUnboundDelegate : Delegate where TBoundDelegate : Delegate { var obj = DefaultDelegatePool<TPooled>.Instance.Allocate(); obj.Initialize(unboundDelegate, argument); boundDelegate = obj.BoundDelegate; return new Releaser(obj); } /// <summary> /// Gets an <see cref="Action"/> delegate, which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>. The resulting <paramref name="boundAction"/> may be called any number of times /// until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a callback action that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithActionCallback(() => this.DoSomething(x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// callback action:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledAction(arg => arg.self.DoSomething(arg.x), (self: this, x), out Action action); /// RunWithActionCallback(action); /// </code> /// </example> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundAction"/>.</typeparam> /// <param name="unboundAction">The unbound action delegate.</param> /// <param name="argument">The argument to pass to the unbound action delegate.</param> /// <param name="boundAction">A delegate which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledAction<TArg>(Action<TArg> unboundAction, TArg argument, out Action boundAction) => GetPooledDelegate<ActionWithBoundArgument<TArg>, TArg, Action<TArg>, Action>(unboundAction, argument, out boundAction); /// <summary> /// Gets an <see cref="Action{T}"/> delegate, which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>. The resulting <paramref name="boundAction"/> may be called any number of times /// until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a callback action that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithActionCallback(a => this.DoSomething(a, x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// callback action:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledAction((a, arg) => arg.self.DoSomething(a, arg.x), (self: this, x), out Action<int> action); /// RunWithActionCallback(action); /// </code> /// </example> /// <typeparam name="T1">The type of the first parameter of the bound action.</typeparam> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundAction"/>.</typeparam> /// <param name="unboundAction">The unbound action delegate.</param> /// <param name="argument">The argument to pass to the unbound action delegate.</param> /// <param name="boundAction">A delegate which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledAction<T1, TArg>(Action<T1, TArg> unboundAction, TArg argument, out Action<T1> boundAction) => GetPooledDelegate<ActionWithBoundArgument<T1, TArg>, TArg, Action<T1, TArg>, Action<T1>>(unboundAction, argument, out boundAction); /// <summary> /// Gets an <see cref="Action{T1, T2}"/> delegate, which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>. The resulting <paramref name="boundAction"/> may be called any number of times /// until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a callback action that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithActionCallback((a, b) => this.DoSomething(a, b, x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// callback action:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledAction((a, b, arg) => arg.self.DoSomething(a, b, arg.x), (self: this, x), out Action<int, int> action); /// RunWithActionCallback(action); /// </code> /// </example> /// <typeparam name="T1">The type of the first parameter of the bound action.</typeparam> /// <typeparam name="T2">The type of the second parameter of the bound action.</typeparam> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundAction"/>.</typeparam> /// <param name="unboundAction">The unbound action delegate.</param> /// <param name="argument">The argument to pass to the unbound action delegate.</param> /// <param name="boundAction">A delegate which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledAction<T1, T2, TArg>(Action<T1, T2, TArg> unboundAction, TArg argument, out Action<T1, T2> boundAction) => GetPooledDelegate<ActionWithBoundArgument<T1, T2, TArg>, TArg, Action<T1, T2, TArg>, Action<T1, T2>>(unboundAction, argument, out boundAction); /// <summary> /// Gets an <see cref="Action{T1, T2, T3}"/> delegate, which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>. The resulting <paramref name="boundAction"/> may be called any number of times /// until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a callback action that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithActionCallback((a, b, c) => this.DoSomething(a, b, c, x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// callback action:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledAction((a, b, c, arg) => arg.self.DoSomething(a, b, c, arg.x), (self: this, x), out Action<int, int, int> action); /// RunWithActionCallback(action); /// </code> /// </example> /// <typeparam name="T1">The type of the first parameter of the bound action.</typeparam> /// <typeparam name="T2">The type of the second parameter of the bound action.</typeparam> /// <typeparam name="T3">The type of the third parameter of the bound action.</typeparam> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundAction"/>.</typeparam> /// <param name="unboundAction">The unbound action delegate.</param> /// <param name="argument">The argument to pass to the unbound action delegate.</param> /// <param name="boundAction">A delegate which calls <paramref name="unboundAction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledAction<T1, T2, T3, TArg>(Action<T1, T2, T3, TArg> unboundAction, TArg argument, out Action<T1, T2, T3> boundAction) => GetPooledDelegate<ActionWithBoundArgument<T1, T2, T3, TArg>, TArg, Action<T1, T2, T3, TArg>, Action<T1, T2, T3>>(unboundAction, argument, out boundAction); /// <summary> /// Gets a <see cref="Func{TResult}"/> delegate, which calls <paramref name="unboundFunction"/> with the /// specified <paramref name="argument"/>. The resulting <paramref name="boundFunction"/> may be called any /// number of times until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a predicate that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithPredicate(() => this.IsSomething(x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// predicate:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledFunction(arg => arg.self.IsSomething(arg.x), (self: this, x), out Func<bool> predicate); /// RunWithPredicate(predicate); /// </code> /// </example> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundFunction"/>.</typeparam> /// <typeparam name="TResult">The type of the return value of the function.</typeparam> /// <param name="unboundFunction">The unbound function delegate.</param> /// <param name="argument">The argument to pass to the unbound function delegate.</param> /// <param name="boundFunction">A delegate which calls <paramref name="unboundFunction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledFunction<TArg, TResult>(Func<TArg, TResult> unboundFunction, TArg argument, out Func<TResult> boundFunction) => GetPooledDelegate<FuncWithBoundArgument<TArg, TResult>, TArg, Func<TArg, TResult>, Func<TResult>>(unboundFunction, argument, out boundFunction); /// <summary> /// Equivalent to <see cref="GetPooledFunction{TArg, TResult}(Func{TArg, TResult}, TArg, out Func{TResult})"/>, /// except typed such that it can be used to create a pooled <see cref="ConditionalWeakTable{TKey, /// TValue}.CreateValueCallback"/>. /// </summary> public static Releaser GetPooledCreateValueCallback<TKey, TArg, TValue>( Func<TKey, TArg, TValue> unboundFunction, TArg argument, out ConditionalWeakTable<TKey, TValue>.CreateValueCallback boundFunction) where TKey : class where TValue : class { return GetPooledDelegate<CreateValueCallbackWithBoundArgument<TKey, TArg, TValue>, TArg, Func<TKey, TArg, TValue>, ConditionalWeakTable<TKey, TValue>.CreateValueCallback>(unboundFunction, argument, out boundFunction); } /// <summary> /// Gets a <see cref="Func{T, TResult}"/> delegate, which calls <paramref name="unboundFunction"/> with the /// specified <paramref name="argument"/>. The resulting <paramref name="boundFunction"/> may be called any /// number of times until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a predicate that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithPredicate(a => this.IsSomething(a, x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// predicate:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledFunction((a, arg) => arg.self.IsSomething(a, arg.x), (self: this, x), out Func<int, bool> predicate); /// RunWithPredicate(predicate); /// </code> /// </example> /// <typeparam name="T1">The type of the first parameter of the bound function.</typeparam> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundFunction"/>.</typeparam> /// <typeparam name="TResult">The type of the return value of the function.</typeparam> /// <param name="unboundFunction">The unbound function delegate.</param> /// <param name="argument">The argument to pass to the unbound function delegate.</param> /// <param name="boundFunction">A delegate which calls <paramref name="unboundFunction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledFunction<T1, TArg, TResult>(Func<T1, TArg, TResult> unboundFunction, TArg argument, out Func<T1, TResult> boundFunction) => GetPooledDelegate<FuncWithBoundArgument<T1, TArg, TResult>, TArg, Func<T1, TArg, TResult>, Func<T1, TResult>>(unboundFunction, argument, out boundFunction); /// <summary> /// Gets a <see cref="Func{T1, T2, TResult}"/> delegate, which calls <paramref name="unboundFunction"/> with the /// specified <paramref name="argument"/>. The resulting <paramref name="boundFunction"/> may be called any /// number of times until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a predicate that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithPredicate((a, b) => this.IsSomething(a, b, x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// predicate:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledFunction((a, b, arg) => arg.self.IsSomething(a, b, arg.x), (self: this, x), out Func<int, int, bool> predicate); /// RunWithPredicate(predicate); /// </code> /// </example> /// <typeparam name="T1">The type of the first parameter of the bound function.</typeparam> /// <typeparam name="T2">The type of the second parameter of the bound function.</typeparam> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundFunction"/>.</typeparam> /// <typeparam name="TResult">The type of the return value of the function.</typeparam> /// <param name="unboundFunction">The unbound function delegate.</param> /// <param name="argument">The argument to pass to the unbound function delegate.</param> /// <param name="boundFunction">A delegate which calls <paramref name="unboundFunction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledFunction<T1, T2, TArg, TResult>(Func<T1, T2, TArg, TResult> unboundFunction, TArg argument, out Func<T1, T2, TResult> boundFunction) => GetPooledDelegate<FuncWithBoundArgument<T1, T2, TArg, TResult>, TArg, Func<T1, T2, TArg, TResult>, Func<T1, T2, TResult>>(unboundFunction, argument, out boundFunction); /// <summary> /// Gets a <see cref="Func{T1, T2, T3, TResult}"/> delegate, which calls <paramref name="unboundFunction"/> with the /// specified <paramref name="argument"/>. The resulting <paramref name="boundFunction"/> may be called any /// number of times until the returned <see cref="Releaser"/> is disposed. /// </summary> /// <example> /// <para>The following example shows the use of a capturing delegate for a predicate that requires an /// argument:</para> /// /// <code> /// int x = 3; /// RunWithPredicate((a, b, c) => this.IsSomething(a, b, c, x)); /// </code> /// /// <para>The following example shows the use of a pooled delegate to avoid capturing allocations for the same /// predicate:</para> /// /// <code> /// int x = 3; /// using var _ = GetPooledFunction((a, b, c, arg) => arg.self.IsSomething(a, b, c, arg.x), (self: this, x), out Func<int, int, int, bool> predicate); /// RunWithPredicate(predicate); /// </code> /// </example> /// <typeparam name="T1">The type of the first parameter of the bound function.</typeparam> /// <typeparam name="T2">The type of the second parameter of the bound function.</typeparam> /// <typeparam name="T3">The type of the third parameter of the bound function.</typeparam> /// <typeparam name="TArg">The type of argument to pass to <paramref name="unboundFunction"/>.</typeparam> /// <typeparam name="TResult">The type of the return value of the function.</typeparam> /// <param name="unboundFunction">The unbound function delegate.</param> /// <param name="argument">The argument to pass to the unbound function delegate.</param> /// <param name="boundFunction">A delegate which calls <paramref name="unboundFunction"/> with the specified /// <paramref name="argument"/>.</param> /// <returns>A disposable <see cref="Releaser"/> which returns the object to the delegate pool.</returns> public static Releaser GetPooledFunction<T1, T2, T3, TArg, TResult>(Func<T1, T2, T3, TArg, TResult> unboundFunction, TArg argument, out Func<T1, T2, T3, TResult> boundFunction) => GetPooledDelegate<FuncWithBoundArgument<T1, T2, T3, TArg, TResult>, TArg, Func<T1, T2, T3, TArg, TResult>, Func<T1, T2, T3, TResult>>(unboundFunction, argument, out boundFunction); /// <summary> /// A releaser for a pooled delegate. /// </summary> /// <remarks> /// <para>This type is intended for use as the resource of a <c>using</c> statement. When used in this manner, /// <see cref="Dispose"/> should not be called explicitly.</para> /// /// <para>If used without a <c>using</c> statement, calling <see cref="Dispose"/> is optional. If the call is /// omitted, the object will not be returned to the pool. The behavior of this type if <see cref="Dispose"/> is /// called multiple times is undefined.</para> /// </remarks> [NonCopyable] public readonly struct Releaser : IDisposable { private readonly Poolable _pooledObject; internal Releaser(Poolable pooledObject) { _pooledObject = pooledObject; } public void Dispose() => _pooledObject.ClearAndFree(); } internal abstract class Poolable { public abstract void ClearAndFree(); } private abstract class AbstractDelegateWithBoundArgument<TSelf, TArg, TUnboundDelegate, TBoundDelegate> : Poolable where TSelf : AbstractDelegateWithBoundArgument<TSelf, TArg, TUnboundDelegate, TBoundDelegate>, new() where TUnboundDelegate : Delegate where TBoundDelegate : Delegate { protected AbstractDelegateWithBoundArgument() { BoundDelegate = Bind(); UnboundDelegate = null!; Argument = default!; } public TBoundDelegate BoundDelegate { get; } public TUnboundDelegate UnboundDelegate { get; private set; } public TArg Argument { get; private set; } public void Initialize(TUnboundDelegate unboundDelegate, TArg argument) { UnboundDelegate = unboundDelegate; Argument = argument; } public sealed override void ClearAndFree() { Argument = default!; UnboundDelegate = null!; DefaultDelegatePool<TSelf>.Instance.Free((TSelf)this); } protected abstract TBoundDelegate Bind(); } private sealed class ActionWithBoundArgument<TArg> : AbstractDelegateWithBoundArgument<ActionWithBoundArgument<TArg>, TArg, Action<TArg>, Action> { protected override Action Bind() => () => UnboundDelegate(Argument); } private sealed class ActionWithBoundArgument<T1, TArg> : AbstractDelegateWithBoundArgument<ActionWithBoundArgument<T1, TArg>, TArg, Action<T1, TArg>, Action<T1>> { protected override Action<T1> Bind() => arg1 => UnboundDelegate(arg1, Argument); } private sealed class ActionWithBoundArgument<T1, T2, TArg> : AbstractDelegateWithBoundArgument<ActionWithBoundArgument<T1, T2, TArg>, TArg, Action<T1, T2, TArg>, Action<T1, T2>> { protected override Action<T1, T2> Bind() => (arg1, arg2) => UnboundDelegate(arg1, arg2, Argument); } private sealed class ActionWithBoundArgument<T1, T2, T3, TArg> : AbstractDelegateWithBoundArgument<ActionWithBoundArgument<T1, T2, T3, TArg>, TArg, Action<T1, T2, T3, TArg>, Action<T1, T2, T3>> { protected override Action<T1, T2, T3> Bind() => (arg1, arg2, arg3) => UnboundDelegate(arg1, arg2, arg3, Argument); } private sealed class FuncWithBoundArgument<TArg, TResult> : AbstractDelegateWithBoundArgument<FuncWithBoundArgument<TArg, TResult>, TArg, Func<TArg, TResult>, Func<TResult>> { protected override Func<TResult> Bind() => () => UnboundDelegate(Argument); } private sealed class CreateValueCallbackWithBoundArgument<TKey, TArg, TValue> : AbstractDelegateWithBoundArgument< CreateValueCallbackWithBoundArgument<TKey, TArg, TValue>, TArg, Func<TKey, TArg, TValue>, ConditionalWeakTable<TKey, TValue>.CreateValueCallback> where TKey : class where TValue : class { protected override ConditionalWeakTable<TKey, TValue>.CreateValueCallback Bind() => key => UnboundDelegate(key, Argument); } private sealed class FuncWithBoundArgument<T1, TArg, TResult> : AbstractDelegateWithBoundArgument<FuncWithBoundArgument<T1, TArg, TResult>, TArg, Func<T1, TArg, TResult>, Func<T1, TResult>> { protected override Func<T1, TResult> Bind() => arg1 => UnboundDelegate(arg1, Argument); } private sealed class FuncWithBoundArgument<T1, T2, TArg, TResult> : AbstractDelegateWithBoundArgument<FuncWithBoundArgument<T1, T2, TArg, TResult>, TArg, Func<T1, T2, TArg, TResult>, Func<T1, T2, TResult>> { protected override Func<T1, T2, TResult> Bind() => (arg1, arg2) => UnboundDelegate(arg1, arg2, Argument); } private sealed class FuncWithBoundArgument<T1, T2, T3, TArg, TResult> : AbstractDelegateWithBoundArgument<FuncWithBoundArgument<T1, T2, T3, TArg, TResult>, TArg, Func<T1, T2, T3, TArg, TResult>, Func<T1, T2, T3, TResult>> { protected override Func<T1, T2, T3, TResult> Bind() => (arg1, arg2, arg3) => UnboundDelegate(arg1, arg2, arg3, Argument); } [AttributeUsage(AttributeTargets.Struct)] private sealed class NonCopyableAttribute : Attribute { } } } |