Skip to content

Prevent with_critical_section_mutex2 from receiving the same object twice - #6098

Merged
davidhewitt merged 6 commits into
PyO3:mainfrom
ngoldbaum:critical-section-mutex-fix
Jun 9, 2026
Merged

Prevent with_critical_section_mutex2 from receiving the same object twice#6098
davidhewitt merged 6 commits into
PyO3:mainfrom
ngoldbaum:critical-section-mutex-fix

Conversation

@ngoldbaum

Copy link
Copy Markdown
Contributor

Fixes #6051.

Implemented following the suggestions from @Person-93 and @davidhewitt on the issue.

@davidhewitt davidhewitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, generally LGTM, some brief thoughts.

Comment thread src/sync/critical_section.rs Outdated
/// Rather than receiving the wrapped data directly, access is gated via the
/// [`pyo3::sync::critical_section::EnteredCriticalSection`](crate::sync::critical_section::EnteredCriticalSection)
/// struct. Note that `f` receives an `EnteredCriticalSection<'s, T1>` for the
/// data protected by `m1` but an `Option<EnteredCriticalSectio<'s, T2>>` for

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// data protected by `m1` but an `Option<EnteredCriticalSectio<'s, T2>>` for
/// data protected by `m1` but an `Option<EnteredCriticalSection<'s, T2>>` for

Comment thread src/sync/critical_section.rs Outdated
};
if core::ptr::eq(m1.mutex.get(), m2.mutex.get()) {
f(EnteredCriticalSection(&m1.data), None)
} else {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be possible to go via the single-critical-section path if the pointers are the same

Suggested change
} else {
if core::ptr::eq(m1, m2) {
with_critical_section_mutex(|cs| f(cs, None))
} else {

... I believe this would avoid the need for the interpreter to also do an equality check on the two mutexes? But the cost of additional function indirection might cancel the benefit out.

Comment thread src/sync/critical_section.rs Outdated
Comment on lines +435 to +437
if let Some(ref inner) = b2 {
assert!(unsafe { *inner.get() });
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should do b2.unwrap().get() here given we expect it to be non-none? Same for the other cases, and it looks like we might be missing a test for the new m1 == m2 case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and it looks like we might be missing a test for the new m1 == m2 case?

There's already a test for that, test_critical_section2_same_object_no_deadlock. But with this new API deadlocks aren't really a concern anymore so I'll go ahead and change the name.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should do b2.unwrap().get() here given we expect it to be non-none?

Ah good call, I changed it to match the mutable one above but that can be spelled *b2.as_mut().unwrap().get_mut() which is better anyway. I'll change this one.

@davidhewitt
davidhewitt added this pull request to the merge queue Jun 9, 2026
Merged via the queue into PyO3:main with commit f9c4c17 Jun 9, 2026
46 of 47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

with_critical_section_mutex improvements

3 participants