Skip to content

Commit d875305

Browse files
authored
Merge pull request connamara#966 from gbirchmeier/nonsessionlog-concurrency
fix connamara#963 NonSessionLog concurrency bug on Windows
2 parents 3ef667b + 3e9c985 commit d875305

File tree

4 files changed

+43
-36
lines changed

4 files changed

+43
-36
lines changed

QuickFIXn/Logger/NonSessionLog.cs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,31 @@ namespace QuickFix.Logger;
44
/// A logger that can be used when the calling logic cannot identify a session (which is rare).
55
/// Does not create a log artifact until first write.
66
/// </summary>
7-
public class NonSessionLog : System.IDisposable {
7+
public class NonSessionLog {
88

99
private readonly ILogFactory _logFactory;
10-
private ILog? _log;
1110

12-
private readonly object _sync = new();
11+
private readonly static object _sync = new();
1312

1413
internal NonSessionLog(ILogFactory logFactory) {
1514
_logFactory = logFactory;
1615
}
1716

17+
/// <summary>
18+
/// Write to a log that is not tied to a session.
19+
/// Calls to this should be rare, and only to record problem events that
20+
/// cannot be tied to a specific session.
21+
/// (Think errors and maybe warnings, NOT infos.)
22+
/// </summary>
23+
/// <param name="s">message to log</param>
1824
internal void OnEvent(string s) {
19-
if (_disposed) return;
20-
25+
// Atomic open/write/close operation.
26+
// This function should not be called often enough
27+
// for this overhead to cause a performance issue.
2128
lock (_sync) {
22-
_log ??= _logFactory.CreateNonSessionLog();
23-
}
24-
_log?.OnEvent(s);
25-
}
26-
27-
private bool _disposed;
28-
public void Dispose() {
29-
if (_disposed) return;
30-
31-
if (_log != null) {
29+
ILog _log = _logFactory.CreateNonSessionLog();
30+
_log.OnEvent(s);
3231
_log.Dispose();
33-
_log = null;
34-
_disposed = true;
3532
}
3633
}
3734
}

QuickFIXn/ThreadedSocketAcceptor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,6 @@ public void Stop(bool force)
274274
LogoutAllSessions(force);
275275
DisposeSessions();
276276
_sessions.Clear();
277-
_nonSessionLog.Dispose();
278277
_isStarted = false;
279278

280279
// FIXME StopSessionTimer();

RELEASE_NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ What's New
4242
* #942 - fix #942: field 369 (LastMsgSeqNumProcessed) wrong in ResendRequest message (gbirchmeier)
4343
* #940 - Create an alternate CharEncoding.GetBytes impl which uses ArrayPool to improve memory performance (VAllens)
4444
* #951 - fix: restore Session disconnect during SocketInitiatorThread.Read exception (gbirchmeier/trevor-bush)
45+
* #963 - fix: concurrency bug with NonSessionLog on Windows (gbirchmeier)
4546

4647
### v1.13.1
4748
* backport #951 to 1.13

UnitTests/Logger/NonSessionLogTests.cs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,17 @@
55
namespace UnitTests.Logger;
66

77
[TestFixture]
8-
public class NonSessionLogTests {
8+
public class NonSessionLogTests
9+
{
910
private readonly string _logDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, "log");
1011

11-
private NonSessionLog? _nslog;
12-
1312
[TearDown]
1413
public void Teardown()
1514
{
16-
_nslog?.Dispose();
17-
_nslog = null;
1815
}
19-
20-
private FileLogFactory CreateFileLogFactory() {
16+
17+
private FileLogFactory CreateFileLogFactory()
18+
{
2119
if (Directory.Exists(_logDirectory))
2220
Directory.Delete(_logDirectory, true);
2321

@@ -32,47 +30,59 @@ private FileLogFactory CreateFileLogFactory() {
3230
TargetCompID=TARGETCOMP
3331
""";
3432

35-
QuickFix.SessionSettings settings = new QuickFix.SessionSettings(
33+
QuickFix.SessionSettings settings = new(
3634
new StringReader(configString));
3735

3836
return new FileLogFactory(settings);
3937
}
4038

4139
[Test]
42-
public void TestWithFileLogFactory() {
40+
public void TestWithFileLogFactory()
41+
{
4342
FileLogFactory flf = CreateFileLogFactory();
44-
_nslog = new NonSessionLog(flf);
43+
NonSessionLog nslog = new(flf);
4544

4645
// Log artifact not created before first log-write
4746
Assert.That(Directory.Exists(_logDirectory), Is.False);
4847

4948
// Log artifact exists after first log-write
50-
_nslog.OnEvent("some text");
49+
nslog.OnEvent("some text");
5150
Assert.That(Directory.Exists(_logDirectory));
5251
Assert.That(File.Exists(Path.Combine(_logDirectory, "Non-Session-Log.event.current.log")));
5352

5453
// cleanup (don't delete log unless success)
55-
_nslog.Dispose();
56-
_nslog = null;
5754
Directory.Delete(_logDirectory, true);
5855
}
5956

6057
[Test]
61-
public void TestWithCompositeLogFactory() {
58+
public void TestTwoFileLogs()
59+
{
60+
FileLogFactory flf = CreateFileLogFactory();
61+
NonSessionLog nslog = new(flf);
62+
nslog.OnEvent("log1");
63+
64+
NonSessionLog nslog2 = new(flf);
65+
nslog2.OnEvent("log2");
66+
67+
// cleanup (don't delete log unless success)
68+
Directory.Delete(_logDirectory, true);
69+
}
70+
71+
[Test]
72+
public void TestWithCompositeLogFactory()
73+
{
6274
CompositeLogFactory clf = new CompositeLogFactory([CreateFileLogFactory(), new NullLogFactory()]);
63-
_nslog = new NonSessionLog(clf);
75+
NonSessionLog nslog = new(clf);
6476

6577
// Log artifact not created before first log-write
6678
Assert.That(Directory.Exists(_logDirectory), Is.False);
6779

6880
// Log artifact exists after first log-write
69-
_nslog.OnEvent("some text");
81+
nslog.OnEvent("some text");
7082
Assert.That(Directory.Exists(_logDirectory));
7183
Assert.That(File.Exists(Path.Combine(_logDirectory, "Non-Session-Log.event.current.log")));
7284

7385
// cleanup (don't delete log unless success)
74-
_nslog.Dispose();
75-
_nslog = null;
7686
Directory.Delete(_logDirectory, true);
7787
}
7888
}

0 commit comments

Comments
 (0)