@@ -1729,61 +1729,59 @@ void DebuggerController::LaunchOrConnect()
17291729}
17301730
17311731
1732- // Can't use a vector here as initialization order is not guaranteed.
1733- DbgRef<DebuggerController>* DebuggerController::g_debuggerControllers = nullptr ;
1734- size_t DebuggerController::g_controllerCount = 0 ;
1732+ // Use a function-local static to avoid two problems:
1733+ // 1. Static initialization order fiasco -- if any other translation unit's static initializer
1734+ // calls GetController before this TU is initialized, a global would not yet be constructed.
1735+ // 2. Static destruction order -- during process exit, a namespace-scope std::mutex can be
1736+ // destroyed before cleanup code (e.g., Python GC calling Destroy() via FFI) tries to lock it,
1737+ // causing "mutex lock failed: Invalid argument". Bundling the mutex and vector in the same
1738+ // function-local static ensures they share the same lifetime.
1739+ DebuggerController::ControllerState& DebuggerController::GetControllerState ()
1740+ {
1741+ // Intentionally heap-allocated and never freed. A function-local static would still be
1742+ // destroyed during static cleanup, but Python's GC can call Destroy() -> DeleteController()
1743+ // even later than that, hitting a destroyed mutex. Leaking the allocation ensures the mutex
1744+ // and vector remain valid for the entire process lifetime. The OS reclaims the memory at exit.
1745+ static ControllerState* state = new ControllerState ();
1746+ return *state;
1747+ }
17351748
17361749
17371750DbgRef<DebuggerController> DebuggerController::GetController (BinaryViewRef data)
17381751{
1739- for (size_t i = 0 ; i < g_controllerCount; i++)
1752+ auto & state = GetControllerState ();
1753+ std::lock_guard<std::mutex> lock (state.mutex );
1754+ for (auto & c : state.controllers )
17401755 {
1741- DebuggerController* controller = g_debuggerControllers[i];
1742- if (!controller)
1743- continue ;
1744- if (controller->m_file == data->GetFile ())
1745- return controller;
1756+ if (c && c->m_file == data->GetFile ())
1757+ return c;
17461758 }
17471759
17481760 auto controller = new DebuggerController (data);
1749- g_debuggerControllers = (DbgRef<DebuggerController>*)realloc (g_debuggerControllers,
1750- sizeof (DbgRef<DebuggerController>) * (g_controllerCount + 1 ));
1751-
1752- // We must call the DbgRef ctor on the newly allocated space explicitly. If we do a
1753- // g_debuggerControllers[g_controllerCount] = controller;
1754- // The `=` operator on the next line will cause a call to `DbgRef<T>& operator=(T* obj)` on an uninitialized DbgRef
1755- // object, leading to a crash when `DbgRef::m_obj` is de-referenced.
1756- // In fact, this is how std::vector does things inside `push_back`.
1757- new (&g_debuggerControllers[g_controllerCount]) DbgRef<DebuggerController>(controller);
1758- g_controllerCount++;
1761+ state.controllers .emplace_back (controller);
17591762 return controller;
17601763}
17611764
17621765
17631766void DebuggerController::DeleteController (BinaryViewRef data)
17641767{
1765- for (size_t i = 0 ; i < g_controllerCount; i++)
1768+ auto & state = GetControllerState ();
1769+ std::lock_guard<std::mutex> lock (state.mutex );
1770+ for (auto & c : state.controllers )
17661771 {
1767- DbgRef<DebuggerController> controller = g_debuggerControllers[i];
1768- if (!controller)
1769- continue ;
1770-
1771- if (controller->GetFile () == data->GetFile ())
1772- {
1773- g_debuggerControllers[i] = nullptr ;
1774- }
1772+ if (c && c->GetFile () == data->GetFile ())
1773+ c = nullptr ;
17751774 }
17761775}
17771776
17781777
17791778bool DebuggerController::ControllerExists (BinaryViewRef data)
17801779{
1781- for (size_t i = 0 ; i < g_controllerCount; i++)
1780+ auto & state = GetControllerState ();
1781+ std::lock_guard<std::mutex> lock (state.mutex );
1782+ for (auto & c : state.controllers )
17821783 {
1783- DbgRef<DebuggerController> controller = g_debuggerControllers[i];
1784- if (!controller)
1785- continue ;
1786- if (controller->GetFile () == data->GetFile ())
1784+ if (c && c->GetFile () == data->GetFile ())
17871785 return true ;
17881786 }
17891787
@@ -1793,13 +1791,12 @@ bool DebuggerController::ControllerExists(BinaryViewRef data)
17931791
17941792DbgRef<DebuggerController> DebuggerController::GetController (FileMetadataRef file)
17951793{
1796- for (size_t i = 0 ; i < g_controllerCount; i++)
1794+ auto & state = GetControllerState ();
1795+ std::lock_guard<std::mutex> lock (state.mutex );
1796+ for (auto & c : state.controllers )
17971797 {
1798- DebuggerController* controller = g_debuggerControllers[i];
1799- if (!controller)
1800- continue ;
1801- if (controller->GetFile () == file)
1802- return controller;
1798+ if (c && c->GetFile () == file)
1799+ return c;
18031800 }
18041801
18051802 // You cannot create a controller from a file -- you must use a binary view for it
@@ -1809,12 +1806,11 @@ DbgRef<DebuggerController> DebuggerController::GetController(FileMetadataRef fil
18091806
18101807bool DebuggerController::ControllerExists (FileMetadataRef file)
18111808{
1812- for (size_t i = 0 ; i < g_controllerCount; i++)
1809+ auto & state = GetControllerState ();
1810+ std::lock_guard<std::mutex> lock (state.mutex );
1811+ for (auto & c : state.controllers )
18131812 {
1814- DbgRef<DebuggerController> controller = g_debuggerControllers[i];
1815- if (!controller)
1816- continue ;
1817- if (controller->GetFile () == file)
1813+ if (c && c->GetFile () == file)
18181814 return true ;
18191815 }
18201816
@@ -1824,16 +1820,12 @@ bool DebuggerController::ControllerExists(FileMetadataRef file)
18241820
18251821void DebuggerController::DeleteController (FileMetadataRef file)
18261822{
1827- for (size_t i = 0 ; i < g_controllerCount; i++)
1823+ auto & state = GetControllerState ();
1824+ std::lock_guard<std::mutex> lock (state.mutex );
1825+ for (auto & c : state.controllers )
18281826 {
1829- DbgRef<DebuggerController> controller = g_debuggerControllers[i];
1830- if (!controller)
1831- continue ;
1832-
1833- if (controller->GetFile () == file)
1834- {
1835- g_debuggerControllers[i] = nullptr ;
1836- }
1827+ if (c && c->GetFile () == file)
1828+ c = nullptr ;
18371829 }
18381830}
18391831
0 commit comments