Skip to content

Commit c5d8738

Browse files
committed
Update comments and assertion messages
1 parent 25909b0 commit c5d8738

3 files changed

Lines changed: 20 additions & 5 deletions

File tree

include/pybind11/detail/internals.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,14 @@ inline object get_python_state_dict() {
551551
}
552552

553553
// Get or create per-storage capsule in the current interpreter's state dict.
554+
// - The storage is interpreter-dependent: different interpreters will have different storage.
555+
// This is important when using multiple-interpreters, to avoid sharing unshareable objects
556+
// between interpreters.
557+
// - There is one storage per `key` in an interpreter and it is accessible between all extensions
558+
// in the same interpreter.
559+
// - The life span of the storage is tied to the interpreter: it will be kept alive until the
560+
// interpreter shuts down.
561+
//
554562
// Use test-and-set pattern with `PyDict_SetDefault` for thread-safe concurrent access.
555563
// WARNING: There can be multiple threads creating the storage at the same time, while only one
556564
// will succeed in inserting its capsule into the dict. Therefore, the deleter will be
@@ -693,6 +701,11 @@ class internals_pp_manager {
693701
: holder_id_(id), on_fetch_(on_fetch) {}
694702

695703
std::unique_ptr<InternalsType> *get_or_create_pp_in_state_dict() {
704+
// The `unique_ptr<InternalsType>` output is leaked on interpreter shutdown. Once an
705+
// instance is created, it will never be deleted until the process exits (compare to
706+
// interpreter shutdown in multiple-interpreter scenarios).
707+
// Because we cannot guarantee the order of destruction of capsules in the interpreter
708+
// state dict, leaking avoids potential use-after-free issues during interpreter shutdown.
696709
auto *pp
697710
= atomic_get_or_create_in_state_dict<std::unique_ptr<InternalsType>,
698711
/*LeakOnInterpreterShutdown=*/true>(holder_id_);

include/pybind11/gil_safe_call_once.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ class gil_safe_call_once_and_store {
248248
}
249249

250250
// Get or create per-storage capsule in the current interpreter's state dict.
251+
// The storage is interpreter-dependent and will not be shared across interpreters.
251252
storage_type *get_or_create_storage_in_state_dict() {
252253
return detail::atomic_get_or_create_in_state_dict<storage_type>(get_storage_key().c_str());
253254
}

tests/test_multiple_interpreters/test_multiple_interpreters.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def test():
221221
import mod_per_interpreter_gil_with_singleton as m
222222
223223
objects = m.get_objects_in_singleton()
224-
assert objects == [
224+
expected = [
225225
type(None),
226226
tuple,
227227
list,
@@ -230,11 +230,12 @@ def test():
230230
collections.defaultdict,
231231
collections.deque,
232232
]
233+
assert objects == expected, f"Expected {{expected!r}}, got {{objects!r}}."
233234
234-
assert hasattr(m, 'MyClass')
235-
assert hasattr(m, 'MyGlobalError')
236-
assert hasattr(m, 'MyLocalError')
237-
assert hasattr(m, 'MyEnum')
235+
assert hasattr(m, 'MyClass'), "Module missing MyClass"
236+
assert hasattr(m, 'MyGlobalError'), "Module missing MyGlobalError"
237+
assert hasattr(m, 'MyLocalError'), "Module missing MyLocalError"
238+
assert hasattr(m, 'MyEnum'), "Module missing MyEnum"
238239
"""
239240
).lstrip()
240241

0 commit comments

Comments
 (0)