Skip to content

Commit 33d3cce

Browse files
committed
docs(tests): describe the parameter defect in the past tense
Three comments in `reentrant_service.rs` described the pre-fix implementation in the present tense -- "`validate_and_apply` holds `on_set_callback.read()` across the user callback" -- in a branch whose entire purpose is that it no longer does. A reader arriving later would conclude the defect is still live. Also states plainly what the recursive-`set_parameter` scenario detects. Recursive `read()` on one thread succeeds unless a writer is queued between the two acquisitions, and nothing in the test queues one, so against unfixed source it is a coin flip rather than a detector -- which matches this PR's own evidence, where only the re-registering case fired. It is a regression test for the fixed behaviour; the re-registering case is the deterministic one. Saying so stops the next reader trusting it as proof the defect existed.
1 parent 4a6ae8e commit 33d3cce

1 file changed

Lines changed: 20 additions & 10 deletions

File tree

crates/hiroz-tests/tests/reentrant_service.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,17 @@ fn service_handler_calling_another_service_does_not_deadlock() {
252252
///
253253
/// `ParameterState::validate_and_apply` invokes the user callback while holding
254254
/// `on_set_callback.read()` (an `std::sync::RwLock` read guard). A callback that
255-
/// calls `set_parameter` re-enters `validate_and_apply` on the same thread and
256-
/// therefore takes that same read lock recursively. Recursive read acquisition on
255+
/// calls `set_parameter` re-entered `validate_and_apply` on the same thread and
256+
/// therefore took that same read lock recursively. Recursive read acquisition on
257257
/// `std::sync::RwLock` is explicitly not guaranteed by the standard library — it
258-
/// deadlocks if a writer is queued between the two acquisitions — so this is the
258+
/// deadlocks if a writer is queued between the two acquisitions — so this was the
259259
/// closest analogue to the pub/sub bug outside pub/sub.
260+
///
261+
/// Note what this test does and does not detect. Recursive `read()` on one
262+
/// thread usually *succeeds* unless a writer is queued in between, and nothing
263+
/// here queues one — so against unfixed source it is a coin flip, not a
264+
/// detector. The re-registering case below is the deterministic one. This is a
265+
/// regression test for the fixed behaviour, not proof the defect existed.
260266
#[test]
261267
#[serial]
262268
fn parameter_on_set_callback_setting_another_parameter_does_not_deadlock() {
@@ -304,11 +310,14 @@ fn parameter_on_set_callback_setting_another_parameter_does_not_deadlock() {
304310

305311
/// A parameter `on_set` callback that replaces the callback registration.
306312
///
307-
/// This is the deterministic form of the same defect. `validate_and_apply` holds
308-
/// `on_set_callback.read()` across the user callback; `on_set_parameters` takes
309-
/// `on_set_callback.write()`. A callback that re-registers therefore asks the
310-
/// same thread for a write lock while it still holds a read lock on the same
311-
/// `std::sync::RwLock` — a guaranteed self-deadlock, no race required.
313+
/// This is the deterministic form of the same defect — stated in the past tense
314+
/// because this branch is what removes it. `validate_and_apply` *used to* hold
315+
/// `on_set_callback.read()` across the user callback, while `on_set_parameters`
316+
/// takes `on_set_callback.write()`. A callback that re-registered therefore
317+
/// asked the same thread for a write lock while it still held a read lock on
318+
/// the same `std::sync::RwLock` — a guaranteed self-deadlock, no race required.
319+
/// The fix clones the callback `Arc` out and drops the guard before invoking,
320+
/// so no lock is held when the callback runs; this test holds that line.
312321
///
313322
/// "Swap out the validator once the node is configured" is an ordinary thing to
314323
/// want, and rclcpp supports it (`remove_on_set_parameters_callback` /
@@ -333,8 +342,9 @@ fn parameter_on_set_callback_reregistering_does_not_deadlock() {
333342
if !ran_c.swap(true, Ordering::SeqCst)
334343
&& let Some(n) = node_weak.upgrade()
335344
{
336-
// Re-register from inside the callback: write lock requested
337-
// while this thread still holds the read lock.
345+
// Re-register from inside the callback. Pre-fix this asked
346+
// for a write lock while the same thread still held the read
347+
// lock; post-fix no guard is live here at all.
338348
n.on_set_parameters(|_| SetParametersResult::success());
339349
}
340350
SetParametersResult::success()

0 commit comments

Comments
 (0)