@@ -631,7 +631,7 @@ TEST_F(ThreadFilterTest, SnapshotCapturesOwnedLifecycle) {
631631 EXPECT_FALSE (slot->snapshotBlockRun ().active );
632632}
633633
634- TEST_F (ThreadFilterTest, OwnedBlockSuppressesBeforeAnyWallSample ) {
634+ TEST_F (ThreadFilterTest, OwnedBlockSuppressesOnlyAfterSuccessfulWallSample ) {
635635 filter->init (nullptr , true );
636636 int slot_id = filter->registerThread (1234 );
637637 ASSERT_GE (slot_id, 0 );
@@ -642,6 +642,12 @@ TEST_F(ThreadFilterTest, OwnedBlockSuppressesBeforeAnyWallSample) {
642642
643643 ThreadEntry entry{1234 , slot, slot->lifecycleGeneration (),
644644 slot->recordingEpoch ()};
645+ u64 generation = 0 ;
646+ EXPECT_TRUE (filter->activeOwnedBlockGeneration (entry, generation));
647+ EXPECT_EQ (ThreadFilter::tokenGeneration (token), generation);
648+ EXPECT_FALSE (filter->isOwnedBlockSuppressionCandidate (entry));
649+
650+ slot->markBlockGenerationSampled (generation);
645651 EXPECT_TRUE (filter->isOwnedBlockSuppressionCandidate (entry));
646652 EXPECT_FALSE (filter->isOwnedBlockSuppressionCandidate (
647653 {1235 , slot, slot->lifecycleGeneration (), slot->recordingEpoch ()}));
@@ -654,6 +660,38 @@ TEST_F(ThreadFilterTest, OwnedBlockSuppressesBeforeAnyWallSample) {
654660 EXPECT_FALSE (filter->isOwnedBlockSuppressionCandidate (entry));
655661}
656662
663+ TEST_F (ThreadFilterTest, StaleSampleCompletionCannotSuppressNewBlockGeneration) {
664+ filter->init (nullptr , true );
665+ int slot_id = filter->registerThread (1234 );
666+ ASSERT_GE (slot_id, 0 );
667+ ThreadFilter::Slot* slot = filter->slotForId (slot_id);
668+ ASSERT_NE (nullptr , slot);
669+
670+ u64 first_token = filter->enterBlockedRun (slot_id, OSThreadState::SLEEPING );
671+ ASSERT_NE (0ULL , first_token);
672+ u64 first_generation = ThreadFilter::tokenGeneration (first_token);
673+ ASSERT_TRUE (filter->exitBlockedRun (slot_id, first_generation));
674+
675+ u64 second_token =
676+ filter->enterBlockedRun (slot_id, OSThreadState::CONDVAR_WAIT );
677+ ASSERT_NE (0ULL , second_token);
678+ u64 second_generation = ThreadFilter::tokenGeneration (second_token);
679+ ASSERT_GT (second_generation, first_generation);
680+
681+ ThreadEntry entry{1234 , slot, slot->lifecycleGeneration (),
682+ slot->recordingEpoch ()};
683+ slot->markBlockGenerationSampled (first_generation);
684+ EXPECT_FALSE (filter->isOwnedBlockSuppressionCandidate (entry));
685+
686+ slot->markBlockGenerationSampled (second_generation);
687+ EXPECT_TRUE (filter->isOwnedBlockSuppressionCandidate (entry));
688+
689+ // A delayed completion from the first run must not overwrite the newer mark.
690+ slot->markBlockGenerationSampled (first_generation);
691+ EXPECT_EQ (second_generation, slot->sampledBlockGeneration ());
692+ EXPECT_TRUE (filter->isOwnedBlockSuppressionCandidate (entry));
693+ }
694+
657695TEST_F (ThreadFilterTest, ContextScopeNeverSuppressesOwnedBlock) {
658696 filter->init (" 0" , false );
659697 int slot_id = filter->registerThread (1234 );
@@ -666,6 +704,7 @@ TEST_F(ThreadFilterTest, ContextScopeNeverSuppressesOwnedBlock) {
666704
667705 ThreadEntry entry{1234 , slot, slot->lifecycleGeneration (),
668706 slot->recordingEpoch ()};
707+ slot->markBlockGenerationSampled (slot->blockGeneration ());
669708 EXPECT_FALSE (filter->isOwnedBlockSuppressionCandidate (entry));
670709}
671710
@@ -679,6 +718,7 @@ TEST_F(ThreadFilterTest, ContextEpochDisablesOwnedBlockSuppression) {
679718 slot_id, OSThreadState::CONDVAR_WAIT ));
680719 ThreadEntry entry{1234 , slot, slot->lifecycleGeneration (),
681720 slot->recordingEpoch ()};
721+ slot->markBlockGenerationSampled (slot->blockGeneration ());
682722 ASSERT_TRUE (filter->isOwnedBlockSuppressionCandidate (entry));
683723
684724 filter->add (1234 , slot_id);
@@ -868,6 +908,7 @@ TEST_F(ThreadRegistryTest, UnfilteredSuppressionValidatesIdentityAndLifecycle) {
868908
869909 u64 token = registry.enterBlockedRun (slot_id, OSThreadState::SLEEPING );
870910 ASSERT_NE (0u , token);
911+ slot->markBlockGenerationSampled (ThreadFilter::tokenGeneration (token));
871912 ThreadEntry entry{4444 , slot, slot->lifecycleGeneration (),
872913 slot->recordingEpoch ()};
873914 EXPECT_TRUE (registry.isOwnedBlockSuppressionCandidate (entry));
@@ -905,7 +946,9 @@ TEST_F(ThreadRegistryTest, ConcurrentTidReuseInvalidatesSuppressionSnapshot) {
905946 ASSERT_GE (slot_id, 0 );
906947 ThreadFilter::Slot* slot = registry.slotForId (slot_id);
907948 ASSERT_NE (nullptr , slot);
908- ASSERT_NE (0u , registry.enterBlockedRun (slot_id, OSThreadState::SLEEPING ));
949+ u64 token = registry.enterBlockedRun (slot_id, OSThreadState::SLEEPING );
950+ ASSERT_NE (0u , token);
951+ slot->markBlockGenerationSampled (ThreadFilter::tokenGeneration (token));
909952 ThreadEntry stale{tid, slot, slot->lifecycleGeneration (),
910953 slot->recordingEpoch ()};
911954
@@ -996,6 +1039,7 @@ TEST_F(ThreadRegistryTest, NewUnfilteredRecordingReclaimsRetainedSlot) {
9961039
9971040 u64 token = registry.enterBlockedRun (slot_id, OSThreadState::SLEEPING );
9981041 ASSERT_NE (0u , token);
1042+ slot->markBlockGenerationSampled (ThreadFilter::tokenGeneration (token));
9991043 ThreadEntry stale{tid, slot, slot->lifecycleGeneration (),
10001044 slot->recordingEpoch ()};
10011045 ASSERT_TRUE (registry.isOwnedBlockSuppressionCandidate (stale));
0 commit comments