using System.Collections.Concurrent;
namespace AyCode.Services.SignalRs;
///
/// Dedicated off-pool executor for the AsyncSegment streaming deserialization (see
/// SIGNALR_BINARY_PROTOCOL_ISSUES.md#accore-sbp-i-t3w9).
///
/// The streaming deser blocks in AsyncPipeReaderInput.TryAdvanceSegment →
/// ManualResetEventSlim.Wait() between network-delivered chunks. Running that blocking
/// wait on a .NET thread-pool thread (plain Task.Run) starves the pool: the signal that
/// unblocks it (the producer Feed()+Set()) runs on the SignalR receive-continuation,
/// which ALSO needs a pool thread → circular dependency → deadlock under concurrent load + real
/// network latency.
///
/// Design — never-queue, grow-on-demand, reuse-forever: every
/// immediately hands the work to an idle worker (reuse) or spawns a new dedicated background
/// thread (grow). It never queues. Consequences:
///
/// The deser always starts immediately on its own off-pool thread → the [202]
/// GetResult() always waits on a running deser (no queue-coupling).
/// The blocking Wait() never consumes a .NET thread-pool thread → the producer's
/// receive-continuations always get pool threads → no circular dependency, no starvation.
/// Threads are reused (no per-message churn). Thread count is bounded in practice by peak
/// concurrent chunked receives — one in-flight deser per connection (the [202]
/// GetResult drains it before the next message on that connection).
///
///
/// This is TaskCreationOptions.LongRunning's unconditional safety (never-queue,
/// off-pool) plus thread reuse. Workers live forever — no idle-timeout, hence no idle-vs-assign
/// race (the simplest provably-correct variant). An idle-timeout reclaim can be added later if the
/// held-thread memory at peak concurrency becomes a concern; it must use a CAS idle→dead transition.
///
/// Rejected: a bounded-queue executor reintroduces the GetResult-on-queued-deser
/// coupling and drops requests on overload.
///
internal static class AcBinaryDeserExecutor
{
private static readonly ConcurrentStack _idle = new();
// Monotonic id for distinct worker-thread names — eases dump / profiler / debug correlation.
// Incremented only on grow (new thread), never per message.
private static int _threadId;
///
/// Runs on a dedicated off-pool thread (reused idle worker, or a new
/// one) — never queues. Returns a task completing with the work's result.
///
public static Task