@@ -263,7 +263,7 @@ namespace {
263263 bool needsToReserveAllocationSpace () override { return true ; }
264264 };
265265
266- // / A JITLinkMemoryManager for Cling that never frees its allocations .
266+ // / A JITLinkMemoryManager for Cling.
267267 class ClingJITLinkMemoryManager : public InProcessMemoryManager {
268268 public:
269269 using InProcessMemoryManager::InProcessMemoryManager;
@@ -273,16 +273,27 @@ namespace {
273273 // Disabled until CallFunc is informed about unloading, and can
274274 // re-generate the wrapper (if the decl is still available). See
275275 // https://github.com/root-project/root/issues/10898
276-
277- // We still have to release the allocations which resets their addresses
278- // to FinalizedAlloc::InvalidAddr, or the assertion in ~FinalizedAlloc
279- // will be unhappy...
280- for ( auto &Alloc : Allocs) {
281- Alloc. release ( );
282- }
283- // Pretend we successfully deallocated everything...
276+ //
277+ // Releasing the handles orphans each allocation's vector of JITLink
278+ // dealloc actions. Retain them (required for CallFunc) and let the
279+ // base class free everything when this manager is destroyed at
280+ // interpreter teardown.
281+ std::lock_guard<std::mutex> G (m_RetainedMutex );
282+ for ( auto &Alloc : Allocs)
283+ m_Retained. push_back ( std::move (Alloc));
284284 OnDeallocated (Error::success ());
285285 }
286+
287+ ~ClingJITLinkMemoryManager () override {
288+ if (!m_Retained.empty ())
289+ InProcessMemoryManager::deallocate (
290+ std::move (m_Retained),
291+ [](Error Err) { consumeError (std::move (Err)); });
292+ }
293+
294+ private:
295+ std::mutex m_RetainedMutex;
296+ std::vector<FinalizedAlloc> m_Retained;
286297 };
287298
288299 // / A DynamicLibrarySearchGenerator that uses ResourceTracker to remember
0 commit comments