Repository: dotnet/roslyn
File: src/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs (C#)

Code before:
        // We'll use a variant of an AutoResetEvent so in the case we were to have multiple requests for the same category,
        // they're all released. That's not expected to happen, but it ensures better behavior in the case of a misbehaving client.
        var resetEvent = _categoryToLspChanged.GetOrAdd(category, static _ => new ReleaseAllAutoResetEvent(initialState: true));
        await resetEvent.WaitAsync().WithCancellation(cancellationToken).ConfigureAwait(false);

        // We've hit a change, so we close the current request to allow the client to open a new one.
        context.TraceDebug($"Closing workspace/diagnostics request for {category}");
    }

    internal abstract TestAccessor GetTestAccessor();

    internal readonly struct TestAccessor(AbstractWorkspacePullDiagnosticsHandler<TDiagnosticsParams, TReport, TReturn> handler)
    {
        public void TriggerConnectionClose() => handler.UpdateLspChanged();
    }

    /// <summary>
    /// An <see cref="AutoResetEvent"/> with two differences: it supports async waiting, and in the case Set() releases a waiter, it releases all waiters rather than just one.
    /// </summary>
    private sealed class ReleaseAllAutoResetEvent
    {
        private readonly object _gate = new object();
        private readonly List<TaskCompletionSource<object?>> _waiters = new();
        private bool _state;

        public ReleaseAllAutoResetEvent(bool initialState)
        {
            _state = initialState;
        }

        public Task WaitAsync()
        {
            lock (_gate)
            {
                if (_state)
                {
                    // Just let this request through
                    _state = false;
                    return Task.CompletedTask;
                }
                else
                {
                    var waiter = new TaskCompletionSource<object?>(TaskCreationOptions.RunContinuationsAsynchronously);
                    _waiters.Add(waiter);
                    return waiter.Task;
                }
            }
        }

        public void Set()
        {

Review comment (suggestion):
for a type that is entirely private, and/or owns some piece of normal reference-type state, i'm 100% ok with `lock(this)` or `lock(_waiters)`.  but this is also fine.

Code after:
        // Wait until the workspace changes again (or was changed while we were in the middle of processing).
        // We'll use a variant of an AutoResetEvent so in the case we were to have multiple requests for the same category,
        // they're all released. That's not expected to happen, but it ensures better behavior in the case of a misbehaving client.
        var resetEvent = _categoryToLspChanged.GetOrAdd(category, static _ => new ReleaseAllAutoResetEvent(initialState: true));
        await resetEvent.WaitAsync().WithCancellation(cancellationToken).ConfigureAwait(false);

        // We've hit a change, so we close the current request to allow the client to open a new one.
        context.TraceDebug($"Closing workspace/diagnostics request for {category}");
    }

    internal abstract TestAccessor GetTestAccessor();

    internal readonly struct TestAccessor(AbstractWorkspacePullDiagnosticsHandler<TDiagnosticsParams, TReport, TReturn> handler)
    {
        public void TriggerConnectionClose() => handler.UpdateLspChanged();
    }

    /// <summary>
    /// An <see cref="AutoResetEvent"/> with two differences: it supports async waiting, and in the case Set() releases a waiter, it releases all waiters rather than just one.
    /// 
    /// The semantics of this type are thus: there is internally a "set" state. When the event is <see cref="Set()"/>, the next waiter to call <see cref="WaitAsync()"/> will be let through, and the
    /// event resets to false. A call to <see cref="Set()"/> while there is already waiters will release all the waiters, and since it already let a waiter through, the state is untouched.
    /// </summary>
    private sealed class ReleaseAllAutoResetEvent
    {
        private readonly object _gate = new object();
        private readonly List<TaskCompletionSource<object?>> _waiters = new();

        /// <summary>
        /// True if <see cref="Set()"/> has been called, indicating the next waiter should be let through.
        /// </summary>
        private bool _state;

        public ReleaseAllAutoResetEvent(bool initialState)
        {
            _state = initialState;
        }

        public Task WaitAsync()
        {
            lock (_gate)
            {
                if (_state)
                {
                    // Since _state was true, we let the next waiter through. Any waiter after that must wait for the next Set().
                    Contract.ThrowIfTrue(_waiters.Count > 0);
                    _state = false;
                    return Task.CompletedTask;
                }
                else
                {