| 100 | } |
| 101 | |
| 102 | void test_scoped_critical_section2(const py::handle &cls) { |
| 103 | auto barrier = std::barrier(3); |
| 104 | auto bool_wrapper1 = cls(false); |
| 105 | auto bool_wrapper2 = cls(false); |
| 106 | std::pair<bool, bool> output{false, false}; |
| 107 | |
| 108 | { |
| 109 | // Release the GIL to allow run threads in parallel. |
| 110 | py::gil_scoped_release gil_release{}; |
| 111 | |
| 112 | std::thread t1([&]() { |
| 113 | // Use gil_scoped_acquire to ensure we have a valid Python thread state |
| 114 | // before entering the critical section. Otherwise, the critical section |
| 115 | // will cause a segmentation fault. |
| 116 | py::gil_scoped_acquire ensure_tstate{}; |
| 117 | // Enter the critical section with two different objects. |
| 118 | // This will ensure that the critical section is locked for both objects. |
| 119 | py::scoped_critical_section lock{bool_wrapper1, bool_wrapper2}; |
| 120 | // At this point, objects are locked by this thread via the scoped_critical_section. |
| 121 | // This barrier will ensure that other threads wait until this thread has released |
| 122 | // the critical section before proceeding. |
| 123 | barrier.arrive_and_wait(); |
| 124 | // Sleep for a short time to simulate some work in the critical section. |
| 125 | // This sleep is necessary to test the locking mechanism properly. |
| 126 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
| 127 | auto *bw1 = bool_wrapper1.cast<BoolWrapper *>(); |
| 128 | auto *bw2 = bool_wrapper2.cast<BoolWrapper *>(); |
| 129 | bw1->set(true); |
| 130 | bw2->set(true); |
| 131 | }); |
| 132 | |
| 133 | std::thread t2([&]() { |
| 134 | // This thread will wait until the first thread has entered the critical section due to |
| 135 | // the barrier. |
| 136 | barrier.arrive_and_wait(); |
| 137 | { |
| 138 | // Use gil_scoped_acquire to ensure we have a valid Python thread state |
| 139 | // before entering the critical section. Otherwise, the critical section |
| 140 | // will cause a segmentation fault. |
| 141 | py::gil_scoped_acquire ensure_tstate{}; |
| 142 | // Enter the critical section with the same object as the first thread. |
| 143 | py::scoped_critical_section lock{bool_wrapper1}; |
| 144 | // At this point, the critical section is released by the first thread, the value |
| 145 | // is set to true. |
| 146 | auto *bw1 = bool_wrapper1.cast<BoolWrapper *>(); |
| 147 | output.first = bw1->get(); |
| 148 | } |
| 149 | }); |
| 150 | |
| 151 | std::thread t3([&]() { |
| 152 | // This thread will wait until the first thread has entered the critical section due to |
| 153 | // the barrier. |
| 154 | barrier.arrive_and_wait(); |
| 155 | { |
| 156 | // Use gil_scoped_acquire to ensure we have a valid Python thread state |
| 157 | // before entering the critical section. Otherwise, the critical section |
| 158 | // will cause a segmentation fault. |
| 159 | py::gil_scoped_acquire ensure_tstate{}; |
no test coverage detected