Skip to content

Instantly share code, notes, and snippets.

@davidfowl
Created October 11, 2022 02:53

Revisions

  1. davidfowl created this gist Oct 11, 2022.
    55 changes: 55 additions & 0 deletions SingleThreadedScheduler.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,55 @@
    using System.Collections.Concurrent;
    using System.Threading.Channels;

    var channel = Channel.CreateUnbounded<int>();

    var syncContext = new SingleThreadedSyncContext();

    syncContext.Post(async _ =>
    {
    await foreach (var item in channel.Reader.ReadAllAsync())
    {
    Console.WriteLine(Environment.CurrentManagedThreadId);
    }
    },
    null);

    for (int i = 0; ; i++)
    {
    await channel.Writer.WriteAsync(i);
    Thread.Sleep(1000);
    }


    public class SingleThreadedSyncContext : SynchronizationContext
    {
    private readonly BlockingCollection<(SendOrPostCallback Callback, object? State)> _queue = new();
    private readonly Thread _thread;

    public SingleThreadedSyncContext()
    {
    _thread = new(RunQueue!);
    _thread.Start();
    }

    public override void Post(SendOrPostCallback d, object? state)
    {
    _queue.Add((d, state));
    }

    private void RunQueue(object state)
    {
    SetSynchronizationContext(this);

    foreach (var (d, s) in _queue.GetConsumingEnumerable())
    {
    d(s);

    if (Current != this)
    {
    // Set the sync context in case the callback changed it
    SetSynchronizationContext(this);
    }
    }
    }
    }