aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python
diff options
context:
space:
mode:
Diffstat (limited to 'Python')
-rw-r--r--Python/critical_section.c100
-rw-r--r--Python/pystate.c10
2 files changed, 110 insertions, 0 deletions
diff --git a/Python/critical_section.c b/Python/critical_section.c
new file mode 100644
index 00000000000..2214d80eeb2
--- /dev/null
+++ b/Python/critical_section.c
@@ -0,0 +1,100 @@
+#include "Python.h"
+
+#include "pycore_lock.h"
+#include "pycore_critical_section.h"
+
+static_assert(_Alignof(_PyCriticalSection) >= 4,
+ "critical section must be aligned to at least 4 bytes");
+
+void
+_PyCriticalSection_BeginSlow(_PyCriticalSection *c, PyMutex *m)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ c->mutex = NULL;
+ c->prev = (uintptr_t)tstate->critical_section;
+ tstate->critical_section = (uintptr_t)c;
+
+ _PyMutex_LockSlow(m);
+ c->mutex = m;
+}
+
+void
+_PyCriticalSection2_BeginSlow(_PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2,
+ int is_m1_locked)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ c->base.mutex = NULL;
+ c->mutex2 = NULL;
+ c->base.prev = tstate->critical_section;
+ tstate->critical_section = (uintptr_t)c | _Py_CRITICAL_SECTION_TWO_MUTEXES;
+
+ if (!is_m1_locked) {
+ PyMutex_Lock(m1);
+ }
+ PyMutex_Lock(m2);
+ c->base.mutex = m1;
+ c->mutex2 = m2;
+}
+
+static _PyCriticalSection *
+untag_critical_section(uintptr_t tag)
+{
+ return (_PyCriticalSection *)(tag & ~_Py_CRITICAL_SECTION_MASK);
+}
+
+// Release all locks held by critical sections. This is called by
+// _PyThreadState_Detach.
+void
+_PyCriticalSection_SuspendAll(PyThreadState *tstate)
+{
+ uintptr_t *tagptr = &tstate->critical_section;
+ while (_PyCriticalSection_IsActive(*tagptr)) {
+ _PyCriticalSection *c = untag_critical_section(*tagptr);
+
+ if (c->mutex) {
+ PyMutex_Unlock(c->mutex);
+ if ((*tagptr & _Py_CRITICAL_SECTION_TWO_MUTEXES)) {
+ _PyCriticalSection2 *c2 = (_PyCriticalSection2 *)c;
+ if (c2->mutex2) {
+ PyMutex_Unlock(c2->mutex2);
+ }
+ }
+ }
+
+ *tagptr |= _Py_CRITICAL_SECTION_INACTIVE;
+ tagptr = &c->prev;
+ }
+}
+
+void
+_PyCriticalSection_Resume(PyThreadState *tstate)
+{
+ uintptr_t p = tstate->critical_section;
+ _PyCriticalSection *c = untag_critical_section(p);
+ assert(!_PyCriticalSection_IsActive(p));
+
+ PyMutex *m1 = c->mutex;
+ c->mutex = NULL;
+
+ PyMutex *m2 = NULL;
+ _PyCriticalSection2 *c2 = NULL;
+ if ((p & _Py_CRITICAL_SECTION_TWO_MUTEXES)) {
+ c2 = (_PyCriticalSection2 *)c;
+ m2 = c2->mutex2;
+ c2->mutex2 = NULL;
+ }
+
+ if (m1) {
+ PyMutex_Lock(m1);
+ }
+ if (m2) {
+ PyMutex_Lock(m2);
+ }
+
+ c->mutex = m1;
+ if (m2) {
+ c2->mutex2 = m2;
+ }
+
+ tstate->critical_section &= ~_Py_CRITICAL_SECTION_INACTIVE;
+}
diff --git a/Python/pystate.c b/Python/pystate.c
index b369a56d6d5..991d8d204a1 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -4,6 +4,7 @@
#include "Python.h"
#include "pycore_ceval.h"
#include "pycore_code.h" // stats
+#include "pycore_critical_section.h" // _PyCriticalSection_Resume()
#include "pycore_dtoa.h" // _dtoa_state_INIT()
#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init()
#include "pycore_frame.h"
@@ -1911,6 +1912,12 @@ _PyThreadState_Attach(PyThreadState *tstate)
Py_FatalError("thread attach failed");
}
+ // Resume previous critical section. This acquires the lock(s) from the
+ // top-most critical section.
+ if (tstate->critical_section != 0) {
+ _PyCriticalSection_Resume(tstate);
+ }
+
#if defined(Py_DEBUG)
errno = err;
#endif
@@ -1922,6 +1929,9 @@ _PyThreadState_Detach(PyThreadState *tstate)
// XXX assert(tstate_is_alive(tstate) && tstate_is_bound(tstate));
assert(tstate->state == _Py_THREAD_ATTACHED);
assert(tstate == current_fast_get(&_PyRuntime));
+ if (tstate->critical_section != 0) {
+ _PyCriticalSection_SuspendAll(tstate);
+ }
tstate_set_detached(tstate);
tstate_deactivate(tstate);
current_fast_clear(&_PyRuntime);