-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Fix race condition with py::make_key_iterator in free threading #5971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
bfe55ed
fa03953
70c2ffc
8690dd7
be01d1e
61e032e
29840ce
91189c9
603b25a
dc8e35c
f3197de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -230,6 +230,7 @@ using instance_map = std::unordered_multimap<const void *, instance *>; | |||||||||||||||||||||
| #ifdef Py_GIL_DISABLED | ||||||||||||||||||||||
| // Wrapper around PyMutex to provide BasicLockable semantics | ||||||||||||||||||||||
| class pymutex { | ||||||||||||||||||||||
| friend class pycritical_section; | ||||||||||||||||||||||
| PyMutex mutex; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public: | ||||||||||||||||||||||
|
|
@@ -238,6 +239,23 @@ class pymutex { | |||||||||||||||||||||
| void unlock() { PyMutex_Unlock(&mutex); } | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class pycritical_section { | ||||||||||||||||||||||
| pymutex &mutex; | ||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, interesting to me the RAII doesn't own the mutex and the ctor takes a mutable reference.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks correct to me, based on this Cursor-generated analysis: This is the standard lock-guard pattern: the RAII object owns the critical-section lifetime, not the mutex. It takes a pybind11/include/pybind11/detail/internals.h Lines 230 to 239 in 0080cae
|
||||||||||||||||||||||
| PyCriticalSection cs; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public: | ||||||||||||||||||||||
| explicit pycritical_section(pymutex &m) : mutex(m) { | ||||||||||||||||||||||
| PyCriticalSection_BeginMutex(&cs, &mutex.mutex); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| ~pycritical_section() { PyCriticalSection_End(&cs); } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Non-copyable and non-movable to prevent double-unlock | ||||||||||||||||||||||
| pycritical_section(const pycritical_section &) = delete; | ||||||||||||||||||||||
| pycritical_section &operator=(const pycritical_section &) = delete; | ||||||||||||||||||||||
| pycritical_section(pycritical_section &&) = delete; | ||||||||||||||||||||||
| pycritical_section &operator=(pycritical_section &&) = delete; | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Instance map shards are used to reduce mutex contention in free-threaded Python. | ||||||||||||||||||||||
| struct instance_map_shard { | ||||||||||||||||||||||
| instance_map registered_instances; | ||||||||||||||||||||||
|
|
@@ -856,7 +874,7 @@ inline local_internals &get_local_internals() { | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #ifdef Py_GIL_DISABLED | ||||||||||||||||||||||
| # define PYBIND11_LOCK_INTERNALS(internals) std::unique_lock<pymutex> lock((internals).mutex) | ||||||||||||||||||||||
| # define PYBIND11_LOCK_INTERNALS(internals) pycritical_section lock((internals).mutex) | ||||||||||||||||||||||
| #else | ||||||||||||||||||||||
| # define PYBIND11_LOCK_INTERNALS(internals) | ||||||||||||||||||||||
| #endif | ||||||||||||||||||||||
|
|
@@ -885,7 +903,7 @@ inline auto with_exception_translators(const F &cb) | |||||||||||||||||||||
| get_local_internals().registered_exception_translators)) { | ||||||||||||||||||||||
| auto &internals = get_internals(); | ||||||||||||||||||||||
| #ifdef Py_GIL_DISABLED | ||||||||||||||||||||||
| std::unique_lock<pymutex> lock((internals).exception_translator_mutex); | ||||||||||||||||||||||
| pycritical_section lock((internals).exception_translator_mutex); | ||||||||||||||||||||||
| #endif | ||||||||||||||||||||||
| auto &local_internals = get_local_internals(); | ||||||||||||||||||||||
| return cb(internals.registered_exception_translators, | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to be careful about copy/move here?
... give me a moment to look at this with the help of some LLM ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Result: be01d1e
(created with Cursor Composer 1 model)