using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.ClearScript.V8;
namespace ClearScriptPlaygroud;
public class SingleThreadSynchronizationContext : SynchronizationContext
BlockingCollection<(SendOrPostCallback Callback, object? State)> _continuations = new();
ManualResetEvent _isDone = new ManualResetEvent(false);
public override void Post(SendOrPostCallback d, object? state) => _continuations.Add((d, state));
public static void RunSync(Func<Task> task)
var syncContext = new SingleThreadSynchronizationContext();
var eventLoop = new Thread(syncContext.Run);
using var mre = new ManualResetEvent(false);
syncContext._continuations.Add((_ => { task().ContinueWith(_ => { mre.Set(); }); }, null));
syncContext._continuations.CompleteAdding();
syncContext._isDone.WaitOne();
SetSynchronizationContext(this);
foreach (var (callback, state) in _continuations.GetConsumingEnumerable())
private static int _privilege;
public static void AcquirePrivilege()
if (_privilege == 0) _privilege = Environment.CurrentManagedThreadId;
throw new InvalidOperationException("Not allowed to acquire privilege");
public static void ReleasePrivilege()
if (Environment.CurrentManagedThreadId == _privilege) _privilege = 0;
throw new InvalidOperationException("Not allowed to release privilege");
public static Task PerformWork()
var task = new Task(() =>
EchoPrivilege($"Performing background work...");
public static void EchoPrivilege(string message)
var privilegeStr = _privilege == Environment.CurrentManagedThreadId ? "has-privilege" : "no-privilege ";
Console.WriteLine($"{privilegeStr} | {message}");
static async Task RunScript()
using var runtime = new V8Runtime();
using var engine = runtime.CreateScriptEngine(V8ScriptEngineFlags.EnableTaskPromiseConversion);
engine.AddHostType(typeof(Util));
engine.Execute("script.js", @"
async function doAsync() {
Util.EchoPrivilege(`Sync before`);
await Util.PerformWork();
Util.EchoPrivilege(`Sync after`);
await (Task)engine.Invoke("doAsync");
static async Task RunManaged()
Util.EchoPrivilege("Sync before");
await Util.PerformWork();
Util.EchoPrivilege("Sync after");
public static void Main()
Console.WriteLine("Managed result:");
SingleThreadSynchronizationContext.RunSync(RunManaged);
Console.WriteLine("\nScript result:");
SingleThreadSynchronizationContext.RunSync(RunScript);