private static void Run(Func <Task> func, LateArrivalBehavior lateArrivalBehavior, Action onFirstLateArrival)
        {
            if (func == null)
            {
                throw new ArgumentNullException("func");
            }

            var prevCtx = SynchronizationContext.Current;

            var syncCtx = new SingleThreadSynchronizationContext();

            try
            {
                syncCtx.Thread             = Thread.CurrentThread;
                syncCtx.onFirstLateArrival = onFirstLateArrival;
                //Console.WriteLine("Running " + syncCtx.GetHashCode() + " on thread " + Environment.CurrentManagedThreadId);
                syncCtx.lateArrivalBehavior = lateArrivalBehavior;
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                var t = func();
                if (t == null)
                {
                    throw new InvalidOperationException("No task provided.");
                }
                t.ContinueWith(delegate
                {
                    try
                    {
                        var q = syncCtx.m_queue;
                        if (q != null)
                        {
                            q.CompleteAdding();
                        }
                    }
                    catch
                    {
                        // TODO should we do something here? can it happen?
                        //Console.WriteLine("Perdindirindina");
                    }
                    syncCtx.completed = true;
                }, TaskScheduler.Default);
                syncCtx.RunOnCurrentThread();
                if (!syncCtx.aborted)
                {
                    t.GetAwaiter().GetResult();
                }
                if (syncCtx.aborted)
                {
                    throw new OperationCanceledException("The SingleThreadSynchronizationContext was aborted.");
                }
            }
            finally
            {
                SynchronizationContext.SetSynchronizationContext(prevCtx);

                var q = syncCtx.m_queue;
                if (q != null)
                {
                    q.Dispose();
                }
                syncCtx.m_queue = null;

                var d = syncCtx.drainingCompletion;
                if (d != null)
                {
                    d.TrySetResult(true);
                }
            }
        }
        private bool EnqueueJob(Job job, bool allowForward)
        {
            if (!completed)
            {
                try
                {
                    var q = m_queue;
                    q.Add(job);
                    return(true);
                }
                catch (InvalidOperationException)
                {
                }
            }

            if (onFirstLateArrival != null)
            {
                if (Interlocked.Increment(ref firstLateArrivalCallbackExecuted) == 1)
                {
                    onFirstLateArrival();
                    onFirstLateArrival = null;
                }
            }

            if (allowForward && onFailureForwardTo != null)
            {
                return(onFailureForwardTo.EnqueueJob(job, false));
            }

            if (lateArrivalBehavior == LateArrivalBehavior.Throw)
            {
#if DESKTOP
                var frames = GetStackTrace(false).GetFrames();
                foreach (var frame in frames)
                {
                    var m = frame.GetMethod();
                    if (m != null)
                    {
                        if (m.DeclaringType == typeof(SingleThreadSynchronizationContext))
                        {
                            continue;
                        }
                        if (m.DeclaringType.GetTypeInfo().Assembly == typeof(Task).GetTypeInfo().Assembly&& m.DeclaringType.Name == "SynchronizationContextAwaitTaskContinuation")
                        {
                            return(false); // We have to pretend nothing happened and not to throw the exception
                        }
                        break;
                    }
                }
#else
                var frames = new Exception().StackTrace.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var frame in frames)
                {
                    if (frame.Contains("SingleThreadSynchronizationContext"))
                    {
                        continue;
                    }
                    if (frame.Contains("SynchronizationContextAwaitTaskContinuation"))
                    {
                        return(false);
                    }
                    break;
                }
#endif
                throw new InvalidOperationException("The SingleThreadSynchronizationContext has completed and is no longer accepting continuations or callbacks.");
            }
            if (lateArrivalBehavior == LateArrivalBehavior.SpawnNewThread)
            {
                lock (this)
                {
                    if (lateArrivalSyncCtx == null || !lateArrivalSyncCtx.EnqueueJob(job, false))
                    {
                        var    readyTcs = new TaskCompletionSource <bool>();
                        var    firstContinuationExecutedTcs = new TaskCompletionSource <bool>();
                        Action deleg = () =>
                        {
                            SingleThreadSynchronizationContext.Run(async() =>
                            {
                                var ctx = (SingleThreadSynchronizationContext)SynchronizationContext.Current;
                                ctx.onFailureForwardTo = this;
                                lateArrivalSyncCtx     = ctx;
                                readyTcs.SetResult(true);
                                drainingCompletion.Task.Wait();
                                await firstContinuationExecutedTcs.Task;
                                await Task.Delay(10000);
                            }, LateArrivalBehavior.Suppress);
                        };
#if DESKTOP
                        new Thread(() => deleg())
                        {
                            Name = "Respawned thread for SingleThreadSynchronizationContext"
                        }.Start();
#else
                        Task.Run(deleg);
#endif
                        readyTcs.Task.Wait();
                        if (!lateArrivalSyncCtx.EnqueueJob(job, false))
                        {
                            throw new Exception();
                        }
                        firstContinuationExecutedTcs.SetResult(true);
                    }
                    return(true);
                }
            }


            return(false);
        }