@@ -36,7 +36,7 @@ class BoolWrapper {
3636 void set (bool value) { value_.store (value, std::memory_order_release); }
3737
3838private:
39- std::atomic< bool > value_{false };
39+ std::atomic_bool value_{false };
4040};
4141
4242#if defined(PYBIND11_HAS_BARRIER)
@@ -47,22 +47,40 @@ void test_scoped_critical_section(const py::handle &cls) {
4747 bool output = false ;
4848
4949 {
50+ // Release the GIL to allow run threads in parallel.
5051 py::gil_scoped_release gil_release{};
5152
5253 std::thread t1 ([&]() {
54+ // Use gil_scoped_acquire to ensure we have a valid Python thread state
55+ // before entering the critical section. Otherwise, the critical section
56+ // will cause a segmentation fault.
5357 py::gil_scoped_acquire ensure_tstate{};
58+ // Enter the critical section with the same object as the second thread.
5459 py::scoped_critical_section lock{bool_wrapper};
60+ // At this point, the object is locked by this thread via the scoped_critical_section.
61+ // This barrier will ensure that the second thread waits until this thread has released
62+ // the critical section before proceeding.
5563 barrier.arrive_and_wait ();
56- auto *bw = bool_wrapper.cast <BoolWrapper *>();
64+ // Sleep for a short time to simulate some work in the critical section.
65+ // This sleep is necessary to test the locking mechanism properly.
5766 std::this_thread::sleep_for (std::chrono::milliseconds (10 ));
67+ auto *bw = bool_wrapper.cast <BoolWrapper *>();
5868 bw->set (true );
5969 });
6070
6171 std::thread t2 ([&]() {
72+ // This thread will wait until the first thread has entered the critical section due to
73+ // the barrier.
6274 barrier.arrive_and_wait ();
6375 {
76+ // Use gil_scoped_acquire to ensure we have a valid Python thread state
77+ // before entering the critical section. Otherwise, the critical section
78+ // will cause a segmentation fault.
6479 py::gil_scoped_acquire ensure_tstate{};
80+ // Enter the critical section with the same object as the first thread.
6581 py::scoped_critical_section lock{bool_wrapper};
82+ // At this point, the critical section is released by the first thread, the value
83+ // is set to true.
6684 auto *bw = bool_wrapper.cast <BoolWrapper *>();
6785 output = bw->get ();
6886 }
@@ -84,12 +102,23 @@ void test_scoped_critical_section2(const py::handle &cls) {
84102 std::pair<bool , bool > output{false , false };
85103
86104 {
105+ // Release the GIL to allow run threads in parallel.
87106 py::gil_scoped_release gil_release{};
88107
89108 std::thread t1 ([&]() {
109+ // Use gil_scoped_acquire to ensure we have a valid Python thread state
110+ // before entering the critical section. Otherwise, the critical section
111+ // will cause a segmentation fault.
90112 py::gil_scoped_acquire ensure_tstate{};
113+ // Enter the critical section with two different objects.
114+ // This will ensure that the critical section is locked for both objects.
91115 py::scoped_critical_section lock{bool_wrapper1, bool_wrapper2};
116+ // At this point, objects is locked by this thread via the scoped_critical_section.
117+ // This barrier will ensure that other threads wait until this thread has released
118+ // the critical section before proceeding.
92119 barrier.arrive_and_wait ();
120+ // Sleep for a short time to simulate some work in the critical section.
121+ // This sleep is necessary to test the locking mechanism properly.
93122 std::this_thread::sleep_for (std::chrono::milliseconds (10 ));
94123 auto *bw1 = bool_wrapper1.cast <BoolWrapper *>();
95124 auto *bw2 = bool_wrapper2.cast <BoolWrapper *>();
@@ -98,20 +127,36 @@ void test_scoped_critical_section2(const py::handle &cls) {
98127 });
99128
100129 std::thread t2 ([&]() {
130+ // This thread will wait until the first thread has entered the critical section due to
131+ // the barrier.
101132 barrier.arrive_and_wait ();
102133 {
134+ // Use gil_scoped_acquire to ensure we have a valid Python thread state
135+ // before entering the critical section. Otherwise, the critical section
136+ // will cause a segmentation fault.
103137 py::gil_scoped_acquire ensure_tstate{};
138+ // Enter the critical section with the same object as the first thread.
104139 py::scoped_critical_section lock{bool_wrapper1};
140+ // At this point, the critical section is released by the first thread, the value
141+ // is set to true.
105142 auto *bw1 = bool_wrapper1.cast <BoolWrapper *>();
106143 output.first = bw1->get ();
107144 }
108145 });
109146
110147 std::thread t3 ([&]() {
148+ // This thread will wait until the first thread has entered the critical section due to
149+ // the barrier.
111150 barrier.arrive_and_wait ();
112151 {
152+ // Use gil_scoped_acquire to ensure we have a valid Python thread state
153+ // before entering the critical section. Otherwise, the critical section
154+ // will cause a segmentation fault.
113155 py::gil_scoped_acquire ensure_tstate{};
156+ // Enter the critical section with the same object as the first thread.
114157 py::scoped_critical_section lock{bool_wrapper2};
158+ // At this point, the critical section is released by the first thread, the value
159+ // is set to true.
115160 auto *bw2 = bool_wrapper2.cast <BoolWrapper *>();
116161 output.second = bw2->get ();
117162 }
@@ -134,22 +179,40 @@ void test_scoped_critical_section2_same_object_no_deadlock(const py::handle &cls
134179 bool output = false ;
135180
136181 {
182+ // Release the GIL to allow run threads in parallel.
137183 py::gil_scoped_release gil_release{};
138184
139185 std::thread t1 ([&]() {
186+ // Use gil_scoped_acquire to ensure we have a valid Python thread state
187+ // before entering the critical section. Otherwise, the critical section
188+ // will cause a segmentation fault.
140189 py::gil_scoped_acquire ensure_tstate{};
141- py::scoped_critical_section lock{bool_wrapper, bool_wrapper};
190+ // Enter the critical section with the same object as the second thread.
191+ py::scoped_critical_section lock{bool_wrapper, bool_wrapper}; // same object used here
192+ // At this point, the object is locked by this thread via the scoped_critical_section.
193+ // This barrier will ensure that the second thread waits until this thread has released
194+ // the critical section before proceeding.
142195 barrier.arrive_and_wait ();
196+ // Sleep for a short time to simulate some work in the critical section.
197+ // This sleep is necessary to test the locking mechanism properly.
143198 std::this_thread::sleep_for (std::chrono::milliseconds (10 ));
144199 auto *bw = bool_wrapper.cast <BoolWrapper *>();
145200 bw->set (true );
146201 });
147202
148203 std::thread t2 ([&]() {
204+ // This thread will wait until the first thread has entered the critical section due to
205+ // the barrier.
149206 barrier.arrive_and_wait ();
150207 {
208+ // Use gil_scoped_acquire to ensure we have a valid Python thread state
209+ // before entering the critical section. Otherwise, the critical section
210+ // will cause a segmentation fault.
151211 py::gil_scoped_acquire ensure_tstate{};
212+ // Enter the critical section with the same object as the first thread.
152213 py::scoped_critical_section lock{bool_wrapper};
214+ // At this point, the critical section is released by the first thread, the value
215+ // is set to true.
153216 auto *bw = bool_wrapper.cast <BoolWrapper *>();
154217 output = bw->get ();
155218 }
@@ -190,8 +253,12 @@ TEST_SUBMODULE(scoped_critical_section, m) {
190253 auto BoolWrapperHandle = py::handle (BoolWrapperClass);
191254 (void ) BoolWrapperHandle.ptr (); // suppress unused variable warning
192255
256+ m.attr (" has_barrier" ) =
193257#ifdef PYBIND11_HAS_BARRIER
194- m.attr (" has_barrier" ) = true ;
258+ true ;
259+ #else
260+ false ;
261+ #endif
195262
196263 m.def (" test_scoped_critical_section" ,
197264 [BoolWrapperHandle]() -> void { test_scoped_critical_section (BoolWrapperHandle); });
@@ -200,7 +267,4 @@ TEST_SUBMODULE(scoped_critical_section, m) {
200267 m.def (" test_scoped_critical_section2_same_object_no_deadlock" , [BoolWrapperHandle]() -> void {
201268 test_scoped_critical_section2_same_object_no_deadlock (BoolWrapperHandle);
202269 });
203- #else
204- m.attr (" has_barrier" ) = false ;
205- #endif
206270}
0 commit comments