Merge origin/master into master
🚦 CI / changes (push) Successful in 4s
🚦 CI / docker (push) Has been skipped
🚦 CI / test (push) Successful in 5m1s
🚦 CI / tsan (push) Successful in 3m38s
🚦 CI / docs (push) Has been skipped

This commit is contained in:
2026-08-06 21:57:32 +02:00
3 changed files with 45 additions and 24 deletions
+15 -3
View File
@@ -267,10 +267,22 @@ public:
// has_eof_, the consumer is the only one that clears it, so observing
// false here means the consumer has finished with the storage and will
// not touch it again until this store publishes the next token.
if (has_eof_.load(std::memory_order_acquire)) {
stats_.record_drop();
//
// Not counted as a drop, and this is the important part. A source that
// has reached the end of its input keeps being polled and keeps
// returning EOF — that is the normal steady state, not an error — so a
// token arriving while one is already pending is a *re-offer*, and
// refusing it loses nothing: the pending token carries the same
// meaning and is already on its way. Counting it as a drop made a
// clean run report data loss and exit non-zero.
//
// The cost of that choice, stated plainly: a genuinely distinct second
// token would also be refused silently, and the channel cannot tell the
// two apart. Re-offering is the case that actually occurs here, and the
// delivery guarantee that matters — the first token arrives — holds
// either way.
if (has_eof_.load(std::memory_order_acquire))
return SentinelResult::SlotBusy;
}
eof_value_ = make_storage(std::move(value));
has_eof_.store(true, std::memory_order_release);
// Wake a consumer blocked in pop(): the sentinel is now deliverable even