How to stop thread reuse while still having uncompleted child-threads in ASP .NET Core request pipeline?

Viewed 143

I have a webapi with a custom execution context set on the request pipeline using a middleware something like this:

public async Task Invoke(HttpContext httpContext)
{
  using (Context.With(new TokenContext("token")))
  {
    await _next(httpContext);
  }
}

And the Context-manager looks a bit like this with a stack of IContext objects that are local to the current thread (and the threads it create).

public static class Context
{
  private static AsyncLocal<Stack<IContext>> _current = new AsyncLocal<Stack<IContext>>();
  public static IContext Current
  {
    get
    {
      return _current.Value != null && _current.Value.Count > 0 ?_current.Value.Peek() : null;
    }
  }

  public static IDisposable With(IContext context)
  {
    Assert.IsNotNull(context);

    if (_current.Value == null) _current.Value = new Stack<IContext>();
    _current.Value.Push(context);

    return new DelegateDisposable(() =>
    {
      _current.Value.Pop();
    });
  }
}

The problem I experience is when having multiple requests going on at the same time. Sometimes I get issues with the current context not being what I expected. I have noticed that the current context sometimes have a value already before I set it with the using-statement. My conclusion is that the initial thread used for the request is returned to a thread pool when hitting the await _next line and that this thread is/can be reused for following requests, which sometimes happen before "child-threads" have completed. This leaves me with a lot of unpredictable behavior.

So, my question: How can I tweak this so that I don't run into this issue? Can I force the request-pipeline to not return threads before all "child-threads" have completed? Will this be inefficient? Can I force the "mother-thread" to clean up it's reference to the context-stack before it returns itself to the pool?

Any other options?

1 Answers
Related