Refuse loads and renders that would not fit in memory

Opening a project of ninety 17 MP photos got Effector killed by the
kernel's OOM killer: 4.6 GB of decoded RGB on a machine with 3.6 GB free,
and no core dump to show for it. Bulk allocations are now sized first and
refused with an OutOfMemoryError past three quarters of the free memory
(MemAvailable, or less under a cgroup memory.max). loadImages sizes the
batch from the file headers and refuses it before decoding anything, and
holds the decodes in flight to what is left; renderChain and renderFrames
refuse a canvas at 24 bytes a pixel, plus any picture a Path param loads.
The interface keeps a refused batch's paths, so saving the project never
drops them. Version 0.6.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Si
2026-09-26 18:31:53 +02:00
co-authored by Claude Opus 5.5
parent 385617781e
commit ea59db5acd
13 changed files with 401 additions and 4 deletions
+15
View File
@@ -261,6 +261,21 @@ then copies pixels in parallel.
copy-on-write via `mutateSources()`, so a render in flight can never race with
an add/remove.
### Memory is checked before it is taken
Running out of memory does not fail cleanly on Linux: the kernel's OOM killer
ends the program (and often the terminal or session it runs in), leaving no
core dump. So bulk allocations are sized first and refused with an
`OutOfMemoryError` (`src/core/memory.hpp`) when they would take more than three
quarters of `availableMemory()` — the system's MemAvailable, or less under a
cgroup `memory.max`. `loadImages` sizes the whole batch from the file headers
(`imageFilePixels`) and refuses it before decoding anything, and holds the
decodes it runs at once to what is left; `renderChain` and `renderFrames`
refuse a canvas at 24 bytes a pixel (13 measured, plus the interface's copy),
plus any picture a `Path` param loads. The interface keeps a refused batch's
paths in `refusedSources_`, so saving the project never drops them; the tests
fake a small machine with `limitAvailableMemory`.
### Image buffer
`Image` is tightly packed 8-bit RGB with stride exactly `width * 3` — effects