diff --git a/src/Common/src/CoreLib/System/Threading/Tasks/Task.cs b/src/Common/src/CoreLib/System/Threading/Tasks/Task.cs
index 4d790f483db8..5d893c5ceec6 100644
--- a/src/Common/src/CoreLib/System/Threading/Tasks/Task.cs
+++ b/src/Common/src/CoreLib/System/Threading/Tasks/Task.cs
@@ -131,8 +131,6 @@ public class Task : IAsyncResult, IDisposable
{
[ThreadStatic]
internal static Task t_currentTask; // The currently executing task.
- [ThreadStatic]
- private static StackGuard t_stackGuard; // The stack guard object for this thread
internal static int s_taskIdCounter; //static counter used to generate unique task IDs
@@ -1257,23 +1255,6 @@ internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptio
return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
}
- ///
- /// Gets the StackGuard object assigned to the current thread.
- ///
- internal static StackGuard CurrentStackGuard
- {
- get
- {
- StackGuard sg = t_stackGuard;
- if (sg == null)
- {
- t_stackGuard = sg = new StackGuard();
- }
- return sg;
- }
- }
-
-
///
/// Gets the Exception that caused the Task to end prematurely. If the
- /// Internal helper class to keep track of stack depth and decide whether we should inline or not.
- ///
- internal class StackGuard
- {
- // current thread's depth of nested inline task executions
- private int m_inliningDepth = 0;
-
- // For relatively small inlining depths we don't want to get into the business of stack probing etc.
- // This clearly leaves a window of opportunity for the user code to SO. However a piece of code
- // that can SO in 20 inlines on a typical 1MB stack size probably needs to be revisited anyway.
- private const int MAX_UNCHECKED_INLINING_DEPTH = 20;
-
- ///
- /// This method needs to be called before attempting inline execution on the current thread.
- /// If false is returned, it means we are too close to the end of the stack and should give up inlining.
- /// Each call to TryBeginInliningScope() that returns true must be matched with a
- /// call to EndInliningScope() regardless of whether inlining actually took place.
- ///
- internal bool TryBeginInliningScope()
- {
- // If we're still under the 'safe' limit we'll just skip the stack probe to save p/invoke calls
- if (m_inliningDepth < MAX_UNCHECKED_INLINING_DEPTH || RuntimeHelpers.TryEnsureSufficientExecutionStack())
- {
- m_inliningDepth++;
- return true;
- }
- else
- return false;
- }
-
- ///
- /// This needs to be called once for each previous successful TryBeginInliningScope() call after
- /// inlining related logic runs.
- ///
- internal void EndInliningScope()
- {
- m_inliningDepth--;
- Debug.Assert(m_inliningDepth >= 0, "Inlining depth count should never go negative.");
-
- // do the right thing just in case...
- if (m_inliningDepth < 0) m_inliningDepth = 0;
- }
- }
-
// Special internal struct that we use to signify that we are not interested in
// a Task's result.
internal struct VoidTaskResult { }
@@ -6609,18 +6547,17 @@ public UnwrapPromise(Task outerTask, bool lookForOce)
// For ITaskCompletionAction
public void Invoke(Task completingTask)
{
- // Check the current stack guard. If we're ok to inline,
- // process the task, and reset the guard when we're done.
- var sg = Task.CurrentStackGuard;
- if (sg.TryBeginInliningScope())
+ // If we're ok to inline, process the task. Otherwise, we're too deep on the stack, and
+ // we shouldn't run the continuation chain here, so queue a work item to call back here
+ // to Invoke asynchronously.
+ if (RuntimeHelpers.TryEnsureSufficientExecutionStack())
+ {
+ InvokeCore(completingTask);
+ }
+ else
{
- try { InvokeCore(completingTask); }
- finally { sg.EndInliningScope(); }
+ InvokeCoreAsync(completingTask);
}
- // Otherwise, we're too deep on the stack, and
- // we shouldn't run the continuation chain here, so queue a work
- // item to call back here to Invoke asynchronously.
- else InvokeCoreAsync(completingTask);
}
///
diff --git a/src/Common/src/CoreLib/System/Threading/Tasks/TaskScheduler.cs b/src/Common/src/CoreLib/System/Threading/Tasks/TaskScheduler.cs
index f274f20558cd..1d1a581b0b91 100644
--- a/src/Common/src/CoreLib/System/Threading/Tasks/TaskScheduler.cs
+++ b/src/Common/src/CoreLib/System/Threading/Tasks/TaskScheduler.cs
@@ -179,12 +179,11 @@ internal bool TryRunInline(Task task, bool taskWasPreviouslyQueued)
// Delegate cross-scheduler inlining requests to target scheduler
if (ets != this && ets != null) return ets.TryRunInline(task, taskWasPreviouslyQueued);
- StackGuard currentStackGuard;
if ((ets == null) ||
(task.m_action == null) ||
task.IsDelegateInvoked ||
task.IsCanceled ||
- (currentStackGuard = Task.CurrentStackGuard).TryBeginInliningScope() == false)
+ !RuntimeHelpers.TryEnsureSufficientExecutionStack())
{
return false;
}
@@ -192,27 +191,19 @@ internal bool TryRunInline(Task task, bool taskWasPreviouslyQueued)
// Task class will still call into TaskScheduler.TryRunInline rather than TryExecuteTaskInline() so that
// 1) we can adjust the return code from TryExecuteTaskInline in case a buggy custom scheduler lies to us
// 2) we maintain a mechanism for the TLS lookup optimization that we used to have for the ConcRT scheduler (will potentially introduce the same for TP)
- bool bInlined = false;
- try
- {
- if (TplEventSource.Log.IsEnabled())
- task.FireTaskScheduledIfNeeded(this);
+ if (TplEventSource.Log.IsEnabled())
+ task.FireTaskScheduledIfNeeded(this);
- bInlined = TryExecuteTaskInline(task, taskWasPreviouslyQueued);
- }
- finally
- {
- currentStackGuard.EndInliningScope();
- }
+ bool inlined = TryExecuteTaskInline(task, taskWasPreviouslyQueued);
// If the custom scheduler returned true, we should either have the TASK_STATE_DELEGATE_INVOKED or TASK_STATE_CANCELED bit set
// Otherwise the scheduler is buggy
- if (bInlined && !(task.IsDelegateInvoked || task.IsCanceled))
+ if (inlined && !(task.IsDelegateInvoked || task.IsCanceled))
{
throw new InvalidOperationException(SR.TaskScheduler_InconsistentStateAfterTryExecuteTaskInline);
}
- return bInlined;
+ return inlined;
}
///