Skip to content

Instantly share code, notes, and snippets.

@ruccho
Last active April 6, 2026 06:22
Show Gist options
  • Select an option

  • Save ruccho/253fc9f52a6ede8c58c51e9c025d621a to your computer and use it in GitHub Desktop.

Select an option

Save ruccho/253fc9f52a6ede8c58c51e9c025d621a to your computer and use it in GitHub Desktop.
An utility to get Action<T> which is automatically pooled after called once.
using System;
using System.Threading;
using System.Collections.Concurrent;
namespace PooledActions
{
internal readonly struct PooledActionOnce<TContext> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext> Get(
Action<TContext, Action> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = () =>
{
_callback!(_context, Wrapper);
Release();
};
}
public Action Wrapper { get; }
public void Set(Action<TContext, Action> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1> Get(
Action<TContext, Action<T1>, T1> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1>, T1> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1) =>
{
_callback!(_context, Wrapper, arg1);
Release();
};
}
public Action<T1> Wrapper { get; }
public void Set(Action<TContext, Action<T1>, T1> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2> Get(
Action<TContext, Action<T1, T2>, T1, T2> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2>, T1, T2> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2) =>
{
_callback!(_context, Wrapper, arg1, arg2);
Release();
};
}
public Action<T1, T2> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2>, T1, T2> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3> Get(
Action<TContext, Action<T1, T2, T3>, T1, T2, T3> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3>, T1, T2, T3> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3);
Release();
};
}
public Action<T1, T2, T3> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3>, T1, T2, T3> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4> Get(
Action<TContext, Action<T1, T2, T3, T4>, T1, T2, T3, T4> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4>, T1, T2, T3, T4> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4);
Release();
};
}
public Action<T1, T2, T3, T4> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4>, T1, T2, T3, T4> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5> Get(
Action<TContext, Action<T1, T2, T3, T4, T5>, T1, T2, T3, T4, T5> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5>, T1, T2, T3, T4, T5> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5);
Release();
};
}
public Action<T1, T2, T3, T4, T5> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5>, T1, T2, T3, T4, T5> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6>, T1, T2, T3, T4, T5, T6> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6>, T1, T2, T3, T4, T5, T6> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6>, T1, T2, T3, T4, T5, T6> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7>, T1, T2, T3, T4, T5, T6, T7> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7>, T1, T2, T3, T4, T5, T6, T7> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7>, T1, T2, T3, T4, T5, T6, T7> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7, T8> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8>, T1, T2, T3, T4, T5, T6, T7, T8> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8>, T1, T2, T3, T4, T5, T6, T7, T8> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7, T8> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8>, T1, T2, T3, T4, T5, T6, T7, T8> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9>, T1, T2, T3, T4, T5, T6, T7, T8, T9> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9>, T1, T2, T3, T4, T5, T6, T7, T8, T9> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9>, T1, T2, T3, T4, T5, T6, T7, T8, T9> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
internal readonly struct PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Get(
Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14) =>
{
_callback!(_context, Wrapper, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
Release();
};
}
public Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Wrapper { get; }
public void Set(Action<TContext, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
}
<#@ template language="C#" #>
<#@ import namespace="System.Linq" #>
using System;
using System.Threading;
using System.Collections.Concurrent;
namespace PooledActions
{
<#
const int MaxArgs = 14;
for (var i = 0; i <= MaxArgs; i++)
{
var typeParams = Enumerable.Range(1, i).Select(n => $"T{n}");
var typeParamListWithBrackets = typeParams.Any() ? $"<{string.Join(", ", typeParams)}>" : string.Empty;
var typeParamListWithPrecedingComma = typeParams.Any() ? $", {string.Join(", ", typeParams)}" : string.Empty;
var lambdaParams = Enumerable.Range(1, i).Select(n => $"arg{n}").ToArray();
var lambdaParamList = lambdaParams.Any() ? string.Join(", ", lambdaParams) : string.Empty;
var lambdaParamListWithPrecedingComma = lambdaParams.Any() ? $", {lambdaParamList}" : string.Empty;
#>
internal readonly struct PooledActionOnce<TContext<#= typeParamListWithPrecedingComma #>> : IDisposable
{
private static int _poolCapacity = 32;
public static int PoolCapacity
{
get => _poolCapacity;
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Pool capacity must be non-negative.");
_poolCapacity = value;
while (Volatile.Read(ref _poolCount) > _poolCapacity)
{
if (!Pool.TryDequeue(out _))
{
break;
}
Interlocked.Decrement(ref _poolCount);
}
}
}
private static int _poolCount = 0;
private static readonly ConcurrentQueue<Core> Pool = new();
private readonly ushort _version;
private readonly Core _core;
public Action<#= typeParamListWithBrackets #> Wrapper
{
get
{
if (_core._version != _version) throw new InvalidOperationException();
return _core.Wrapper;
}
}
public static PooledActionOnce<TContext<#= typeParamListWithPrecedingComma #>> Get(
Action<TContext, Action<#= typeParamListWithBrackets #><#= typeParamListWithPrecedingComma #>> callback, TContext context)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (Pool.TryDequeue(out var pooled))
{
Interlocked.Decrement(ref _poolCount);
}
else
{
pooled = new Core();
}
pooled.Set(callback, context);
return new PooledActionOnce<TContext<#= typeParamListWithPrecedingComma #>>(pooled);
}
public void Dispose()
{
if (_core._version != _version) return;
_core.Release();
}
private PooledActionOnce(Core core)
{
_version = core._version;
_core = core;
}
private class Core
{
private Action<TContext, Action<#= typeParamListWithBrackets #><#= typeParamListWithPrecedingComma #>> _callback;
private TContext _context;
public ushort _version;
public Core()
{
Wrapper = (<#= lambdaParamList #>) =>
{
_callback!(_context, Wrapper<#= lambdaParamListWithPrecedingComma #>);
Release();
};
}
public Action<#= typeParamListWithBrackets #> Wrapper { get; }
public void Set(Action<TContext, Action<#= typeParamListWithBrackets #><#= typeParamListWithPrecedingComma #>> callback, TContext context)
{
_callback = callback;
_context = context;
}
public void Release()
{
_callback = null;
_context = default;
if (++_version == ushort.MaxValue) return;
var reservedCount = Interlocked.Increment(ref _poolCount);
if (reservedCount > PoolCapacity)
{
Interlocked.Decrement(ref _poolCount);
return;
}
Pool.Enqueue(this);
}
}
}
<#
}
#>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment