@@ -284,10 +284,111 @@ test_critical_sections_gc(PyObject *self, PyObject *Py_UNUSED(args))
284284
285285#endif
286286
287+ #ifdef Py_GIL_DISABLED
288+
289+ static PyObject *
290+ test_critical_section1_reacquisition (PyObject * self , PyObject * Py_UNUSED (args ))
291+ {
292+ PyObject * a = PyDict_New ();
293+ assert (a != NULL );
294+
295+ PyCriticalSection cs1 , cs2 ;
296+ // First acquisition of critical section on object locks it
297+ PyCriticalSection_Begin (& cs1 , a );
298+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
299+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
300+ assert (_PyThreadState_GET ()-> critical_section == (uintptr_t )& cs1 );
301+ // Attempting to re-acquire critical section on same object which
302+ // is already locked by top-most critical section is a no-op.
303+ PyCriticalSection_Begin (& cs2 , a );
304+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
305+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
306+ assert (_PyThreadState_GET ()-> critical_section == (uintptr_t )& cs1 );
307+ // Releasing second critical section is a no-op.
308+ PyCriticalSection_End (& cs2 );
309+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
310+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
311+ assert (_PyThreadState_GET ()-> critical_section == (uintptr_t )& cs1 );
312+ // Releasing first critical section unlocks the object
313+ PyCriticalSection_End (& cs1 );
314+ assert (!PyMutex_IsLocked (& a -> ob_mutex ));
315+
316+ Py_DECREF (a );
317+ Py_RETURN_NONE ;
318+ }
319+
320+ static PyObject *
321+ test_critical_section2_reacquisition (PyObject * self , PyObject * Py_UNUSED (args ))
322+ {
323+ PyObject * a = PyDict_New ();
324+ assert (a != NULL );
325+ PyObject * b = PyDict_New ();
326+ assert (b != NULL );
327+
328+ PyCriticalSection2 cs ;
329+ // First acquisition of critical section on objects locks them
330+ PyCriticalSection2_Begin (& cs , a , b );
331+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
332+ assert (PyMutex_IsLocked (& b -> ob_mutex ));
333+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
334+ assert ((_PyThreadState_GET ()-> critical_section &
335+ ~_Py_CRITICAL_SECTION_MASK ) == (uintptr_t )& cs );
336+
337+ // Attempting to re-acquire critical section on either of two
338+ // objects already locked by top-most critical section is a no-op.
339+
340+ // Check re-acquiring on first object
341+ PyCriticalSection a_cs ;
342+ PyCriticalSection_Begin (& a_cs , a );
343+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
344+ assert (PyMutex_IsLocked (& b -> ob_mutex ));
345+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
346+ assert ((_PyThreadState_GET ()-> critical_section &
347+ ~_Py_CRITICAL_SECTION_MASK ) == (uintptr_t )& cs );
348+ // Releasing critical section on either object is a no-op.
349+ PyCriticalSection_End (& a_cs );
350+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
351+ assert (PyMutex_IsLocked (& b -> ob_mutex ));
352+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
353+ assert ((_PyThreadState_GET ()-> critical_section &
354+ ~_Py_CRITICAL_SECTION_MASK ) == (uintptr_t )& cs );
355+
356+ // Check re-acquiring on second object
357+ PyCriticalSection b_cs ;
358+ PyCriticalSection_Begin (& b_cs , b );
359+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
360+ assert (PyMutex_IsLocked (& b -> ob_mutex ));
361+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
362+ assert ((_PyThreadState_GET ()-> critical_section &
363+ ~_Py_CRITICAL_SECTION_MASK ) == (uintptr_t )& cs );
364+ // Releasing critical section on either object is a no-op.
365+ PyCriticalSection_End (& b_cs );
366+ assert (PyMutex_IsLocked (& a -> ob_mutex ));
367+ assert (PyMutex_IsLocked (& b -> ob_mutex ));
368+ assert (_PyCriticalSection_IsActive (PyThreadState_GET ()-> critical_section ));
369+ assert ((_PyThreadState_GET ()-> critical_section &
370+ ~_Py_CRITICAL_SECTION_MASK ) == (uintptr_t )& cs );
371+
372+ // Releasing critical section on both objects unlocks them
373+ PyCriticalSection2_End (& cs );
374+ assert (!PyMutex_IsLocked (& a -> ob_mutex ));
375+ assert (!PyMutex_IsLocked (& b -> ob_mutex ));
376+
377+ Py_DECREF (a );
378+ Py_DECREF (b );
379+ Py_RETURN_NONE ;
380+ }
381+
382+ #endif // Py_GIL_DISABLED
383+
287384static PyMethodDef test_methods [] = {
288385 {"test_critical_sections" , test_critical_sections , METH_NOARGS },
289386 {"test_critical_sections_nest" , test_critical_sections_nest , METH_NOARGS },
290387 {"test_critical_sections_suspend" , test_critical_sections_suspend , METH_NOARGS },
388+ #ifdef Py_GIL_DISABLED
389+ {"test_critical_section1_reacquisition" , test_critical_section1_reacquisition , METH_NOARGS },
390+ {"test_critical_section2_reacquisition" , test_critical_section2_reacquisition , METH_NOARGS },
391+ #endif
291392#ifdef Py_CAN_START_THREADS
292393 {"test_critical_sections_threads" , test_critical_sections_threads , METH_NOARGS },
293394 {"test_critical_sections_gc" , test_critical_sections_gc , METH_NOARGS },
0 commit comments