Skip to content

Commit 0e94835

Browse files
avoid lock in tracemalloc in general case
1 parent 3960878 commit 0e94835

File tree

2 files changed

+27
-21
lines changed

2 files changed

+27
-21
lines changed

Include/internal/pycore_tracemalloc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ struct _PyTraceMalloc_Config {
2121
} initialized;
2222

2323
/* Is tracemalloc tracing memory allocations?
24-
Variable protected by the TABLES_LOCK(). */
24+
Variable protected by the TABLES_LOCK() and stored atomically.
25+
Atomic store is used so that it can read without locking for the
26+
general case of checking if tracemalloc is enabled.
27+
*/
2528
int tracing;
2629

2730
/* limit of the number of frames in a traceback, 1 by default.

Python/tracemalloc.c

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ _PyTraceMalloc_Start(int max_nframe)
850850

851851
/* everything is ready: start tracing Python memory allocations */
852852
TABLES_LOCK();
853-
tracemalloc_config.tracing = 1;
853+
_Py_atomic_store_int_relaxed(&tracemalloc_config.tracing, 1);
854854
TABLES_UNLOCK();
855855

856856
return 0;
@@ -867,7 +867,7 @@ _PyTraceMalloc_Stop(void)
867867
}
868868

869869
/* stop tracing Python memory allocations */
870-
tracemalloc_config.tracing = 0;
870+
_Py_atomic_store_int_relaxed(&tracemalloc_config.tracing, 0);
871871

872872
/* unregister the hook on memory allocators */
873873
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
@@ -1207,18 +1207,20 @@ int
12071207
PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
12081208
size_t size)
12091209
{
1210+
if (_Py_atomic_load_int_relaxed(&tracemalloc_config.tracing) == 0) {
1211+
/* tracemalloc is not tracing: do nothing */
1212+
return -2;
1213+
}
12101214
PyGILState_STATE gil_state = PyGILState_Ensure();
12111215
TABLES_LOCK();
1212-
1213-
int result;
1214-
if (tracemalloc_config.tracing) {
1215-
result = tracemalloc_add_trace_unlocked(domain, ptr, size);
1216-
}
1217-
else {
1216+
// Check again now that we hold the lock
1217+
if (!tracemalloc_config.tracing) {
1218+
TABLES_UNLOCK();
1219+
PyGILState_Release(gil_state);
12181220
/* tracemalloc is not tracing: do nothing */
1219-
result = -2;
1221+
return -2;
12201222
}
1221-
1223+
int result = tracemalloc_add_trace_unlocked(domain, ptr, size);
12221224
TABLES_UNLOCK();
12231225
PyGILState_Release(gil_state);
12241226
return result;
@@ -1228,20 +1230,21 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
12281230
int
12291231
PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
12301232
{
1231-
TABLES_LOCK();
1232-
1233-
int result;
1234-
if (tracemalloc_config.tracing) {
1235-
tracemalloc_remove_trace_unlocked(domain, ptr);
1236-
result = 0;
1237-
}
1238-
else {
1233+
if (_Py_atomic_load_int_relaxed(&tracemalloc_config.tracing) == 0) {
12391234
/* tracemalloc is not tracing: do nothing */
1240-
result = -2;
1235+
return -2;
12411236
}
12421237

1238+
TABLES_LOCK();
1239+
// Check again now that we hold the lock
1240+
if (!tracemalloc_config.tracing) {
1241+
TABLES_UNLOCK();
1242+
/* tracemalloc is not tracing: do nothing */
1243+
return -2;
1244+
}
1245+
tracemalloc_remove_trace_unlocked(domain, ptr);
12431246
TABLES_UNLOCK();
1244-
return result;
1247+
return 0;
12451248
}
12461249

12471250

0 commit comments

Comments
 (0)