diff --git a/MAINTENANCE.md b/MAINTENANCE.md index c93c6f6..3ccf604 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -163,7 +163,7 @@ guards every dependency with `if(NOT TARGET ...)`, so a top-level build must def `akerror::akerror` and `akstdlib::akstdlib` from `deps/libakerror` and `deps/libakstdlib` **before** `add_subdirectory(deps/libakgl)`, or the targets are declared twice. -That order is load-bearing for a second reason: `deps/libakerror` is at **2.0.2**, whose 2.0.0 +That order is load-bearing for a second reason: `deps/libakerror` is at **2.0.3**, whose 2.0.0 was a source and ABI break carrying an soname (`libakerror.so.2`). `libakstdlib` and `libakgl` must be compiled against that header, not a 1.x one, and an installed `libakerror.so.1` must not be picked up. The break is quiet if you get it wrong: the context behind `IGNORE` became @@ -174,46 +174,22 @@ a mixed build leaks pool slots or frees one twice rather than failing to link. | Submodule | Version | soname | ABI rule | Version API | |---|---|---|---|---| -| `deps/libakerror` | 2.0.2 | `libakerror.so.2` | major only | **none** — no version macro; `include/akbasic/error.h` feature-tests `AKERR_THREAD_SAFE` and `AKERR_EXIT_STATUS_UNREPRESENTABLE` instead | +| `deps/libakerror` | 2.0.3 | `libakerror.so.2` | major only | **none** — no version macro; `include/akbasic/error.h` feature-tests `AKERR_THREAD_SAFE` and `AKERR_EXIT_STATUS_UNREPRESENTABLE` instead | | `deps/libakstdlib` | 0.2.0 | `libakstdlib.so.0.2` | **`MAJOR.MINOR` while major is 0** | `AKSL_VERSION_*`, `aksl_version()`, `AKSL_VERSION_CHECK()` | | `deps/libakgl` | 0.9.0 | `libakgl.so.0.9` | **`MAJOR.MINOR` while major is 0** | `AKGL_VERSION*`, `akgl_version()`, `AKGL_VERSION_AT_LEAST()` | -**The `libakerror` row is what `project(akerror VERSION ...)` declares, and it disagrees with -that library's own release notes**, which carry a "Release 2.0.3" section describing CI-only -changes. The version the build stamps into the soname and into -`akerrorConfigVersion.cmake` is 2.0.2; nothing consumers can observe says 2.0.3. Read the -table as the ABI and the notes as the changelog, and do not "correct" this row to 2.0.3 -without the upstream `project()` bump to go with it. Reported as `libakerror` issue #38. +`project(akerror VERSION ...)` now stamps 2.0.3, matching that library's own "Release 2.0.3" +release notes. It previously disagreed — the soname and `akerrorConfigVersion.cmake` still +said 2.0.2 while the notes described 2.0.3 — but that was `libakerror` issue #38, which is +closed; the table and the notes agree again. -#### What 2.0.2's `IGNORE` change costs, measured - -`IGNORE` now takes a *copy* of the context so the pool slot can be released, which fixes a -real leak. The copy lives in the public header as a file-scope `static`: - -```c norun -static AKERR_THREAD_LOCAL akerr_ErrorContext akerr_last_ignored; -``` - -`static` means one per translation unit, and `AKERR_THREAD_LOCAL` means each one is -thread-local. `sizeof(akerr_ErrorContext)` is **37,296 bytes**, so: - -| Build | TLS segment of `build/basic` | -|---|---| -| libakerror 2.0.1 | none at all | -| libakerror 2.0.2 | 1,417,248 bytes — **1.35 MiB**, per thread | - -38 copies × 37,296 accounts for the segment exactly; `readelf -sW build/basic` shows all 38. -It scales with translation-unit count, not with anything about the program, and every thread -a host game spawns pays it again — which is worth knowing in a tree that spent a release -taking `akbasic_Runtime` from 10.75 MiB to 2.40 MiB. - -It also emits **84 `-Wunused-variable` warnings**, one per TU that includes `akerror.h` -without using `IGNORE`. Nothing here builds with `-Werror`, so it is noise rather than a -failure — but a consumer that does cannot include the header. - -**Both are `libakerror` issue #37 and neither is worked around here**, per the rule above: a -one-line `extern` in the header and a definition in `src/error.c` fixes it upstream, and -patching our copy would fork the submodule. Delete this subsection when it lands. +`IGNORE` still takes a *copy* of the ignored context so the pool slot can be released, which +fixes a real leak, but the copy no longer costs TLS per translation unit. It was a file-scope +`static` in the public header — one copy per TU, 37,296 bytes each, 1.35 MiB of thread-local +storage in `build/basic` alone, plus 84 `-Wunused-variable` warnings for every TU that never +called `IGNORE`. `libakerror` issue #37 moved it to an `extern` declaration in the header with +a single definition in `src/error.c`, which restores one copy per thread and silences the +warning. Issue #37 is closed and this subsection is gone accordingly. For both 0.x libraries the soname carries `MAJOR.MINOR` deliberately: 0.1 and 0.2 are *different* ABIs, and both become major-only at 1.0. Do not read `0.1 → 0.2` as a compatible diff --git a/deps/libakerror b/deps/libakerror index ee38eed..9249f8c 160000 --- a/deps/libakerror +++ b/deps/libakerror @@ -1 +1 @@ -Subproject commit ee38eed31c0c0733233676a289d9c6dd9a19aa9d +Subproject commit 9249f8c029d6af5f2f837316098d2fefed8c4467