diff --git a/Projects/Server.Tests/Tests/Serialization/SerializationChunkSourceTests.cs b/Projects/Server.Tests/Tests/Serialization/SerializationChunkSourceTests.cs index 1cdb08188..69387f4e6 100644 --- a/Projects/Server.Tests/Tests/Serialization/SerializationChunkSourceTests.cs +++ b/Projects/Server.Tests/Tests/Serialization/SerializationChunkSourceTests.cs @@ -4,9 +4,8 @@ using Xunit; namespace Server.Tests; -// Constructing a persistence mutates the static Persistence registry (an unsynchronized -// SortedSet); every test that does so must share the sequential collection or parallel -// collections corrupt the tree. +// Constructing a persistence mutates the static registry (an unsynchronized SortedSet); +// tests that do so must share the sequential collection. [Collection("Sequential Server Tests")] public class SerializationChunkSourceTests { diff --git a/Projects/Server.Tests/Tests/Serialization/SerializationThreadWorkerHandshakeTests.cs b/Projects/Server.Tests/Tests/Serialization/SerializationThreadWorkerHandshakeTests.cs index 58b7f4982..5ea281172 100644 --- a/Projects/Server.Tests/Tests/Serialization/SerializationThreadWorkerHandshakeTests.cs +++ b/Projects/Server.Tests/Tests/Serialization/SerializationThreadWorkerHandshakeTests.cs @@ -7,12 +7,9 @@ namespace Server.Tests; [Collection("Sequential Server Tests")] public class SerializationThreadWorkerHandshakeTests { - // Regression: the pause handshake cleared _pause and checked the exit flag AFTER - // signaling _stopEvent. Wake/Sleep/Exit all run on the owning thread, so an Exit() - // issued the moment a Sleep() returned could either be clobbered (worker spins - // forever) or orphaned (worker returns without servicing Exit's Sleep) — a silent - // deadlock. Churn the full lifecycle with the racy back-to-back Sleep/Exit pattern; - // the watchdog turns a reintroduced deadlock into a failure instead of a hung run. + // The pause handshake must tolerate a new cycle starting the moment _stopEvent is + // set (Exit right after Sleep). The watchdog turns a reintroduced deadlock into a + // failure instead of a hung run. [Fact] public void WakeSleepExitChurn_NeverDeadlocks() { diff --git a/Projects/Server.Tests/Tests/Serialization/ShadowDictionaryEntriesTests.cs b/Projects/Server.Tests/Tests/Serialization/ShadowDictionaryEntriesTests.cs index 315a58900..bdee95698 100644 --- a/Projects/Server.Tests/Tests/Serialization/ShadowDictionaryEntriesTests.cs +++ b/Projects/Server.Tests/Tests/Serialization/ShadowDictionaryEntriesTests.cs @@ -4,9 +4,8 @@ using Xunit; namespace Server.Tests; -// Constructing a persistence mutates the static Persistence registry (an unsynchronized -// SortedSet); every test that does so must share the sequential collection or parallel -// collections corrupt the tree. +// Constructing a persistence mutates the static registry (an unsynchronized SortedSet); +// tests that do so must share the sequential collection. [Collection("Sequential Server Tests")] public class ShadowDictionaryEntriesTests { diff --git a/Projects/Server/Serialization/FileBufferWriter.cs b/Projects/Server/Serialization/FileBufferWriter.cs index c7408f7bc..9f1e9c290 100644 --- a/Projects/Server/Serialization/FileBufferWriter.cs +++ b/Projects/Server/Serialization/FileBufferWriter.cs @@ -25,9 +25,8 @@ namespace Server; /// growing: the full raw write path (unrolled encoded ints, in-place strings) composes into /// memory, and the file sees large sequential positional writes. Seeks flush the staging /// block and move the file offset, so backwards patches (e.g. the idx entity count) become -/// small positional writes. This replaces memory-mapped snapshot writing, which paid soft -/// page faults on every composed page and unpredictable dirty-section teardown stalls at -/// dispose — measured ~4x slower end-to-end than staged writes at snapshot sizes. +/// small positional writes. Memory-mapped writing pays soft page faults on every composed +/// page and dirty-section teardown stalls at dispose — measured ~4x slower at snapshot sizes. /// A single item larger than the staging block grows the block via the base resize path, /// so oversized spans and strings remain correct. /// diff --git a/Projects/Server/Serialization/SerializationThreadWorker.cs b/Projects/Server/Serialization/SerializationThreadWorker.cs index d936213c4..5a9b5ed1c 100644 --- a/Projects/Server/Serialization/SerializationThreadWorker.cs +++ b/Projects/Server/Serialization/SerializationThreadWorker.cs @@ -270,12 +270,9 @@ public class SerializationThreadWorker writer.Close(); - // Wake/Sleep/Exit all run on the owning thread, so the moment _stopEvent is set - // the owner may start another pause cycle (Exit does exactly that). Clear _pause - // and sample the exit condition BEFORE signaling: clearing after the signal can - // clobber the next cycle's pause request (this thread then spins forever waiting - // for a pause that never reads true), and deciding to exit after the signal can - // return without servicing that cycle's Sleep (the owner then blocks forever). + // The owning thread may start another pause cycle the moment _stopEvent is set + // (Exit does exactly that). Clear _pause and sample the exit condition before + // signaling, or the new cycle's pause request is clobbered / its Sleep orphaned. var exiting = Core.Closing || worker._exit; Volatile.Write(ref worker._pause, false);