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;
}
}
}
}