fix(physics): jolt static vectors being freed#1418
Open
Dhruv-0-Arora wants to merge 3 commits into
Open
Conversation
Bound methods that return a vector by value (`Normalized()`, `AddRVec3()`) do not allocate. Emscripten's binding writes into one fixed static per method and hands back that address, so the result is only valid until the next call to the same method, and `JOLT.destroy` on one runs `free()` on an address `malloc` never issued. `createAnchorPoint` returned `jointOrigin.AddRVec3(jointOriginOffset)` straight through, so every caller was handed a static. `PhysicsSystem` then freed it in four places: two bare `JOLT.destroy(anchorPoint)` calls on the ball constraint path, and two `convertJoltRVec3ToJoltVec3(anchorPoint)` calls whose `destroy` parameter defaults to true. Each one asked dlmalloc to reclaim a data segment address it has no record of. Add `ownVec3`/`ownRVec3` and use them so no static escapes the expression that produced it. `createAnchorPoint` and `getPerpendicular` now return heap vectors the caller owns, which is what makes those frees legal.
Settings structs declare their vector members by value, so assigning into one memcpys the bytes in and the struct never takes ownership. Destroying the settings does not free the source vector, which means every `new JOLT.Vec3` and `convert*` result assigned into settings is still ours. Several were dropped instead: `wheelPos`, the four `urdfWheelBasis` vectors from `inferURDFAutoWheelBasis`, and `minBounds`/`maxBounds`. These run at joint and body creation rather than per frame, so the leak is bounded by spawns and is small in absolute terms, but it is unreclaimed all the same. Destroying `anchorPoint` is only valid because `createAnchorPoint` now returns a real allocation.
Ownership depends on where a vector came from, and that is not visible in any signature. These tests make the two cases concrete: `new JOLT.Vec3` is a real `malloc` that hands out a fresh address each time and recycles it on destroy, while a bound method's return value is one fixed static that sits below malloc's block, survives 2000 calls without accumulating, and is clobbered by the next call. Also covers the struct-copies-the-bytes-in rule that the destroys in `PhysicsSystem` depend on, and checks that `getPerpendicular` copies out of the static so callers get distinct heap vectors.
Dhruv-0-Arora
requested review from
0xda157,
BrandonPacewic,
PepperLola,
azaleacolburn and
rutmanz
and removed request for
0xda157
July 16, 2026 17:30
| import JOLT from "@/util/loading/JoltSyncLoader" | ||
|
|
||
| /** | ||
| * what you have to destroy and why |
Collaborator
Author
There was a problem hiding this comment.
... that was me?
Collaborator
Author
There was a problem hiding this comment.
Sorry if it was unclear :( @azaleacolburn
Contributor
|
Also could you not write your pr descriptions with claude please, it's just like a really annoying style |
Contributor
|
Also, please use the pr description template in the future |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Task
Jolt.Vec3 is stored in heap but
anything a bound method returns by value (
Normalized(),AddRVec3(),Add()) is not allocated. the binding keeps one static per method.How it looks:
So, we instead will copy the values from this memory and store it in a heap allocated Jolt.Vec3 for safer memory management. This means, however, that these Jolt.Vec3 s will need to be Jolt.destroy(x) ed.
Symptom
createAnchorPointreturnedjointOrigin.AddRVec3(...)straight through, so callers got a static temp.PhysicsSystemfreed it in four places: twoJOLT.destroy(anchorPoint)on the ball constraint path, and twoconvertJoltRVec3ToJoltVec3(anchorPoint)whosedestroyparam defaults to true.the function
JOLT.getPointer()shows thatAddRVec3returns 54992 every call, malloc's block (heap) starts around 1157600. all four were freeing a data segment address (meaning they did nothing).In addition
settings members are declared by value (
RVec3 mPoint1;), so setters memcpy and the struct never takes ownership.JOLT.destroy(settings)won't free a vector you assigned in, but you can destroy it yourself right after assigning. we were droppingwheelPos, the foururdfWheelBasisvectors, andminBounds/maxBounds.Solution
ownVec3/ownRVec3helpers so no static escapes the expression that made it.createAnchorPointandgetPerpendicularnow return owned heap vectorsanchorPointnow that it's real memoryJoltOwnership.test.tspins down both halves: fresh address per malloc, recycling on destroy, the static below the heap, 2000 calls yielding one address, the clobber, and structs copying bytes intesting
JoltOwnership.test.tspasses in chromium + firefox