Skip to content

Commit 11f3bd9

Browse files
feat(profiling): add threading.RLock support to the Lock profiler (re-entrant lock) (#14823)
[PROF-12721] ## Description This PR adds profiling support for a new kind of synchronization mechanism: Reentrant Lock. A RLock is a lock that can be acquired **by the same thread** multiple times without blocking on itself. From the documentation: > Once a thread has acquired a reentrant lock, the same thread may acquire it again without blocking; the thread must release it once for each time it has acquired it. This feature was identified as one of the most - low-hanging-fruits, since `RLock`s are being used extensively in Python world. ### Changes * added new `ThreadingRLockCollector` class in `threading.py` * register the new collector in Profiler in `profiler.py` * current `test_threading.py` module has been updated to reuse existing tests for both `Lock` and `RLock` primitives (via new inheritance) * `test_global_locks` test rewritten extensively to support the unit test code reuse between `Lock` and `RLock` (effectively duplicating the existing extensive `Lock` unit tests for `RLock` ... without the code duplication) ## Testing * added new unit tests * added new e2e test: - Reentrant behavior works for multi-level acquisition - Thread contention handling validated ## Risks none ## Additional Notes * [RLock docs](https://docs.python.org/3/library/threading.html#threading.RLock) [PROF-12721]: https://datadoghq.atlassian.net/browse/PROF-12721?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Taegyun Kim <[email protected]>
1 parent 93aff64 commit 11f3bd9

File tree

6 files changed

+382
-190
lines changed

6 files changed

+382
-190
lines changed

ddtrace/profiling/collector/threading.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class _ProfiledThreadingLock(_lock._ProfiledLock):
1414
pass
1515

1616

17+
class _ProfiledThreadingRLock(_lock._ProfiledLock):
18+
pass
19+
20+
1721
class ThreadingLockCollector(_lock.LockCollector):
1822
"""Record threading.Lock usage."""
1923

@@ -29,6 +33,21 @@ def _set_patch_target(
2933
threading.Lock = value
3034

3135

36+
class ThreadingRLockCollector(_lock.LockCollector):
37+
"""Record threading.RLock usage."""
38+
39+
PROFILED_LOCK_CLASS = _ProfiledThreadingRLock
40+
41+
def _get_patch_target(self) -> typing.Type[threading.RLock]:
42+
return threading.RLock
43+
44+
def _set_patch_target(
45+
self,
46+
value: typing.Any,
47+
) -> None:
48+
threading.RLock = value
49+
50+
3251
# Also patch threading.Thread so echion can track thread lifetimes
3352
def init_stack_v2() -> None:
3453
if config.stack.v2_enabled and stack_v2.is_available:

ddtrace/profiling/profiler.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
from ddtrace.settings.profiling import config_str
3030

3131

32+
# TODO(vlad): add type annotations
33+
3234
LOG = logging.getLogger(__name__)
3335

3436

@@ -223,6 +225,7 @@ def start_collector(collector_class: Type) -> None:
223225

224226
self._collectors_on_import = [
225227
("threading", lambda _: start_collector(threading.ThreadingLockCollector)),
228+
("threading", lambda _: start_collector(threading.ThreadingRLockCollector)),
226229
("asyncio", lambda _: start_collector(asyncio.AsyncioLockCollector)),
227230
]
228231

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- |
4+
profiling: Add support for ``threading.RLock`` (reentrant lock) profiling. The Lock profiler now tracks both ``threading.Lock`` and ``threading.RLock`` usage, providing comprehensive lock contention visibility for Python applications.

tests/profiling/collector/global_locks.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)