viewer: fix HTTP live-follow OOM via datasetLoaded backpressure + shared pixel mask

The viewer could grow to ~100 GB RAM when live-following an HTTP broker. The
rc.153 images_in_flight backpressure only throttled imageLoaded; the heavy
per-frame payload rides datasetLoaded, fanned out over ~10 queued cross-thread
connections with no cap. In HTTPSyncDataset follow mode (entered when an operator
clicks an image while following live) RefreshDatasetOnly_i emits a fresh full
dataset every autoload tick with no imageLoaded, so the gate never engaged and
the queued events - each pinning a full JFJochReaderDataset (full-detector
PixelMask + per-image plots) - accumulated without bound.

Backpressure datasetLoaded the same way as imageLoaded: a datasets_in_flight
counter (cap 2), all emits routed through EmitDatasetLoaded_i, and
AutoLoadTimerExpired gated on it (covers HTTPSyncDataset). The window routes the
worker's datasetLoaded through a single OnDatasetReady sink that fans out
synchronously via datasetReady and acks with datasetConsumed. Under load stale
datasets are dropped; the next tick sends the latest.

Share the pixel mask instead of deep-copying it: JFJochReaderDataset::pixel_mask
is now shared_ptr<const PixelMask>, so per-frame dataset copies share the ~72 MB
mask. UpdateUserMask does copy-on-write; JFJochHttpReader caches the mask by
arm_date so a live refresh reuses one shared mask per acquisition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 10:58:10 +02:00
co-authored by Claude Fable 5
parent b4a29ebb32
commit dc8a80f3e0
16 changed files with 120 additions and 44 deletions
+2 -2
View File
@@ -239,8 +239,8 @@ TEST_CASE("JFJochReader_PixelMask", "[HDF5][Full]") {
JFJochHDF5Reader reader;
reader.ReadFile("test16_master.h5");
auto dataset = reader.GetDataset();
REQUIRE(dataset->pixel_mask.GetMask().size() == x.GetPixelsNum());
CHECK(dataset->pixel_mask.GetMask() == pixel_mask);
REQUIRE(dataset->pixel_mask->GetMask().size() == x.GetPixelsNum());
CHECK(dataset->pixel_mask->GetMask() == pixel_mask);
std::shared_ptr<JFJochReaderImage> reader_image;
REQUIRE_NOTHROW(reader_image = reader.LoadImage(0));