Stabilize timing-sensitive bthread tests and fix the butex interruption race - #3545
Open
chenBright wants to merge 2 commits into
Open
chenBright wants to merge 2 commits into
chenBright wants to merge 2 commits into
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved test-synchronization and cleanup issues remain, including a critical timer-thread cleanup path.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Stabilizes timing-sensitive bthread tests and fixes a pthread-backed butex interruption race.
Changes:
- Replaces fixed sleeps with state polling and completion synchronization.
- Improves cleanup, socket readiness checks, and multi-tag assertions.
- Preserves butex timeout and value-mismatch error semantics.
File summaries
| File | Reviewed changes and findings |
|---|---|
test/bthread_work_stealing_queue_unittest.cpp |
Makes stop-state synchronization atomic and resettable. |
test/bthread_unittest.cpp |
Observes sleep registration before stopping. moderate (1 vote): Fatal assertion cleanup can leave the worker alive. |
test/bthread_timer_thread_unittest.cpp |
Adds timer state waits. critical (1 vote): Fatal assertions can skip timer cleanup while callbacks reference stack state. moderate (1 vote): _npending is not reset per round. |
test/bthread_rwlock_unittest.cpp |
Synchronizes lock-state tests. moderate (1 vote each): Fixed reader delays do not guarantee contention, and non-fatal writer registration checks can allow the ordering test to pass spuriously. |
test/bthread_mutex_unittest.cpp |
Waits for mutex contention. moderate (1 vote): Fatal timeout cleanup can leave a locked mutex and live bthread. |
test/bthread_futex_unittest.cpp |
Adds bounded waiter cleanup. moderate (1 vote): Cleanup-induced EWOULDBLOCK results are not accepted when appropriate. |
test/bthread_fd_unittest.cpp |
Improves interruption and local connection tests. moderate (3 votes): pthread_kill may return ESRCH during the completion race. |
test/bthread_cond_unittest.cpp |
Resets shared condition-test state. |
test/bthread_butex_unittest.cpp |
Adds interruption and timing tests. moderate (1 vote each): Synchronization does not establish waiter registration, and a non-fatal timer-registration check can allow the interruption test to pass without validating its precondition. |
test/bthread_butex_multi_tag_unittest.cpp |
Validates actual task tags. |
test/brpc_socket_unittest.cpp |
Waits for accepted socket publication before checking settings. |
src/bthread/butex.cpp |
Fixes pthread butex interruption race handling. |
Review details
Suppressed comments (8)
test/bthread_butex_unittest.cpp:258
- The gate only acknowledges execution immediately before
butex_wait; it does not establish that the waiter has been registered, nor does it hold the task between registration andfutex_wait_private. Consequentlybthread_stopcan occur before registration or after the wait has started, and 100 repetitions can still pass without exercising the newEWOULDBLOCK-to-EINTRpath. Add a deterministic synchronization point around waiter registration and the underlying wait.
arg->entering_wait->signal();
}
timespec ts = butil::milliseconds_from_now(arg->wait_msec);
int rc = bthread::butex_wait(arg->butex, arg->expected_val,
arg->wait_msec < 0 ? nullptr : &ts);
test/bthread_butex_unittest.cpp:449
EXPECT_TRUEis non-fatal here, so if timer registration never appears the test continues tobthread_stop. For the normal-stack case that stop is then consumed beforebthread_usleep, which still produces the expectedESTOP; the test can therefore pass without validating interruption after sleep registration. Record the failure and make it fail after cleanup, or otherwise make this precondition mandatory while preserving cleanup.
EXPECT_TRUE(sleeping) << "Timed out waiting for sleep registration";
test/bthread_futex_unittest.cpp:124
- The failure cleanup below sets
lock1to 1 to release waiters that had not registered yet. Such a waiter returns-1withEWOULDBLOCKbecause the futex value no longer matches, but this helper unconditionally expectsrc == 0, so a missed registration produces extra assertion failures and obscures the intended deadline failure. Accept the value-mismatch result only when the cleanup store has occurred.
EXPECT_EQ(0, rc);
test/bthread_mutex_unittest.cpp:62
- When the deadline expires,
ASSERT_EQreturns from the test before unlockingmor joiningth1;lockeris still blocked on the mutex, so this failure path leaks a live bthread and can contaminate later tests. Use a non-fatal check here so the existing cleanup always runs.
ASSERT_EQ(257u, state->load(butil::memory_order_relaxed));
test/bthread_rwlock_unittest.cpp:536
- The new timed acquisition still relies on the preceding fixed 50 ms sleep to create a continuous reader load. Under scheduler delay, the writer can acquire before any reader has entered, so the test passes without exercising the starvation scenario it claims to cover. Wait on an explicit reader-ready count (and then keep readers active) before starting the writer.
// A timed acquisition also makes the failure path reachable when the
// writer really starves, so readers can be stopped and joined safely.
timespec deadline = butil::seconds_from_now(10);
int rc = bthread_rwlock_timedwrlock(&rw, &deadline);
EXPECT_EQ(0, rc) << "Writer starved under concurrent readers";
test/bthread_rwlock_unittest.cpp:358
EXPECT_TRUElets the test proceed when the writer was not observed inwriter_wait_count. It then starts the reader and may still pass based on scheduling, so the ordering assertion no longer proves the documented writer-priority race. Make registration a mandatory precondition while retaining cleanup if the wait times out.
EXPECT_TRUE(WaitForRWLockState([&] {
return reinterpret_cast<butil::atomic<unsigned>*>(rw.writer_wait_count)
->load(butil::memory_order_relaxed) == 1;
}));
test/bthread_timer_thread_unittest.cpp:342
_npendingis the heap size published by the timer thread and is not reset per round. After round 0 it remains at leastkBatch, so on later rounds this predicate is already true before the newly scheduled near-term task is consumed; the test can unschedule the batch while it is still in buckets and pass without exercising the sweep path. Wait on a per-round callback acknowledgement before unscheduling.
ASSERT_TRUE(WaitUntil([&] {
return timer_thread._npending.load(butil::memory_order_relaxed) >=
static_cast<int64_t>(kBatch);
}));
test/bthread_unittest.cpp:527
- If the sleep-registration deadline expires, this fatal assertion returns before
bthread_stopandbthread_join; the 60-second worker is then left alive, contrary to the cleanup comment and potentially affecting the rest of the test process. This should be non-fatal so the stop/join cleanup still executes.
ASSERT_TRUE(sleeping);
- Files reviewed: 12/12 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
What problem does this PR solve?
Issue Number: resolve
Problem Summary:
Running unit tests in parallel exposes timing assumptions that can cause intermittent failures:
What is changed and the side effects?
Changed:
Side effects:
Performance effects:
Breaking backward compatibility:
Check List: