Skip to content

Commit a269d18

Browse files
committed
Add locking to warnings.py.
Expose the mutex from _warnings.c and hold it when mutating the filter state.
1 parent 30efede commit a269d18

File tree

3 files changed

+125
-28
lines changed

3 files changed

+125
-28
lines changed

Lib/warnings.py

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -186,23 +186,25 @@ def simplefilter(action, category=Warning, lineno=0, append=False):
186186
_add_filter(action, None, category, None, lineno, append=append)
187187

188188
def _add_filter(*item, append):
189-
# Remove possible duplicate filters, so new one will be placed
190-
# in correct place. If append=True and duplicate exists, do nothing.
191-
if not append:
192-
try:
193-
filters.remove(item)
194-
except ValueError:
195-
pass
196-
filters.insert(0, item)
197-
else:
198-
if item not in filters:
199-
filters.append(item)
200-
_filters_mutated()
189+
with _lock:
190+
if not append:
191+
# Remove possible duplicate filters, so new one will be placed
192+
# in correct place. If append=True and duplicate exists, do nothing.
193+
try:
194+
filters.remove(item)
195+
except ValueError:
196+
pass
197+
filters.insert(0, item)
198+
else:
199+
if item not in filters:
200+
filters.append(item)
201+
_filters_mutated()
201202

202203
def resetwarnings():
203204
"""Clear the list of warning filters, so that no filters are active."""
204-
filters[:] = []
205-
_filters_mutated()
205+
with _lock:
206+
filters[:] = []
207+
_filters_mutated()
206208

207209
class _OptionError(Exception):
208210
"""Exception used by option processing helpers."""
@@ -488,11 +490,12 @@ def __enter__(self):
488490
if self._entered:
489491
raise RuntimeError("Cannot enter %r twice" % self)
490492
self._entered = True
491-
self._filters = self._module.filters
492-
self._module.filters = self._filters[:]
493-
self._module._filters_mutated()
494-
self._showwarning = self._module.showwarning
495-
self._showwarnmsg_impl = self._module._showwarnmsg_impl
493+
with _lock:
494+
self._filters = self._module.filters
495+
self._module.filters = self._filters[:]
496+
self._module._filters_mutated()
497+
self._showwarning = self._module.showwarning
498+
self._showwarnmsg_impl = self._module._showwarnmsg_impl
496499
if self._filter is not None:
497500
simplefilter(*self._filter)
498501
if self._record:
@@ -508,10 +511,11 @@ def __enter__(self):
508511
def __exit__(self, *exc_info):
509512
if not self._entered:
510513
raise RuntimeError("Cannot exit %r without entering first" % self)
511-
self._module.filters = self._filters
512-
self._module._filters_mutated()
513-
self._module.showwarning = self._showwarning
514-
self._module._showwarnmsg_impl = self._showwarnmsg_impl
514+
with _lock:
515+
self._module.filters = self._filters
516+
self._module._filters_mutated()
517+
self._module.showwarning = self._showwarning
518+
self._module._showwarnmsg_impl = self._showwarnmsg_impl
515519

516520

517521
class deprecated:
@@ -701,15 +705,31 @@ def extract():
701705
# If either if the compiled regexs are None, match anything.
702706
try:
703707
from _warnings import (filters, _defaultaction, _onceregistry,
704-
warn, warn_explicit, _filters_mutated)
708+
warn, warn_explicit, _filters_mutated,
709+
_acquire_lock, _release_lock,
710+
)
705711
defaultaction = _defaultaction
706712
onceregistry = _onceregistry
707713
_warnings_defaults = True
714+
715+
class _Lock:
716+
def __enter__(self):
717+
_acquire_lock()
718+
return self
719+
720+
def __exit__(self, *args):
721+
_release_lock()
722+
723+
_lock = _Lock()
724+
708725
except ImportError:
709726
filters = []
710727
defaultaction = "default"
711728
onceregistry = {}
712729

730+
import _thread
731+
_lock = _thread.LockType()
732+
713733
_filters_version = 1
714734

715735
def _filters_mutated():

Python/_warnings.c

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,47 @@ get_warnings_attr(PyInterpreterState *interp, PyObject *attr, int try_import)
232232
return obj;
233233
}
234234

235+
/*[clinic input]
236+
_acquire_lock as warnings_acquire_lock
237+
238+
[clinic start generated code]*/
239+
240+
static PyObject *
241+
warnings_acquire_lock_impl(PyObject *module)
242+
/*[clinic end generated code: output=594313457d1bf8e1 input=46ec20e55acca52f]*/
243+
{
244+
PyInterpreterState *interp = get_current_interp();
245+
if (interp == NULL) {
246+
return NULL;
247+
}
248+
249+
WarningsState *st = warnings_get_state(interp);
250+
assert(st != NULL);
251+
252+
_PyMutex_Lock(&st->mutex);
253+
Py_RETURN_NONE;
254+
}
255+
256+
/*[clinic input]
257+
_release_lock as warnings_release_lock
258+
259+
[clinic start generated code]*/
260+
261+
static PyObject *
262+
warnings_release_lock_impl(PyObject *module)
263+
/*[clinic end generated code: output=d73d5a8789396750 input=ea01bb77870c5693]*/
264+
{
265+
PyInterpreterState *interp = get_current_interp();
266+
if (interp == NULL) {
267+
return NULL;
268+
}
269+
270+
WarningsState *st = warnings_get_state(interp);
271+
assert(st != NULL);
272+
273+
_PyMutex_Unlock(&st->mutex);
274+
Py_RETURN_NONE;
275+
}
235276

236277
static PyObject *
237278
get_once_registry(PyInterpreterState *interp)
@@ -1181,14 +1222,14 @@ warnings_filters_mutated_impl(PyObject *module)
11811222
WarningsState *st = warnings_get_state(interp);
11821223
assert(st != NULL);
11831224

1184-
Py_BEGIN_CRITICAL_SECTION_MUT(&st->mutex);
1225+
// Note that the lock must be held by the caller.
1226+
assert(PyMutex_IsLocked(&st->mutex));
1227+
11851228
st->filters_version++;
1186-
Py_END_CRITICAL_SECTION();
11871229

11881230
Py_RETURN_NONE;
11891231
}
11901232

1191-
11921233
/* Function to issue a warning message; may raise an exception. */
11931234

11941235
static int
@@ -1465,6 +1506,8 @@ static PyMethodDef warnings_functions[] = {
14651506
WARNINGS_WARN_METHODDEF
14661507
WARNINGS_WARN_EXPLICIT_METHODDEF
14671508
WARNINGS_FILTERS_MUTATED_METHODDEF
1509+
WARNINGS_ACQUIRE_LOCK_METHODDEF
1510+
WARNINGS_RELEASE_LOCK_METHODDEF
14681511
/* XXX(brett.cannon): add showwarning? */
14691512
/* XXX(brett.cannon): Reasonable to add formatwarning? */
14701513
{NULL, NULL} /* sentinel */

Python/clinic/_warnings.c.h

Lines changed: 35 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)