Skip to content

Commit f3a8123

Browse files
committed
Add isolation and gc test for gil_safe_call_once_and_store
1 parent 58c08ac commit f3a8123

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

tests/test_with_catch/test_subinterpreter.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,30 @@ void unsafe_reset_internals_for_single_interpreter() {
4040
py::detail::get_local_internals();
4141
}
4242

43+
py::object &get_dict_type_object() {
44+
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
45+
return storage
46+
.call_once_and_store_result(
47+
[]() -> py::object { return py::module_::import("builtins").attr("dict"); })
48+
.get_stored();
49+
}
50+
51+
py::object &get_ordered_dict_type_object() {
52+
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
53+
return storage
54+
.call_once_and_store_result(
55+
[]() -> py::object { return py::module_::import("collections").attr("OrderedDict"); })
56+
.get_stored();
57+
}
58+
59+
py::object &get_default_dict_type_object() {
60+
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
61+
return storage
62+
.call_once_and_store_result(
63+
[]() -> py::object { return py::module_::import("collections").attr("defaultdict"); })
64+
.get_stored();
65+
}
66+
4367
TEST_CASE("Single Subinterpreter") {
4468
unsafe_reset_internals_for_single_interpreter();
4569

@@ -324,9 +348,15 @@ TEST_CASE("gil_safe_call_once_and_store per-interpreter isolation") {
324348
.get_stored();
325349
REQUIRE(main_value.cast<py::interpid_t>() == main_interp_id);
326350

351+
py::object dict_type = get_dict_type_object();
352+
py::object ordered_dict_type = get_ordered_dict_type_object();
353+
py::object default_dict_type = get_default_dict_type_object();
354+
327355
py::interpid_t sub_interp_id = -1;
328356
py::interpid_t sub_cached_value = -1;
329357

358+
bool sub_default_dict_type_destroyed = false;
359+
330360
// Create a subinterpreter and check that it gets its own storage
331361
{
332362
py::scoped_subinterpreter ssi;
@@ -351,12 +381,38 @@ TEST_CASE("gil_safe_call_once_and_store per-interpreter isolation") {
351381
// This would fail without per-interpreter storage.
352382
REQUIRE(sub_cached_value == sub_interp_id);
353383
REQUIRE(sub_cached_value != main_interp_id);
384+
385+
py::object sub_dict_type = get_dict_type_object();
386+
py::object sub_ordered_dict_type = get_ordered_dict_type_object();
387+
py::object sub_default_dict_type = get_default_dict_type_object();
388+
389+
REQUIRE(sub_dict_type.is(dict_type)); // dict is a static type
390+
REQUIRE(sub_ordered_dict_type.is(ordered_dict_type)); // OrderedDict is a static type
391+
REQUIRE_FALSE(sub_default_dict_type.is(default_dict_type)); // defaultdict is a heap type
392+
393+
(void) py::weakref(sub_default_dict_type,
394+
py::cpp_function([&](py::handle weakref) -> void {
395+
sub_default_dict_type_destroyed = true;
396+
weakref.dec_ref();
397+
}))
398+
.release();
354399
}
355400

356401
// Back in main interpreter, verify main's value is unchanged
357402
auto &main_value_after = storage.get_stored();
358403
REQUIRE(main_value_after.cast<py::interpid_t>() == main_interp_id);
359404

405+
// Verify that the types cached in main are unchanged
406+
py::object dict_type_after = get_dict_type_object();
407+
py::object ordered_dict_type_after = get_ordered_dict_type_object();
408+
py::object default_dict_type_after = get_default_dict_type_object();
409+
REQUIRE(dict_type_after.is(dict_type));
410+
REQUIRE(ordered_dict_type_after.is(ordered_dict_type));
411+
REQUIRE(default_dict_type_after.is(default_dict_type));
412+
413+
// Verify that the subinterpreter's cached default_dict_type was destroyed
414+
REQUIRE(sub_default_dict_type_destroyed);
415+
360416
unsafe_reset_internals_for_single_interpreter();
361417
}
362418

0 commit comments

Comments
 (0)