Skip to content

Commit 6489d4a

Browse files
committed
maybe leak
1 parent 9dd1405 commit 6489d4a

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-7
lines changed

ddtrace/internal/_threads.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,8 @@ static PyTypeObject PeriodicThreadType = {
516516
// ----------------------------------------------------------------------------
517517
static PyMethodDef _threads_methods[] = {
518518
{ "reset_locks", (PyCFunction)lock_reset_locks, METH_NOARGS, "Reset all locks (generally after a fork)" },
519+
{ "begin_reset_locks", (PyCFunction)lock_begin_reset_locks, METH_NOARGS, "Begin resetting locks (before a fork)" },
520+
{ "end_reset_locks", (PyCFunction)lock_end_reset_locks, METH_NOARGS, "End resetting locks (after a fork)" },
519521
{ NULL, NULL, 0, NULL } /* Sentinel */
520522
};
521523

ddtrace/internal/_threads.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class Lock(_BaseLock): ...
1212
class RLock(_BaseLock): ...
1313

1414
def reset_locks() -> None: ...
15+
def begin_reset_locks() -> None: ...
16+
def end_reset_locks() -> None: ...
1517

1618
class PeriodicThread:
1719
name: str

ddtrace/internal/_threads/lock.hpp

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <mutex>
88
#include <set>
99

10-
std::unique_ptr<std::mutex> _lock_set_mutex = std::make_unique<std::mutex>();
10+
std::mutex _lock_set_mutex;
1111

1212
// ----------------------------------------------------------------------------
1313
// Lock class
@@ -35,14 +35,25 @@ Lock_init(Lock* self, PyObject* args, PyObject* kwargs)
3535
{
3636
AllowThreads _;
3737

38-
std::lock_guard<std::mutex> guard(*_lock_set_mutex);
38+
std::lock_guard<std::mutex> guard(_lock_set_mutex);
3939

4040
lock_set.insert(self);
4141
}
4242

4343
return 0;
4444
}
4545

46+
// ----------------------------------------------------------------------------
47+
static inline void
48+
_Lock_maybe_leak(Lock* self)
49+
{
50+
// This function is used to ensure that the mutex is not leaked if it is
51+
// still locked when the lock object is deallocated.
52+
if (self->_locked) {
53+
self->_mutex.release(); // DEV: This releases the unique_ptr, not the mutex!
54+
}
55+
}
56+
4657
// ----------------------------------------------------------------------------
4758
static void
4859
Lock_dealloc(Lock* self)
@@ -51,11 +62,13 @@ Lock_dealloc(Lock* self)
5162
{
5263
AllowThreads _;
5364

54-
std::lock_guard<std::mutex> guard(*_lock_set_mutex);
65+
std::lock_guard<std::mutex> guard(_lock_set_mutex);
5566

5667
lock_set.erase(self);
5768
}
5869

70+
_Lock_maybe_leak(self);
71+
5972
self->_mutex = nullptr;
6073

6174
Py_TYPE(self)->tp_free((PyObject*)self);
@@ -153,6 +166,7 @@ Lock_exit(Lock* self, PyObject* args, PyObject* kwargs)
153166
static inline void
154167
Lock_reset(Lock* self)
155168
{
169+
_Lock_maybe_leak(self);
156170
self->_mutex = std::make_unique<std::timed_mutex>();
157171
self->_locked = 0;
158172
}
@@ -212,26 +226,38 @@ RLock_init(RLock* self, PyObject* args, PyObject* kwargs)
212226
{
213227
AllowThreads _;
214228

215-
std::lock_guard<std::mutex> guard(*_lock_set_mutex);
229+
std::lock_guard<std::mutex> guard(_lock_set_mutex);
216230

217231
rlock_set.insert(self);
218232
}
219233

220234
return 0;
221235
}
222236

237+
// ----------------------------------------------------------------------------
238+
static inline void
239+
_RLock_maybe_leak(RLock* self)
240+
{
241+
// This function is used to ensure that the mutex is not leaked if it is
242+
// still locked when the re-entrant lock object is deallocated.
243+
if (self->_locked) {
244+
self->_mutex.release(); // DEV: This releases the unique_ptr, not the mutex!
245+
}
246+
}
247+
223248
// ----------------------------------------------------------------------------
224249
static void
225250
RLock_dealloc(RLock* self)
226251
{
227252
{
228253
AllowThreads _;
229254

230-
std::lock_guard<std::mutex> guard(*_lock_set_mutex);
255+
std::lock_guard<std::mutex> guard(_lock_set_mutex);
231256

232257
rlock_set.erase(self);
233258
}
234259

260+
_RLock_maybe_leak(self);
235261
self->_mutex = nullptr;
236262

237263
Py_TYPE(self)->tp_free((PyObject*)self);
@@ -329,6 +355,7 @@ RLock_exit(RLock* self, PyObject* args, PyObject* kwargs)
329355
static inline void
330356
RLock_reset(RLock* self)
331357
{
358+
_RLock_maybe_leak(self);
332359
self->_mutex = std::make_unique<std::recursive_timed_mutex>();
333360
self->_locked = 0;
334361
}
@@ -380,8 +407,33 @@ lock_reset_locks(PyObject* Py_UNUSED(self), PyObject* Py_UNUSED(args))
380407
RLock_reset(rlock);
381408
}
382409

383-
// Reset the lock set mutex too!
384-
_lock_set_mutex = std::make_unique<std::mutex>();
410+
_lock_set_mutex.unlock();
411+
412+
Py_RETURN_NONE;
413+
}
414+
415+
// ----------------------------------------------------------------------------
416+
static PyObject*
417+
lock_begin_reset_locks(PyObject* Py_UNUSED(self), PyObject* Py_UNUSED(args))
418+
{
419+
// This function is called before a fork to ensure that the lock set mutex
420+
// is not held by any thread.
421+
{
422+
AllowThreads _;
423+
424+
_lock_set_mutex.lock();
425+
}
426+
427+
Py_RETURN_NONE;
428+
}
429+
430+
// ----------------------------------------------------------------------------
431+
static PyObject*
432+
lock_end_reset_locks(PyObject* Py_UNUSED(self), PyObject* Py_UNUSED(args))
433+
{
434+
// This function is called after a fork to ensure that the lock set mutex
435+
// is released and can be used by the new process.
436+
_lock_set_mutex.unlock();
385437

386438
Py_RETURN_NONE;
387439
}

0 commit comments

Comments
 (0)