You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
3.3 KiB

2 months ago
  1. /*
  2. tests/test_call_policies.cpp -- keep_alive and call_guard
  3. Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
  4. All rights reserved. Use of this source code is governed by a
  5. BSD-style license that can be found in the LICENSE file.
  6. */
  7. #include "pybind11_tests.h"
  8. class Child {
  9. public:
  10. Child() { py::print("Allocating child."); }
  11. ~Child() { py::print("Releasing child."); }
  12. };
  13. class Parent {
  14. public:
  15. Parent() { py::print("Allocating parent."); }
  16. ~Parent() { py::print("Releasing parent."); }
  17. void addChild(Child *) { }
  18. Child *returnChild() { return new Child(); }
  19. Child *returnNullChild() { return nullptr; }
  20. };
  21. #if !defined(PYPY_VERSION)
  22. class ParentGC : public Parent {
  23. public:
  24. using Parent::Parent;
  25. };
  26. #endif
  27. test_initializer keep_alive([](py::module &m) {
  28. py::class_<Parent>(m, "Parent")
  29. .def(py::init<>())
  30. .def("addChild", &Parent::addChild)
  31. .def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>())
  32. .def("returnChild", &Parent::returnChild)
  33. .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
  34. .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
  35. .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>());
  36. #if !defined(PYPY_VERSION)
  37. py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr())
  38. .def(py::init<>());
  39. #endif
  40. py::class_<Child>(m, "Child")
  41. .def(py::init<>());
  42. });
  43. struct CustomGuard {
  44. static bool enabled;
  45. CustomGuard() { enabled = true; }
  46. ~CustomGuard() { enabled = false; }
  47. static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
  48. };
  49. bool CustomGuard::enabled = false;
  50. struct DependentGuard {
  51. static bool enabled;
  52. DependentGuard() { enabled = CustomGuard::enabled; }
  53. ~DependentGuard() { enabled = false; }
  54. static const char *report_status() { return enabled ? "guarded" : "unguarded"; }
  55. };
  56. bool DependentGuard::enabled = false;
  57. test_initializer call_guard([](py::module &pm) {
  58. auto m = pm.def_submodule("call_policies");
  59. m.def("unguarded_call", &CustomGuard::report_status);
  60. m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
  61. m.def("multiple_guards_correct_order", []() {
  62. return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status();
  63. }, py::call_guard<CustomGuard, DependentGuard>());
  64. m.def("multiple_guards_wrong_order", []() {
  65. return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status();
  66. }, py::call_guard<DependentGuard, CustomGuard>());
  67. #if defined(WITH_THREAD) && !defined(PYPY_VERSION)
  68. // `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
  69. // but it's unclear how to test it without `PyGILState_GetThisThreadState`.
  70. auto report_gil_status = []() {
  71. auto is_gil_held = false;
  72. if (auto tstate = py::detail::get_thread_state_unchecked())
  73. is_gil_held = (tstate == PyGILState_GetThisThreadState());
  74. return is_gil_held ? "GIL held" : "GIL released";
  75. };
  76. m.def("with_gil", report_gil_status);
  77. m.def("without_gil", report_gil_status, py::call_guard<py::gil_scoped_release>());
  78. #endif
  79. });