Add Pixely.Observations, an append-only log readers drain at their own pace - #488
Merged
Conversation
…ders drain at their own pace
…f interfaces over one type
|
✅ Development package |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #486.
Game state answers what is true now. It does not answer a transition that no pair of reads one frame apart
contains: a unit that walked a path and arrived, one that appeared and was gone again, an action that resolved
and was reversed inside the same frame. Rules can resolve many such transitions between two reads, so any
"most recent transition" field is overwritten before a reader looks at it.
Pixely.Observationsis a new project holding an append-only log that readers drain at their own pace, with nodependency on anything else in the tree.
Shape
Three types, each with one job:
ObservationLog<TEntry>is the storage. It is constructed and handed to the other two, and has no otherpublic members.
ObservationWriter<TEntry>appends and cannot read.ObservationReader<TEntry>is one reader's position in the log, and cannot append.A rule holding a writer has no way to drain the log, and a reader has no way to record an observation no rule
produced. That split is by type rather than by an interface projected over one class, so nothing has to trust a
narrower view of a wider object.
Each consumer constructs its own reader instead of being handed one, because every reader of a given log is the
same closed type and the container resolves by type. Constructing it also lets the consumer name itself, which
is what the stall message reports.
Entries
One log carries one entry type. To carry several kinds of entry in one order,
TEntryis a tagged type the lognever looks inside. A value type keeps heterogeneous entries and no per-entry allocation from fighting each
other: entries go in by
in, are stored inline in the ring, come out by copy, and the trim walks the readerlist with the struct enumerator rather than LINQ. A class entry type is allowed where a game prefers one, and
the log releases each slot as it trims so a drained entry is not held alive.
Addressing is the game's business. The perceiver rides in
TEntryand the reader skips what it did notperceive, so the log stays ignorant of participants.
Bounds
The buffer starts at 16 and doubles towards the maximum capacity given to the constructor, so a log is not
sized for its worst burst up front. It never shrinks, and it is reclaimed when its scope dies.
Maximum capacity is a stall detector rather than a working size. A reader that stops draining holds the trim
point where it stopped; once the log fills, appending throws and names the reader that stopped and how far
behind it is. Dropping the oldest entry instead would hide exactly that failure.
Tests
tests/Pixely.Observations.Testscovers ordering, a reader seeing only what follows its creation, independentpacing between readers, growth past the initial capacity, growth while the retained entries are wrapped so the
copy runs in two segments, growth stopping at a maximum that doubling would overshoot, a maximum below the
initial capacity, wraparound under interleaved append and read, trimming holding steady over 3200 entries in a
32-slot log, the stall message naming one reader and naming several tied at the back, appending succeeding
again once the stalled reader drains, disposal freeing a stalled log while preserving what another reader still
needs, double disposal, reads after disposal, and a weak-reference check that a class entry really is released
when the log trims.
The documented registration was verified against the real DI source generator rather than asserted from the
docs, and the
[Explicit]package archive test was run by filter to confirm the new assembly ships.Not in this PR
Pixely.EventsandPixely.Architectureare untouched. Removing them is a separate change.