public static void UsingReadLockInsideFcs()

in ReSharper.FSharp/src/FSharp/FSharp.ProjectModelBase/src/FSharpAsyncUtil.cs [56:149]


    public static void UsingReadLockInsideFcs(IShellLocks locks, Action action)
    {
      var logger = Logger.GetLogger(typeof(FSharpAsyncUtil));

      // Capture FCS cancellation token so we can propagate cancellation in cases like F#->C#->F#,
      // where the last FCS request is initiated from inside reading the C# metadata,
      // and the initial request may get cancelled
      var fcsToken = Cancellable.HasCancellationToken ? Cancellable.Token : CancellationToken.None;

      // Try to acquire read lock on the current thread.
      // It should be possible, unless there's a write lock request that prevents it.
      try
      {
        // FCS token is the source for the cancellation
        using var _ = Interruption.Current.Add(new LifetimeInterruptionSource(fcsToken));
        if (locks.TryExecuteWithReadLock(action))
        {
          logger.Trace("UsingReadLockInsideFcs: executed on the initial thread, returning");
          return;
        }
      }
      catch (Exception e) when (e.IsOperationCanceled())
      {
        logger.Trace("UsingReadLockInsideFcs: exception: the operation cancelled on the initial thread");

        if (locks.IsReadAccessAllowed())
        {
          // The FCS request has originated from a R# thread and was cancelled. We don't want to requeue this request.
          // If the request is coming from FCS, it's cancelled below in the loop.
          // We could also check `Cancellable.HasCancellationToken` instead.
          logger.Trace("UsingReadLockInsideFcs: read lock was acquired before the request, rethrowing");
          throw;
        }
      }
      catch (Exception e)
      {
        logger.Trace("UsingReadLockInsideFcs: exception: rethrowing");
        Logger.LogException(e);
        throw;
      }

      // Could not finish task under a read lock. Queue a request to be processed by a thread calling FCS.
      // To ensure FCS metadata consistency, we retry cancelled requests in this loop.
      // This may happen when R# read lock is cancelled, but the corresponding FCS request has not seen it yet.
      // We check the FCS request cancellation separately via its cancellation token.
      logger.Trace("UsingReadLockInsideFcs: before the loop");
      while (true)
      {
        CheckAndThrow();

        logger.Trace("UsingReadLockInsideFcs: enqueueing a new request");
        var tcs = new TaskCompletionSource<Unit>();
        ReadRequests.Enqueue(() =>
        {
          try
          {
            logger.Trace("UsingReadLockInsideFcs: before action");
            action();
            logger.Trace("UsingReadLockInsideFcs: after action");

            tcs.SetResult(Unit.Instance);
          }
          catch (Exception e) when (e.IsOperationCanceled())
          {
            tcs.SetCanceled();
            logger.Trace("UsingReadLockInsideFcs: exception: cancelled");
          }
          catch (Exception e)
          {
            tcs.SetException(e);
            logger.Trace("UsingReadLockInsideFcs: exception");
            Logger.LogException(e);
          }
        });

        try
        {
          logger.Trace("UsingReadLockInsideFcs: before tcs.Task.Wait");
          tcs.Task.Wait();
          logger.Trace("UsingReadLockInsideFcs: after tcs.Task.Wait, breaking");
          break;
        }
        catch (Exception e) when (e.IsOperationCanceled())
        {
          logger.Trace("UsingReadLockInsideFcs: exception: cancelled, checking FCS token");
          CheckAndThrow();
        }
        catch (Exception)
        {
          logger.Trace("UsingReadLockInsideFcs: exception");
          throw;
        }
      }
    }