From 82f85dfa1ca05add690fa6a1e0783b4dc3070f30 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 7 Dec 2023 17:12:24 +0000 Subject: [PATCH] Fix DmaAllocator memory leak when closing camera When closing the camera, the allocator was not closing the open file descriptors, causing a memory leak. Fixed by closing the file descriptors and deleting the allocator when closing the camera. Signed-off-by: William Vinnicombe --- picamera2/allocators/allocator.py | 3 +++ picamera2/allocators/dmaallocator.py | 12 ++++++++++++ picamera2/picamera2.py | 5 ++++- tests/allocator_leak_test.py | 19 +++++++++++++++++++ tests/test_list.txt | 1 + 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/allocator_leak_test.py diff --git a/picamera2/allocators/allocator.py b/picamera2/allocators/allocator.py index a4904d54..c616668e 100644 --- a/picamera2/allocators/allocator.py +++ b/picamera2/allocators/allocator.py @@ -16,6 +16,9 @@ def acquire(self, bufs): def release(self, bufs): pass + def close(self): + pass + class Sync: """Base class for allocator syncronisations""" diff --git a/picamera2/allocators/dmaallocator.py b/picamera2/allocators/dmaallocator.py index 5b95b0ab..f59e16fd 100644 --- a/picamera2/allocators/dmaallocator.py +++ b/picamera2/allocators/dmaallocator.py @@ -87,6 +87,18 @@ def cleanup(self): for k in [k for k, v in self.mapped_buffers.items() if v.closed]: del self.mapped_buffers[k] + def close(self): + self.libcamera_fds = [] + self.cleanup() + # Close our copies of fds + for fd in self.open_fds: + os.close(fd) + self.frame_buffers = {} + self.open_fds = [] + + def __del__(self): + self.close() + class DmaSync(Sync): """Dma Buffer Sync""" diff --git a/picamera2/picamera2.py b/picamera2/picamera2.py index 6301cf98..34a9dd77 100644 --- a/picamera2/picamera2.py +++ b/picamera2/picamera2.py @@ -22,7 +22,7 @@ import picamera2.formats as formats import picamera2.platform as Platform import picamera2.utils as utils -from picamera2.allocators import DmaAllocator +from picamera2.allocators import DmaAllocator, Allocator from picamera2.encoders import Encoder, H264Encoder, MJPEGEncoder, Quality from picamera2.outputs import FfmpegOutput, FileOutput from picamera2.previews import DrmPreview, NullPreview, QtGlPreview, QtPreview @@ -622,6 +622,9 @@ def close(self) -> None: self.video_configuration_ = None self.notifymeread.close() os.close(self.notifyme_w) + # Clean up the allocator + del self.allocator + self.allocator = Allocator() _log.info('Camera closed successfully.') @staticmethod diff --git a/tests/allocator_leak_test.py b/tests/allocator_leak_test.py new file mode 100644 index 00000000..a1f33ee1 --- /dev/null +++ b/tests/allocator_leak_test.py @@ -0,0 +1,19 @@ +#!/usr/bin/python3 + +# Test that the allocators don't leak + +from picamera2 import Picamera2 +from picamera2.allocators import DmaAllocator, LibcameraAllocator + + +for i in range(200): + picam2 = Picamera2() + picam2.allocator = LibcameraAllocator(picam2.camera) + picam2.configure() + picam2.close() + +for i in range(200): + picam2 = Picamera2() + picam2.allocator = DmaAllocator() + picam2.configure() + picam2.close() diff --git a/tests/test_list.txt b/tests/test_list.txt index 09277d0b..49f72cc2 100644 --- a/tests/test_list.txt +++ b/tests/test_list.txt @@ -76,3 +76,4 @@ tests/quality_check.py tests/qt_gl_preview_test.py tests/stop_slow_framerate.py tests/allocator_test.py +tests/allocator_leak_test.py