defer stays the answer; drop is deferred and the shim can count what leaks
This commit is contained in:
parent
a3fccf440c
commit
a1827adb66
43
NEXT.md
43
NEXT.md
@ -362,6 +362,49 @@ Already banked, and it means classes are less work than plan.org implies: **a ge
|
|||||||
whose body is a dispatch table, which a reload extends. That is the expensive half of method dispatch, and it is built
|
whose body is a dispatch table, which a reload extends. That is the expensive half of method dispatch, and it is built
|
||||||
and tested.
|
and tested.
|
||||||
|
|
||||||
|
**Resource cleanup: `defer` stays the answer. `drop` is not built, and `with-cleanup` is not either.** Reached by
|
||||||
|
working the case through rather than by preference, so the reasoning is worth keeping.
|
||||||
|
|
||||||
|
`drop` was specified in `spec-memory.md` this morning. **This amends it: the hook is deferred, not built.** Three things
|
||||||
|
decided against it. It runs code somewhere the reader is not looking, which is the C++ behaviour the author explicitly
|
||||||
|
does not want. It would not even cover the motivating case — `Image` and `Texture2D` are *raylib's* types, and attaching
|
||||||
|
a hook to a foreign type is its own unsolved design question. And its one real advantage, cascading through a container,
|
||||||
|
is the case `Handle` is about to make rare: entities holding handles hold numbers, not resources.
|
||||||
|
|
||||||
|
`with-cleanup` / `unwind-protect` was also put and rejected: awkward with several resources, and it reads worse than
|
||||||
|
what already exists. The raylib begin/end pairs that seemed to motivate it are a macro problem, not a primitive one —
|
||||||
|
`with-drawing` and `with-mode-2d` are three-line macros once the expander lands.
|
||||||
|
|
||||||
|
**What to build instead is small: relax where `defer` may be written.** It is refused today inside a `let`, a loop or a
|
||||||
|
branch. The loop and branch refusals are right — `defer` is a *compile-time* construct, the cleanup copied into every
|
||||||
|
exit path, so "maybe registered" is not expressible and a loop body would fire once at function exit instead of once per
|
||||||
|
iteration. But **a `let` at the top level of a function body has exactly the function's extent** and always registers,
|
||||||
|
so it is as safe as function scope and is refused for a reason that does not apply to it. Relaxing it gives:
|
||||||
|
|
||||||
|
```
|
||||||
|
(defn load-brush []
|
||||||
|
(let [sheet (rl/load-image-from-memory ".png" brush-bytes)]
|
||||||
|
(defer (rl/unload-image sheet))
|
||||||
|
(set brush (rl/load-texture-from-image sheet))
|
||||||
|
(rl/image-flip-horizontal (addr sheet))
|
||||||
|
(set brush-mirrored (rl/load-texture-from-image sheet))))
|
||||||
|
```
|
||||||
|
|
||||||
|
Several resources are several defers, released in reverse, visible in acquisition order. A container of resources is an
|
||||||
|
ordinary loop inside the defer body — the manual cascade, three lines, at one level of nesting.
|
||||||
|
|
||||||
|
**Odin, for the record**, has no destructors, no drop and no finalizers: `delete` frees container memory and nothing
|
||||||
|
else, and resource release is `defer` at the acquisition site. That idiom does not transfer directly only because
|
||||||
|
Odin's `defer` is block-scoped; the relaxation above recovers most of it.
|
||||||
|
|
||||||
|
**The safety net, and the better use of effort: a debug tracking allocator.** ASan's leak detection covers memory
|
||||||
|
*instrumented* code allocated — the Flan allocator, and it is already wired up and clean. It does **not** cover a leaked
|
||||||
|
texture, because that memory belongs to uninstrumented raylib, which is the same reason the sanitizer sweep treats the
|
||||||
|
windowed examples as noise. But every raylib call goes through a *generated* wrapper, so a dev build can count
|
||||||
|
acquisitions against releases at that boundary and report what is still held at exit, by name. No hook, no type
|
||||||
|
annotation, nothing running at a distance — it does not change how code is written, it reports when something was
|
||||||
|
forgotten.
|
||||||
|
|
||||||
## Blocked and unfinished
|
## Blocked and unfinished
|
||||||
|
|
||||||
Everything below was found, decided or half-built and then stopped. Each says what blocks it. Nothing here is a
|
Everything below was found, decided or half-built and then stopped. Each says what blocks it. Nothing here is a
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user