/src/cpython/Python/critical_section.c
Line | Count | Source |
1 | | #include "Python.h" |
2 | | |
3 | | #include "pycore_critical_section.h" |
4 | | #include "pycore_interp.h" |
5 | | #include "pycore_lock.h" |
6 | | |
7 | | #ifdef Py_GIL_DISABLED |
8 | | static_assert(_Alignof(PyCriticalSection) >= 4, |
9 | | "critical section must be aligned to at least 4 bytes"); |
10 | | #endif |
11 | | |
12 | | #ifdef Py_GIL_DISABLED |
13 | | static PyCriticalSection * |
14 | | untag_critical_section(uintptr_t tag) |
15 | | { |
16 | | return (PyCriticalSection *)(tag & ~_Py_CRITICAL_SECTION_MASK); |
17 | | } |
18 | | #endif |
19 | | |
20 | | void |
21 | | _PyCriticalSection_BeginSlow(PyThreadState *tstate, PyCriticalSection *c, PyMutex *m) |
22 | 0 | { |
23 | | #ifdef Py_GIL_DISABLED |
24 | | // As an optimisation for locking the same object recursively, skip |
25 | | // locking if the mutex is currently locked by the top-most critical |
26 | | // section. |
27 | | // If the top-most critical section is a two-mutex critical section, |
28 | | // then locking is skipped if either mutex is m. |
29 | | if (tstate->critical_section) { |
30 | | PyCriticalSection *prev = untag_critical_section(tstate->critical_section); |
31 | | if (prev->_cs_mutex == m) { |
32 | | c->_cs_mutex = NULL; |
33 | | c->_cs_prev = 0; |
34 | | return; |
35 | | } |
36 | | if (tstate->critical_section & _Py_CRITICAL_SECTION_TWO_MUTEXES) { |
37 | | PyCriticalSection2 *prev2 = (PyCriticalSection2 *) |
38 | | untag_critical_section(tstate->critical_section); |
39 | | if (prev2->_cs_mutex2 == m) { |
40 | | c->_cs_mutex = NULL; |
41 | | c->_cs_prev = 0; |
42 | | return; |
43 | | } |
44 | | } |
45 | | } |
46 | | // If the world is stopped, we don't need to acquire the lock because |
47 | | // there are no other threads that could be accessing the object. |
48 | | // Without this check, acquiring a critical section while the world is |
49 | | // stopped could lead to a deadlock. |
50 | | if (tstate->interp->stoptheworld.world_stopped) { |
51 | | c->_cs_mutex = NULL; |
52 | | c->_cs_prev = 0; |
53 | | return; |
54 | | } |
55 | | c->_cs_mutex = NULL; |
56 | | c->_cs_prev = (uintptr_t)tstate->critical_section; |
57 | | tstate->critical_section = (uintptr_t)c; |
58 | | |
59 | | PyMutex_Lock(m); |
60 | | c->_cs_mutex = m; |
61 | | #endif |
62 | 0 | } |
63 | | |
64 | | void |
65 | | _PyCriticalSection2_BeginSlow(PyThreadState *tstate, PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2, |
66 | | int is_m1_locked) |
67 | 0 | { |
68 | | #ifdef Py_GIL_DISABLED |
69 | | if (tstate->interp->stoptheworld.world_stopped) { |
70 | | c->_cs_base._cs_mutex = NULL; |
71 | | c->_cs_mutex2 = NULL; |
72 | | c->_cs_base._cs_prev = 0; |
73 | | return; |
74 | | } |
75 | | // Same optimization as in _PyCriticalSection_BeginSlow: skip locking when |
76 | | // recursively acquiring the same locks. |
77 | | if (tstate->critical_section && |
78 | | tstate->critical_section & _Py_CRITICAL_SECTION_TWO_MUTEXES) { |
79 | | PyCriticalSection2 *prev2 = (PyCriticalSection2 *) |
80 | | untag_critical_section(tstate->critical_section); |
81 | | assert((uintptr_t)m1 < (uintptr_t)m2); |
82 | | assert((uintptr_t)prev2->_cs_base._cs_mutex < |
83 | | (uintptr_t)prev2->_cs_mutex2); |
84 | | if (prev2->_cs_base._cs_mutex == m1 && prev2->_cs_mutex2 == m2) { |
85 | | c->_cs_base._cs_mutex = NULL; |
86 | | c->_cs_mutex2 = NULL; |
87 | | c->_cs_base._cs_prev = 0; |
88 | | return; |
89 | | } |
90 | | } |
91 | | c->_cs_base._cs_mutex = NULL; |
92 | | c->_cs_mutex2 = NULL; |
93 | | c->_cs_base._cs_prev = tstate->critical_section; |
94 | | tstate->critical_section = (uintptr_t)c | _Py_CRITICAL_SECTION_TWO_MUTEXES; |
95 | | |
96 | | if (!is_m1_locked) { |
97 | | PyMutex_Lock(m1); |
98 | | } |
99 | | PyMutex_Lock(m2); |
100 | | c->_cs_base._cs_mutex = m1; |
101 | | c->_cs_mutex2 = m2; |
102 | | #endif |
103 | 0 | } |
104 | | |
105 | | |
106 | | // Release all locks held by critical sections. This is called by |
107 | | // _PyThreadState_Detach. |
108 | | void |
109 | | _PyCriticalSection_SuspendAll(PyThreadState *tstate) |
110 | 0 | { |
111 | | #ifdef Py_GIL_DISABLED |
112 | | uintptr_t *tagptr = &tstate->critical_section; |
113 | | while (_PyCriticalSection_IsActive(*tagptr)) { |
114 | | PyCriticalSection *c = untag_critical_section(*tagptr); |
115 | | |
116 | | if (c->_cs_mutex) { |
117 | | PyMutex_Unlock(c->_cs_mutex); |
118 | | if ((*tagptr & _Py_CRITICAL_SECTION_TWO_MUTEXES)) { |
119 | | PyCriticalSection2 *c2 = (PyCriticalSection2 *)c; |
120 | | if (c2->_cs_mutex2) { |
121 | | PyMutex_Unlock(c2->_cs_mutex2); |
122 | | } |
123 | | } |
124 | | } |
125 | | |
126 | | *tagptr |= _Py_CRITICAL_SECTION_INACTIVE; |
127 | | tagptr = &c->_cs_prev; |
128 | | } |
129 | | #endif |
130 | 0 | } |
131 | | |
132 | | void |
133 | | _PyCriticalSection_Resume(PyThreadState *tstate) |
134 | 0 | { |
135 | | #ifdef Py_GIL_DISABLED |
136 | | uintptr_t p = tstate->critical_section; |
137 | | PyCriticalSection *c = untag_critical_section(p); |
138 | | assert(!_PyCriticalSection_IsActive(p)); |
139 | | |
140 | | PyMutex *m1 = c->_cs_mutex; |
141 | | c->_cs_mutex = NULL; |
142 | | |
143 | | PyMutex *m2 = NULL; |
144 | | PyCriticalSection2 *c2 = NULL; |
145 | | if ((p & _Py_CRITICAL_SECTION_TWO_MUTEXES)) { |
146 | | c2 = (PyCriticalSection2 *)c; |
147 | | m2 = c2->_cs_mutex2; |
148 | | c2->_cs_mutex2 = NULL; |
149 | | } |
150 | | |
151 | | if (m1) { |
152 | | PyMutex_Lock(m1); |
153 | | } |
154 | | if (m2) { |
155 | | PyMutex_Lock(m2); |
156 | | } |
157 | | |
158 | | c->_cs_mutex = m1; |
159 | | if (m2) { |
160 | | c2->_cs_mutex2 = m2; |
161 | | } |
162 | | |
163 | | tstate->critical_section &= ~_Py_CRITICAL_SECTION_INACTIVE; |
164 | | #endif |
165 | 0 | } |
166 | | |
167 | | #undef PyCriticalSection_Begin |
168 | | void |
169 | | PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op) |
170 | 0 | { |
171 | | #ifdef Py_GIL_DISABLED |
172 | | _PyCriticalSection_Begin(_PyThreadState_GET(), c, op); |
173 | | #endif |
174 | 0 | } |
175 | | |
176 | | #undef PyCriticalSection_BeginMutex |
177 | | void |
178 | | PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m) |
179 | 0 | { |
180 | | #ifdef Py_GIL_DISABLED |
181 | | _PyCriticalSection_BeginMutex(_PyThreadState_GET(), c, m); |
182 | | #endif |
183 | 0 | } |
184 | | |
185 | | #undef PyCriticalSection_End |
186 | | void |
187 | | PyCriticalSection_End(PyCriticalSection *c) |
188 | 0 | { |
189 | | #ifdef Py_GIL_DISABLED |
190 | | _PyCriticalSection_End(_PyThreadState_GET(), c); |
191 | | #endif |
192 | 0 | } |
193 | | |
194 | | #undef PyCriticalSection2_Begin |
195 | | void |
196 | | PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b) |
197 | 0 | { |
198 | | #ifdef Py_GIL_DISABLED |
199 | | _PyCriticalSection2_Begin(_PyThreadState_GET(), c, a, b); |
200 | | #endif |
201 | 0 | } |
202 | | |
203 | | #undef PyCriticalSection2_BeginMutex |
204 | | void |
205 | | PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2) |
206 | 0 | { |
207 | | #ifdef Py_GIL_DISABLED |
208 | | _PyCriticalSection2_BeginMutex(_PyThreadState_GET(), c, m1, m2); |
209 | | #endif |
210 | 0 | } |
211 | | |
212 | | #undef PyCriticalSection2_End |
213 | | void |
214 | | PyCriticalSection2_End(PyCriticalSection2 *c) |
215 | 0 | { |
216 | | #ifdef Py_GIL_DISABLED |
217 | | _PyCriticalSection2_End(_PyThreadState_GET(), c); |
218 | | #endif |
219 | 0 | } |