private sealed class LockingStream()

in src/log4net/Appender/FileAppender.cs [86:205]


  private sealed class LockingStream(LockingModelBase lockingModel) : Stream, IDisposable
  {
    [Log4NetSerializable]
    public sealed class LockStateException : LogException
    {
      public LockStateException(string message)
        : base(message)
      { }

      public LockStateException()
      { }

      public LockStateException(string message, Exception innerException)
        : base(message, innerException)
      { }

      private LockStateException(SerializationInfo info, StreamingContext context)
        : base(info, context)
      { }
    }

    private readonly object _syncRoot = new();
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "todo")]
    private Stream? _realStream;
    private int _lockLevel;

    protected override void Dispose(bool disposing)
    {
      lockingModel.CloseFile();
      base.Dispose(disposing);
    }

    public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
      => AssertLocked().ReadAsync(buffer, offset, count, cancellationToken);

    public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
      AssertLocked();
      return base.WriteAsync(buffer, offset, count, cancellationToken);
    }

    public override void Flush() => AssertLocked().Flush();

    public override int Read(byte[] buffer, int offset, int count)
      => AssertLocked().Read(buffer, offset, count);

    public override int ReadByte() => AssertLocked().ReadByte();

    public override long Seek(long offset, SeekOrigin origin)
      => AssertLocked().Seek(offset, origin);

    public override void SetLength(long value) => AssertLocked().SetLength(value);

    void IDisposable.Dispose() => Dispose(true);

    public override void Write(byte[] buffer, int offset, int count)
      => AssertLocked().Write(buffer, offset, count);

    public override void WriteByte(byte value) => AssertLocked().WriteByte(value);

    // Properties
    public override bool CanRead => false;

    public override bool CanSeek => AssertLocked().CanSeek;

    public override bool CanWrite => AssertLocked().CanWrite;

    public override long Length => AssertLocked().Length;

    public override long Position
    {
      get => AssertLocked().Position;
      set => AssertLocked().Position = value;
    }

    private Stream AssertLocked()
    {
      if (_realStream is null)
      {
        throw new LockStateException("The file is not currently locked");
      }

      return _realStream;
    }

    public bool AcquireLock()
    {
      bool ret = false;
      lock (_syncRoot)
      {
        if (_lockLevel == 0)
        {
          // If lock is already acquired, nop
          _realStream = lockingModel.AcquireLock();
        }

        if (_realStream is not null)
        {
          _lockLevel++;
          ret = true;
        }
      }

      return ret;
    }

    public void ReleaseLock()
    {
      lock (_syncRoot)
      {
        _lockLevel--;
        if (_lockLevel == 0)
        {
          // If already unlocked, nop
          lockingModel.ReleaseLock();
          _realStream = null;
        }
      }
    }
  }