Skip to content

Commit

Permalink
Avoid locking when logging on linux.
Browse files Browse the repository at this point in the history
  • Loading branch information
CptMoore committed Dec 7, 2024
1 parent dae0aef commit 1446b13
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 14 deletions.
146 changes: 146 additions & 0 deletions ModTek.Common/Utils/LogStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System;
using System.IO;
using System.Linq;

namespace ModTek.Common.Utils;

internal class LogStream
{
private readonly ILogStream _impl;
internal LogStream(string path)
{
try
{
if (typeof(string).Assembly.GetTypes().Any(x => x.FullName == "System.IO.MonoIO"))
{
_impl = new MonoIoFileStreamImpl(path);
}
else
{
// win32 improvements
throw new NotImplementedException();
}
}
catch
{
_impl = new ThreadSafeFileStreamImpl(path);
}
}

public void Append(byte[] bytes, int srcOffset, int count)
{
_impl.Append(bytes, srcOffset, count);
}

public void Dispose()
{
_impl.Dispose();
}

private interface ILogStream : IDisposable
{
public void Append(byte[] bytes, int srcOffset, int count);
}

private class MonoIoFileStreamImpl : ILogStream
{
private readonly FileStream _stream;
internal MonoIoFileStreamImpl(string path)
{
_stream = new FileStream(
path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite|FileShare.Delete,
1, // minimum size allowed, can't disable the buffer otherwise
FileOptions.None
);
// disables use of non-thread safe internal buf_start within WriteInternal
_ = _stream.SafeFileHandle;
}

public void Append(byte[] bytes, int srcOffset, int count)
{
// skip check as our logging never logs less
//EnsureMinimumSize(ref bytes, ref srcOffset, ref count);

_stream.Write(bytes, srcOffset, count);
}

private const byte Linefeed = (byte)'\n';
private const int ZeroWidthSize = 3; // ZWSP is unfortunately 3 bytes and not 2 in UTF8
private const int ZeroWidthWithNewlineSize = 4;
private static readonly byte[] s_zeroWidthSpaceWithNewline = [0xE2, 0x80, 0x8B, Linefeed];
private static void EnsureMinimumSize(ref byte[] bytes, ref int srcOffset, ref int count)
{
// the buffer within FileStream is never thread safe
// to avoid it we always have to write at least 2 bytes
if (count >= 2)
{
return;
}

if (count > 0)
{
if (count == 1 && bytes[srcOffset] == Linefeed) // linux WriteLine()
{
bytes = s_zeroWidthSpaceWithNewline;
srcOffset = 0;
count = ZeroWidthWithNewlineSize;
}
else
{
var newBytes = new byte[ZeroWidthSize + count];
Buffer.BlockCopy(s_zeroWidthSpaceWithNewline, 0, newBytes, 0, ZeroWidthSize);
Buffer.BlockCopy(bytes, srcOffset, newBytes, ZeroWidthSize, count);
bytes = newBytes;
srcOffset = 0;
count = ZeroWidthSize + count;
}
}
else
{
bytes = s_zeroWidthSpaceWithNewline;
srcOffset = 0;
count = ZeroWidthSize;
}
}

public void Dispose()
{
_stream.Dispose();
}
}

private class ThreadSafeFileStreamImpl : ILogStream
{
private readonly FileStream _stream;
internal ThreadSafeFileStreamImpl(string path)
{
_stream = new FileStream(
path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite|FileShare.Delete,
1, // small buffer size is equivalent to AutoFlush
FileOptions.None
);
}

public void Append(byte[] bytes, int srcOffset, int count)
{
lock (this)
{
_stream.Write(bytes, srcOffset, count);
}
}

public void Dispose()
{
lock (this)
{
_stream.Dispose();
}
}
}
}
17 changes: 3 additions & 14 deletions ModTek/Features/Logging/AppenderFile.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Globalization;
using System.IO;
using ModTek.Common.Utils;

namespace ModTek.Features.Logging;
Expand All @@ -9,7 +8,7 @@ internal class AppenderFile : IDisposable
{
private readonly Filters _filters;
private readonly Formatter _formatter;
private readonly FileStream _writer;
private readonly LogStream _writer;

internal AppenderFile(string path, AppenderSettings settings)
{
Expand All @@ -18,14 +17,7 @@ internal AppenderFile(string path, AppenderSettings settings)

FileUtils.CreateParentOfPath(path);
FileUtils.RotatePath(path, settings.LogRotationCount);
_writer = new FileStream(
path,
FileMode.Append,
FileAccess.Write,
FileShare.ReadWrite|FileShare.Delete,
1, // small buffer size means AutoFlush
FileOptions.None
);
_writer = new LogStream(path);

MTLoggerMessageDto.GetTimings(out var stopwatchTimestamp, out var dateTime, out var unityStartupTime);
Write(System.Text.Encoding.UTF8.GetBytes(
Expand Down Expand Up @@ -80,10 +72,7 @@ private void Write(byte[] bytes, int length)
WriteStopwatch.Start();
try
{
lock(this)
{
_writer.Write(bytes, 0, length);
}
_writer.Append(bytes, 0, length);
}
finally
{
Expand Down

0 comments on commit 1446b13

Please sign in to comment.