@@ -441,3 +441,61 @@ TEST(RHistEngine_RBinWithError, StressFillAtomicWeight)
441441 EXPECT_EQ (engine.GetBinContent (0 ).fSum , NFills * Weight);
442442 EXPECT_EQ (engine.GetBinContent (0 ).fSum2 , NFills * Weight * Weight);
443443}
444+
445+ TEST (RHistEngine_RBinWithError, SnapshotAtomic)
446+ {
447+ static constexpr std::size_t Bins = 20 ;
448+ const RRegularAxis axis (Bins, {0 , Bins});
449+ RHistEngine<RBinWithError> engineA ({axis});
450+
451+ engineA.Fill (-100 );
452+ for (std::size_t i = 0 ; i < Bins; i++) {
453+ engineA.Fill (i + 0.5 );
454+ }
455+ engineA.Fill (100 );
456+
457+ RHistEngine<RBinWithError> engineB = engineA.SnapshotAtomic ();
458+ ASSERT_EQ (engineB.GetNDimensions (), 1 );
459+ ASSERT_EQ (engineB.GetTotalNBins (), Bins + 2 );
460+
461+ EXPECT_EQ (engineB.GetBinContent (RBinIndex::Underflow ()).fSum , 1 );
462+ for (auto index : axis.GetNormalRange ()) {
463+ EXPECT_EQ (engineB.GetBinContent (index).fSum , 1 );
464+ }
465+ EXPECT_EQ (engineB.GetBinContent (RBinIndex::Overflow ()).fSum , 1 );
466+ }
467+
468+ TEST (RHistEngine_RBinWithError, StressSnapshotAtomic)
469+ {
470+ static constexpr std::size_t Bins = 20 ;
471+ static constexpr std::size_t NThreads = 4 ;
472+ static constexpr std::size_t NFillsPerThread = 100000 ;
473+ static constexpr std::size_t NSnapshots = 10000 ;
474+
475+ // Create a histogram with some bins that takes a bit of time to snapshot. The idea of this stress test is then to
476+ // fill the first and last bin from multiple threads. If the snapshot is consistent, it must never have a bigger bin
477+ // content in the last bin.
478+ RHistEngine<RBinWithError> engine (Bins, {0 , Bins});
479+ double first = 0 , last = 0 ;
480+
481+ std::atomic_flag snapshotter;
482+ StressInParallel (NThreads, [&] {
483+ if (!snapshotter.test_and_set ()) {
484+ for (std::size_t i = 0 ; i < NSnapshots; i++) {
485+ RHistEngine<RBinWithError> snapshot = engine.SnapshotAtomic ();
486+ first = snapshot.GetBinContent (0 ).fSum ;
487+ last = snapshot.GetBinContent (Bins - 1 ).fSum ;
488+ if (last > first) {
489+ return ;
490+ }
491+ }
492+ } else {
493+ for (std::size_t i = 0 ; i < NFillsPerThread; i++) {
494+ engine.FillAtomic (0.5 );
495+ engine.FillAtomic (Bins - 0.5 );
496+ }
497+ }
498+ });
499+
500+ EXPECT_GE (first, last);
501+ }
0 commit comments