Repository navigation
Conversation
|
Review requested:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #66842 +/- ##
==========================================
+ Coverage 90.65% 90.67% +0.02%
==========================================
Files 797 797
Lines 278783 278791 +8
Branches 53618 53630 +12
==========================================
+ Hits 252718 252799 +81
+ Misses 16420 16345 -75
- Partials 9645 9647 +2
🚀 New features to boost your workflow:
|
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
addaleax
left a comment
There was a problem hiding this comment.
Just going to point out that there is an existing for this problem in the form of using BaseObjectPtr<DynamicLibrary> owner; instead of DynamicLibrary* owner; in struct FFICallback
7e8c874 to
6344ae1
Compare
Thanks. Switched to |
|
cc @addaleax Can you take a look? It now uses |
|
cc @ShogunPanda Can you review this again? It now uses |
Callback pointers returned by registerCallback() are plain bigint addresses, so they do not keep the owning DynamicLibrary reachable. If user code dropped the library without calling close(), the next GC ran ~DynamicLibrary(), which freed every ffi_closure. Native code still holding a callback pointer then jumped into freed trampoline memory and crashed the process, even though close() was never called. Hold the owning library through a BaseObjectPtr in each FFICallback, so it stays strong until its last callback is unregistered or the library is closed. Callbacks released with unrefCallback() still count, because their trampolines remain live native memory. Since this forms a cycle, release the callbacks from an Environment cleanup hook so the library is still freed at teardown. Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Assisted-by: claude:opus-5.5
6344ae1 to
b0c03a2
Compare
Fixes: #66841
registerCallback()returns a plainbigintaddress, which doesn't keep the owningDynamicLibraryreachable. If user code dropped the library without callingclose(), GC ran~DynamicLibrary()and freed everyffi_closure. Native code still holding a callback pointer then segfaulted, even though no documented rule was broken.This PR makes each
FFICallbackhold its owning library through aBaseObjectPtr, so the library stays strong until its last callback is unregistered orclose()is called. Callbacks afterunrefCallback()still count, since their trampolines remain live native memory. Because callbacks and library now reference each other, anEnvironmentcleanup hook releases the callbacks so the library is still freed at teardown (worker or process exit).Assisted-by: claude:opus-5.5