Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:52

0001 /*
0002     tests/test_call_policies.cpp -- keep_alive and call_guard
0003 
0004     Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
0005 
0006     All rights reserved. Use of this source code is governed by a
0007     BSD-style license that can be found in the LICENSE file.
0008 */
0009 
0010 #include "pybind11_tests.h"
0011 
0012 struct CustomGuard {
0013     static bool enabled;
0014 
0015     CustomGuard() { enabled = true; }
0016     ~CustomGuard() { enabled = false; }
0017 
0018     static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
0019 };
0020 bool CustomGuard::enabled = false;
0021 
0022 struct DependentGuard {
0023     static bool enabled;
0024 
0025     DependentGuard() { enabled = CustomGuard::enabled; }
0026     ~DependentGuard() { enabled = false; }
0027 
0028     static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
0029 };
0030 bool DependentGuard::enabled = false;
0031 
0032 TEST_SUBMODULE(call_policies, m) {
0033     // Parent/Child are used in:
0034     // test_keep_alive_argument, test_keep_alive_return_value, test_alive_gc_derived,
0035     // test_alive_gc_multi_derived, test_return_none, test_keep_alive_constructor
0036     class Child {
0037     public:
0038         Child() { py::print("Allocating child."); }
0039         Child(const Child &) = default;
0040         Child(Child &&) = default;
0041         ~Child() { py::print("Releasing child."); }
0042     };
0043     py::class_<Child>(m, "Child").def(py::init<>());
0044 
0045     class Parent {
0046     public:
0047         Parent() { py::print("Allocating parent."); }
0048         Parent(const Parent &parent) = default;
0049         ~Parent() { py::print("Releasing parent."); }
0050         void addChild(Child *) {}
0051         Child *returnChild() { return new Child(); }
0052         Child *returnNullChild() { return nullptr; }
0053         static Child *staticFunction(Parent *) { return new Child(); }
0054     };
0055     py::class_<Parent>(m, "Parent")
0056         .def(py::init<>())
0057         .def(py::init([](Child *) { return new Parent(); }), py::keep_alive<1, 2>())
0058         .def("addChild", &Parent::addChild)
0059         .def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
0060         .def("returnChild", &Parent::returnChild)
0061         .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
0062         .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
0063         .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
0064         .def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
0065 
0066     m.def(
0067         "free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>());
0068     m.def(
0069         "invalid_arg_index", [] {}, py::keep_alive<0, 1>());
0070 
0071 #if !defined(PYPY_VERSION)
0072     // test_alive_gc
0073     class ParentGC : public Parent {
0074     public:
0075         using Parent::Parent;
0076     };
0077     py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()).def(py::init<>());
0078 #endif
0079 
0080     // test_call_guard
0081     m.def("unguarded_call", &CustomGuard::report_status);
0082     m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
0083 
0084     m.def(
0085         "multiple_guards_correct_order",
0086         []() {
0087             return CustomGuard::report_status() + std::string(" & ")
0088                    + DependentGuard::report_status();
0089         },
0090         py::call_guard<CustomGuard, DependentGuard>());
0091 
0092     m.def(
0093         "multiple_guards_wrong_order",
0094         []() {
0095             return DependentGuard::report_status() + std::string(" & ")
0096                    + CustomGuard::report_status();
0097         },
0098         py::call_guard<DependentGuard, CustomGuard>());
0099 
0100 #if defined(WITH_THREAD) && !defined(PYPY_VERSION)
0101     // `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
0102     // but it's unclear how to test it without `PyGILState_GetThisThreadState`.
0103     auto report_gil_status = []() {
0104         auto is_gil_held = false;
0105         if (auto *tstate = py::detail::get_thread_state_unchecked()) {
0106             is_gil_held = (tstate == PyGILState_GetThisThreadState());
0107         }
0108 
0109         return is_gil_held ? "GIL held" : "GIL released";
0110     };
0111 
0112     m.def("with_gil", report_gil_status);
0113     m.def("without_gil", report_gil_status, py::call_guard<py::gil_scoped_release>());
0114 #endif
0115 }