8000 Add deadlock detection for ValueClass-es · prometheus/client_python@b0210f5 · GitHub
[go: up one dir, main page]

Skip to content

Commit b0210f5

Browse files
committed
Add deadlock detection for ValueClass-es
Signed-off-by: Przemysław Suliga <mail@suligap.net>
1 parent eb874cf commit b0210f5

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

prometheus_client/values.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
import os
2-
from threading import Lock
2+
from threading import Lock, RLock
33
import warnings
44

55
from .mmap_dict import mmap_key, MmapedDict
66

77

8+
class _WarningRLock:
9+
"""A wrapper around threading.RLock that detects possible deadlocks.
10+
11+
Raises a RuntimeError when it detects attempts to re-enter the critical
12+
section from a single thread.
13+
"""
14+
15+
def __init__(self):
16+
self._rlock = RLock()
17+
self._lock = Lock()
18+
19+
def __enter__(self):
20+
self._rlock.acquire()
21+
if not self._lock.acquire(blocking=False):
22+
self._rlock.release()
23+
raise RuntimeError('Attempt to enter a non reentrant context from a single thread.')
24+
25+
def __exit__(self, exc_type, exc_value, traceback):
26+
self._lock.release()
27+
self._rlock.release()
28+
29+
830
class MutexValue:
931
"""A float protected by a mutex."""
1032

@@ -13,7 +35,7 @@ class MutexValue:
1335
def __init__(self, typ, metric_name, name, labelnames, labelvalues, help_text, **kwargs):
1436
self._value = 0.0
1537
self._exemplar = None
16-
self._lock = Lock()
38+
self._lock = _WarningRLock()
1739

1840
def inc(self, amount):
1941
with self._lock:
@@ -47,10 +69,9 @@ def MultiProcessValue(process_identifier=os.getpid):
4769
files = {}
4870
values = []
4971
pid = {'value': process_identifier()}
50-
# Use a single global lock when in multi-processing mode
51-
# as we presume this means there is no threading going on.
72+
# Use a single global lock when in multi-processing mode.
5273
# This avoids the need to also have mutexes in __MmapDict.
53-
lock = Lock()
74+
lock = _WarningRLock()
5475

5576
class MmapedValue:
5677
"""A float protected by a mutex backed by a per-process mmaped file."""

tests/test_core.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,17 @@ def test_exemplar_too_long(self):
135135
'y123456': '7+15 characters',
136136
})
137137

138+
def test_single_thread_deadlock_detection(self):
139+
counter = self.counter
140+
141+
class Tracked(float):
142+
def __radd__(self, other):
143+
counter.inc(10)
144+
return self + other
145+
146+
expected_msg = 'Attempt to enter a non reentrant context from a single thread.'
147+
self.assertRaisesRegex(RuntimeError, expected_msg, counter.inc, Tracked(100))
148+
138149

139150
class TestDisableCreated(unittest.TestCase):
140151
def setUp(self):

0 commit comments

Comments
 (0)
0