Fix bvar worker starvation with recursive bthread mutex - #3417
Fix bvar worker starvation with recursive bthread mutex#3417walterzhaoJR wants to merge 3 commits into
Conversation
Replace the recursive pthread mutex protecting bvar's VarMap with a bthread-aware recursive mutex so yielding metric callbacks do not block worker pthreads. Split RecursiveMutex into a header-only bazel target to avoid a bvar/bthread dependency cycle, and wire make/bazel unit tests (stubs and gtest_main) correctly. Related issue: apache#2888
There was a problem hiding this comment.
Pull request overview
This PR addresses bvar worker starvation/deadlock risk by replacing the VarMap’s recursive pthread_mutex_t with a bthread-aware recursive mutex that tracks ownership by bthread ID (while still supporting native pthread callers). It also adds regression/stress tests and updates the test build rules across Make/CMake/Bazel to support the new coverage and avoid dependency cycles.
Changes:
- Introduce
bthread_recursive_mutex_tplusbthread::RecursiveMutex, and implement recursive locking semantics on top ofbthread_mutex_t. - Switch bvar VarMap locking in
src/bvar/variable.cpptobthread::RecursiveMutexto prevent worker pthread blocking when metric callbacks yield. - Add/adjust unit tests and build rules (Make/CMake/Bazel) including stubs to keep
test_bvarrunnable without the full bthread runtime.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/Makefile | Links test_bvar with stubbed recursive-mutex symbols; adds a custom link rule for brpc_bvar_mutex_unittest to avoid gtest_main. |
| test/CMakeLists.txt | Adds bvar_bthread_stubs.cpp to test_bvar; switches brpc_bvar_mutex_unittest to link gtest (not gtest_main). |
| test/bvar_bthread_stubs.cpp | Provides non-bthread-runtime implementations of bthread_recursive_mutex_* for test_bvar. |
| test/BUILD.bazel | Adds stub source to bvar unit tests; adds a dedicated Bazel test target for brpc_bvar_mutex_unittest; excludes it from the generated brpc unittest set. |
| test/bthread_mutex_unittest.cpp | Adds recursive-mutex unit/stress coverage, including yield/migration scenarios. |
| test/brpc_bvar_mutex_unittest.cpp | Adds regression tests for the bvar VarMap starvation scenario and recursive describe behavior (via fork/exec child modes). |
| src/bvar/variable.cpp | Replaces VarMap’s recursive pthread mutex with bthread::RecursiveMutex; updates locking sites. |
| src/bthread/types.h | Adds the bthread_recursive_mutex_t type. |
| src/bthread/recursive_mutex.h | Adds the public C API declarations and the bthread::RecursiveMutex C++ wrapper. |
| src/bthread/mutex.h | Includes the new recursive-mutex header (to expose the new API alongside existing mutex APIs). |
| src/bthread/mutex.cpp | Implements bthread_recursive_mutex_* atop bthread_mutex_t, tracking ownership by bthread ID or native pthread identity. |
| BUILD.bazel | Introduces :bthread_recursive_mutex header-only target; wires :bvar and :bthread deps to avoid a Bazel dependency cycle. |
Comments suppressed due to low confidence (1)
test/brpc_bvar_mutex_unittest.cpp:305
- execl("/proc/self/exe", ...) is not portable and will cause this test to fail on platforms without /proc. Re-exec using the absolute path returned by butil::GetProcessAbsolutePath().
if (pid == 0) {
execl("/proc/self/exe", "brpc_bvar_mutex_unittest", child_argument,
nullptr);
_exit(127);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| cc_library( | ||
| # Header-only interface so :bvar can use RecursiveMutex without depending on | ||
| # :bthread (which itself depends on :bvar). Implementation lives in :bthread. | ||
| name = "bthread_recursive_mutex", | ||
| hdrs = [ | ||
| "src/bthread/recursive_mutex.h", | ||
| "src/bthread/types.h", | ||
| ], | ||
| includes = [ | ||
| "src/", | ||
| ], | ||
| visibility = ["//visibility:public"], | ||
| deps = [ | ||
| ":butil", | ||
| ], | ||
| ) |
There was a problem hiding this comment.
OK,this is a valid concern. The current //:bthread_recursive_mutex target exports only the headers, while the bthread_recursive_mutex_* implementations remain in //:bthread. As a result, a target depending only on //:bvar can compile but fail to link. The current bvar tests do not expose this because they explicitly link bvar_bthread_stubs.cpp.
Moving only the recursive-mutex functions into a separate library would not fully solve the cycle: they still depend on bthread_self() and bthread_mutex_*(), while //:bthread already depends on //:bvar.
A clean solution would require either:
splitting a bvar-independent mutex/runtime core out of //:bthread; or
combining the Bazel bvar and bthread targets, at the cost of losing standalone //:bvar.
Is standalone //:bvar considered a compatibility requirement? I would appreciate guidance on the preferred dependency boundary before restructuring this part.
Replace the recursive pthread mutex protecting bvar's VarMap with a bthread-aware recursive mutex so yielding metric callbacks do not block worker pthreads. Add regression coverage for worker starvation, recursive ownership across bthread migration, pthread use, and high contention.
Related issue: #2888
What problem does this PR solve?
Issue Number: resolves #2888
Problem Summary:
bvar::Variableprotects its global VarMap with a recursive pthread mutex. If a metric callback, such as aPassiveStatusgetter, yields while holding this lock, other bthreads may block their worker pthreads while waiting for it. Once all workers are blocked, the lock holder cannot resume, causing worker starvation or deadlock.A pthread recursive mutex also identifies its owner by OS pthread, which does not correctly represent a bthread that may migrate between workers.
What is changed and the side effects?
Changed:
bthread_recursive_mutex_tand its C++ wrapper,bthread::RecursiveMutex.bthread_mutex_tfor contention so waiting bthreads are suspended instead of blocking worker pthreads.bthread::RecursiveMutex.test_bvartarget independent of the full bthread runtime.Side effects:
Performance effects:
Breaking backward compatibility:
Test results
bthread_mutex_unittest: 3 recursive mutex tests passed.brpc_bvar_mutex_unittest: 3 bvar regression tests passed.test_bvar: 64/64 tests passed.Check List: