File: System\Linq\Expressions\StackGuard.cs
Web Access
Project: src\src\libraries\System.Linq.Expressions\src\System.Linq.Expressions.csproj (System.Linq.Expressions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
 
namespace System.Linq.Expressions
{
    internal sealed class StackGuard
    {
        private const int MaxExecutionStackCount = 1024;
 
        private int _executionStackCount;
 
        public bool TryEnterOnCurrentStack()
        {
            if (RuntimeHelpers.TryEnsureSufficientExecutionStack())
            {
                return true;
            }
 
            if (_executionStackCount < MaxExecutionStackCount)
            {
                return false;
            }
 
            throw new InsufficientExecutionStackException();
        }
 
        public void RunOnEmptyStack<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2) =>
            RunOnEmptyStackCore(s =>
            {
                var t = ((Action<T1, T2>, T1, T2))s;
                t.Item1(t.Item2, t.Item3);
                return default(object);
            }, (action, arg1, arg2));
 
        public void RunOnEmptyStack<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3) =>
            RunOnEmptyStackCore(s =>
            {
                var t = ((Action<T1, T2, T3>, T1, T2, T3))s;
                t.Item1(t.Item2, t.Item3, t.Item4);
                return default(object);
            }, (action, arg1, arg2, arg3));
 
        public R RunOnEmptyStack<T1, T2, R>(Func<T1, T2, R> action, T1 arg1, T2 arg2) =>
            RunOnEmptyStackCore(s =>
            {
                var t = ((Func<T1, T2, R>, T1, T2))s;
                return t.Item1(t.Item2, t.Item3);
            }, (action, arg1, arg2));
 
        public R RunOnEmptyStack<T1, T2, T3, R>(Func<T1, T2, T3, R> action, T1 arg1, T2 arg2, T3 arg3) =>
            RunOnEmptyStackCore(s =>
            {
                var t = ((Func<T1, T2, T3, R>, T1, T2, T3))s;
                return t.Item1(t.Item2, t.Item3, t.Item4);
            }, (action, arg1, arg2, arg3));
 
        private R RunOnEmptyStackCore<R>(Func<object, R> action, object state)
        {
            _executionStackCount++;
 
            try
            {
                // Using default scheduler rather than picking up the current scheduler.
                Task<R> task = Task.Factory.StartNew(action!, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
 
                // Avoid AsyncWaitHandle lazy allocation of ManualResetEvent in the rare case we finish quickly.
                if (!task.IsCompleted)
                {
                    // Task.Wait has the potential of inlining the task's execution on the current thread; avoid this.
                    ((IAsyncResult)task).AsyncWaitHandle.WaitOne();
                }
 
                // Using awaiter here to propagate original exception
                return task.GetAwaiter().GetResult();
            }
            finally
            {
                _executionStackCount--;
            }
        }
    }
}