112391Sjason@lowepower.com/*
212391Sjason@lowepower.com    tests/test_call_policies.cpp -- keep_alive and call_guard
312391Sjason@lowepower.com
412391Sjason@lowepower.com    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
512391Sjason@lowepower.com
612391Sjason@lowepower.com    All rights reserved. Use of this source code is governed by a
712391Sjason@lowepower.com    BSD-style license that can be found in the LICENSE file.
812391Sjason@lowepower.com*/
912391Sjason@lowepower.com
1012391Sjason@lowepower.com#include "pybind11_tests.h"
1112391Sjason@lowepower.com
1212391Sjason@lowepower.comstruct CustomGuard {
1312391Sjason@lowepower.com    static bool enabled;
1412391Sjason@lowepower.com
1512391Sjason@lowepower.com    CustomGuard() { enabled = true; }
1612391Sjason@lowepower.com    ~CustomGuard() { enabled = false; }
1712391Sjason@lowepower.com
1812391Sjason@lowepower.com    static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
1912391Sjason@lowepower.com};
2012391Sjason@lowepower.combool CustomGuard::enabled = false;
2112391Sjason@lowepower.com
2212391Sjason@lowepower.comstruct DependentGuard {
2312391Sjason@lowepower.com    static bool enabled;
2412391Sjason@lowepower.com
2512391Sjason@lowepower.com    DependentGuard() { enabled = CustomGuard::enabled; }
2612391Sjason@lowepower.com    ~DependentGuard() { enabled = false; }
2712391Sjason@lowepower.com
2812391Sjason@lowepower.com    static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
2912391Sjason@lowepower.com};
3012391Sjason@lowepower.combool DependentGuard::enabled = false;
3112391Sjason@lowepower.com
3212391Sjason@lowepower.comTEST_SUBMODULE(call_policies, m) {
3312391Sjason@lowepower.com    // Parent/Child are used in:
3412391Sjason@lowepower.com    // test_keep_alive_argument, test_keep_alive_return_value, test_alive_gc_derived,
3512391Sjason@lowepower.com    // test_alive_gc_multi_derived, test_return_none, test_keep_alive_constructor
3612391Sjason@lowepower.com    class Child {
3712391Sjason@lowepower.com    public:
3812391Sjason@lowepower.com        Child() { py::print("Allocating child."); }
3914299Sbbruce@ucdavis.edu        Child(const Child &) = default;
4014299Sbbruce@ucdavis.edu        Child(Child &&) = default;
4112391Sjason@lowepower.com        ~Child() { py::print("Releasing child."); }
4212391Sjason@lowepower.com    };
4312391Sjason@lowepower.com    py::class_<Child>(m, "Child")
4412391Sjason@lowepower.com        .def(py::init<>());
4512391Sjason@lowepower.com
4612391Sjason@lowepower.com    class Parent {
4712391Sjason@lowepower.com    public:
4812391Sjason@lowepower.com        Parent() { py::print("Allocating parent."); }
4912391Sjason@lowepower.com        ~Parent() { py::print("Releasing parent."); }
5012391Sjason@lowepower.com        void addChild(Child *) { }
5112391Sjason@lowepower.com        Child *returnChild() { return new Child(); }
5212391Sjason@lowepower.com        Child *returnNullChild() { return nullptr; }
5312391Sjason@lowepower.com    };
5412391Sjason@lowepower.com    py::class_<Parent>(m, "Parent")
5512391Sjason@lowepower.com        .def(py::init<>())
5612391Sjason@lowepower.com        .def(py::init([](Child *) { return new Parent(); }), py::keep_alive<1, 2>())
5712391Sjason@lowepower.com        .def("addChild", &Parent::addChild)
5812391Sjason@lowepower.com        .def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
5912391Sjason@lowepower.com        .def("returnChild", &Parent::returnChild)
6012391Sjason@lowepower.com        .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
6112391Sjason@lowepower.com        .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
6212391Sjason@lowepower.com        .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>());
6312391Sjason@lowepower.com
6412391Sjason@lowepower.com#if !defined(PYPY_VERSION)
6512391Sjason@lowepower.com    // test_alive_gc
6612391Sjason@lowepower.com    class ParentGC : public Parent {
6712391Sjason@lowepower.com    public:
6812391Sjason@lowepower.com        using Parent::Parent;
6912391Sjason@lowepower.com    };
7012391Sjason@lowepower.com    py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr())
7112391Sjason@lowepower.com        .def(py::init<>());
7212391Sjason@lowepower.com#endif
7312391Sjason@lowepower.com
7412391Sjason@lowepower.com    // test_call_guard
7512391Sjason@lowepower.com    m.def("unguarded_call", &CustomGuard::report_status);
7612391Sjason@lowepower.com    m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
7712391Sjason@lowepower.com
7812391Sjason@lowepower.com    m.def("multiple_guards_correct_order", []() {
7912391Sjason@lowepower.com        return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status();
8012391Sjason@lowepower.com    }, py::call_guard<CustomGuard, DependentGuard>());
8112391Sjason@lowepower.com
8212391Sjason@lowepower.com    m.def("multiple_guards_wrong_order", []() {
8312391Sjason@lowepower.com        return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status();
8412391Sjason@lowepower.com    }, py::call_guard<DependentGuard, CustomGuard>());
8512391Sjason@lowepower.com
8612391Sjason@lowepower.com#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
8712391Sjason@lowepower.com    // `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
8812391Sjason@lowepower.com    // but it's unclear how to test it without `PyGILState_GetThisThreadState`.
8912391Sjason@lowepower.com    auto report_gil_status = []() {
9012391Sjason@lowepower.com        auto is_gil_held = false;
9112391Sjason@lowepower.com        if (auto tstate = py::detail::get_thread_state_unchecked())
9212391Sjason@lowepower.com            is_gil_held = (tstate == PyGILState_GetThisThreadState());
9312391Sjason@lowepower.com
9412391Sjason@lowepower.com        return is_gil_held ? "GIL held" : "GIL released";
9512391Sjason@lowepower.com    };
9612391Sjason@lowepower.com
9712391Sjason@lowepower.com    m.def("with_gil", report_gil_status);
9812391Sjason@lowepower.com    m.def("without_gil", report_gil_status, py::call_guard<py::gil_scoped_release>());
9912391Sjason@lowepower.com#endif
10012391Sjason@lowepower.com}
101