diff --git a/.github/workflows/dockerbuild.yml b/.github/workflows/dockerbuild.yml index 30b75c5..d501a96 100644 --- a/.github/workflows/dockerbuild.yml +++ b/.github/workflows/dockerbuild.yml @@ -31,7 +31,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - push: true + push: ${{ github.event_name != 'pull_request' }} tags: | ghcr.io/dipy/gpustreamlines:${{ github.sha }} ghcr.io/dipy/gpustreamlines:latest diff --git a/cuslines/metal/mutils.py b/cuslines/metal/mutils.py index d190e59..91fbfb9 100644 --- a/cuslines/metal/mutils.py +++ b/cuslines/metal/mutils.py @@ -73,6 +73,34 @@ class ModelType(IntEnum): PAGE_SIZE = 16384 +# PyObjC's Metal bindings carry no ownership metadata for the MTLDevice +# protocol's new* methods, so PyObjC treats their +1 return as autoreleased +# and never releases it: every per-batch MTLBuffer leaked (~19 GB / 588k seeds). +# registerMetaDataForSelector replaces the inferred metadata, so pointer and +# out-arguments must be restated here. +def _register_metal_ownership(): + import objc + + ptr = {"type": b"n^v", "c_array_length_in_arg": 3} + err = {"type": b"o^@"} + for sel, args in ( + (b"newBufferWithLength:options:", {}), + (b"newBufferWithBytes:length:options:", {2: ptr}), + (b"newCommandQueue", {}), + (b"newLibraryWithSource:options:error:", {4: err}), + (b"newFunctionWithName:", {}), + (b"newComputePipelineStateWithFunction:error:", {3: err}), + ): + objc.registerMetaDataForSelector( + b"NSObject", + sel, + {"retval": {"already_retained": True}, "arguments": args}, + ) + + +_register_metal_ownership() + + def div_up(a, b): return (a + b - 1) // b