feat: add finish(), the end-of-stream lifecycle hook - #11
Merged
Merged
Conversation
psyinf
added this pull request to stack #12
September 17, 2026 17:44
psyinf
force-pushed
the
feature/lifecycle-finish
branch
from
September 17, 2026 18:15
cf2c2f0 to
7269745
Compare
psyinf
force-pushed
the
feature/lifecycle-finish
branch
from
September 17, 2026 18:22
7269745 to
3dd2804
Compare
A stage only acted when a message reached it, so a stage holding state (statistics, a batch, an open file) had no defined point to flush it; the alternatives were destructors or an in-band end-of-stream message. - MessageFilter::finish(): called once after the last message, defaults to a no-op, produces no message - DslFilterGraph::finish() finishes every stage in run order, so a stage is finished after the stages it reads from - FilterGraph, JsonFilterGraph, AnyFilterChain, FanoutFilter, JoinFilter and nested graphs forward it - detail::FinishScope: every stage is finished even if one throws, and the first exception is rethrown afterwards - breaking: AnyMessageFilter::finish() is pure virtual, like setContext, so a composite cannot silently fail to forward it No tick(): time is domain-specific, so the README documents the in-band tick message instead.
psyinf
force-pushed
the
feature/lifecycle-finish
branch
from
September 17, 2026 18:26
3dd2804 to
24ee99b
Compare
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.
Stack (bottom → top)
GraphContextfinish()lifecycle hook (this PR)Summary
A stage acted only when a message reached it, so a stage that accumulates something (statistics, a batch, an open file) had no defined point at which to flush it. The alternatives were both weak: a destructor (order across a graph is implicit and exceptions cannot propagate) or an in-band end-of-stream message that every stage type has to understand and pass on.
MessageFilter::finish()is that point. It is called once after the last message, defaults to a no-op, and produces no message.Behaviour
FilterGraph,DslFilterGraph,JsonFilterGraph,AnyFilterChain,FanoutFilter,JoinFilterand nested graphs pass it to their stages.DslFilterGraphfinishes its stages in run order, so a stage is finished after the stages it reads from.detail::FinishScope); the first exception is rethrown once the others have run. One failing stage cannot keep the rest from flushing.finish()returns nothing, so nothing is routed downstream — which would need new merge semantics (every other slot a hole). A stage that wants to hand a final result to the application publishes it through theGraphContext. The name leaves the door open for aflush()-style variant later.Breaking change
AnyMessageFilter::finish()is pure virtual, likesetContextin #9: a composite that forgot to forward it would otherwise silently fail to flush its inner stages. CustomAnyMessageFilterimplementations must implement it. (Say the word if you'd rather have a no-op default here — it is a one-line change.)No
tick()Deliberately left out. Time is domain-specific (event time, wall clock, a sensor clock), so a generic signature would fit few users. The README documents the in-band tick message instead: a
Tickalternative in the graph's input type, fed through the graph like any other message.Testing
New
LifecycleTests(6 cases): run order in a DSL graph, a nested graph forwarding to its own stages, the compile-timeFilterGraph, aJsonFilterGraphwith fanout branches and join paths, every stage finishing when one throws, and finishing twice. Full suite: 83/83 locally (MSVC debug, ASan).