diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f8acec..8816688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,55 +18,7 @@ under *Changed* or *Removed*. The `Unreleased` block accumulates entries during development and is rolled into a dated version block (`## [X.Y.Z] — YYYY-MM-DD`) when a release PR closes a milestone. -### Removed - -- **The `docs-site` CI status badge** was removed from the README header in all three - locales (English, `zh-Hans`, `ja`). The published Doxygen site is already linked from - the **API reference** badge, so the separate build-status badge was redundant. The - `docs-site.yml` workflow itself is unchanged. The badge is removed from the - translations in the same change; the `translation-status.md` manifest's two README - rows are marked `stale` here (the English source moved ahead of their pin) and - re-pinned + flipped back to `translated` in a follow-up once this lands on `master`. - Documentation-only; no API change. - -### Changed - -- **`zh-Hans` / `ja` README translations re-synced to `v1.1.1`.** Carries the English - README's `v1.1.1` deltas into both locales — the `v1.1.1` status badge (and its - release-tag link) and the new `v1.1.1` status paragraph (bug ledger, PR-metadata - policy, `SECURITY.md`, `packaging-smoke` CI, session journal, the roadmap-placement - rule, and the changelog split). The `translation-status.md` manifest's two README - rows are re-pinned to the current source commit (`23fc6c4`) and flipped from `stale` - back to `translated`, clearing the `i18n-freshness` flag the `v1.1.1` release raised. - Documentation-only; no API change. - -### Fixed - -- **`InstrumentedPool` data race on the growth counter ([BUG-0001](docs/bugs/2026/06/BUG-0001-instrumented-pool-growth-counter-data-race.md)).** - `notify_if_grew()` read and wrote the non-atomic `last_growths_` on the allocation - hot path, while the decorator is documented as safe to drive concurrently over a - thread-safe pool — a data race (UB) under `MUTEX` + dynamic growth. `last_growths_` - is now `std::atomic` and advanced with a `compare_exchange`, so the growth event is - emitted once per growth without a race. A concurrent `InstrumentedPool` case was - added to the ThreadSanitizer stress suite. Header-only; no API change. -- **`InstrumentedPool::deallocate` no longer underflows `live_` ([BUG-0002](docs/bugs/2026/06/BUG-0002-instrumented-pool-live-counter-underflow.md)).** - A foreign or double-freed pointer (a no-op in the core, ADR-0012) used to decrement - the unsigned `live_` counter unconditionally, wrapping it to `SIZE_MAX` and - corrupting `stats()`. The decrement now clamps at zero. The C header's - `memory_pool_free` note was corrected to stop claiming the Decorator *detects* - double-free (it counts but cannot distinguish one). Header/doc-only; no API change. -- **`InstrumentedPool` move-assignment now emits `destroyed` for the replaced pool - ([BUG-0003](docs/bugs/2026/06/BUG-0003-instrumented-pool-move-assign-missing-destroyed-event.md)).** - Move-assigning over an instrumented pool released its `Pool` and observers without - notifying `PoolEvent::destroyed`, asymmetric with the destructor. It now notifies - before reassignment. Header-only; no API change. -- **Overflow guard in `grow_pool` ([BUG-0004](docs/bugs/2026/06/BUG-0004-grow-pool-growth-size-overflow.md)).** - The dynamic-growth path computed `total * (grow_factor_ - 1)` before any overflow - check, so that product could wrap `size_t` and feed the downstream `block_size` - guard an already-wrapped value. Added a `would_overflow_product` guard on the - growth-count product first, mirroring the create-path guard; on overflow the pool - falls back to fixed-mode exhaustion. Latent (not runtime-reachable — RAM exhausts - first); no API change. +*Nothing yet.* ## Released versions @@ -74,6 +26,7 @@ Each released version is an **immutable** entry under [`docs/changelog/`](docs/c | Version | Date | Highlights | |---------|------|------------| +| [1.1.2](docs/changelog/v1/v1.1.2.md) | 2026-06-15 | Maintenance — InstrumentedPool/core bug fixes (BUG-0001…0004) + docs | | [1.1.1](docs/changelog/v1/v1.1.1.md) | 2026-06-15 | Maintenance — bug ledger, PR-metadata policy, journal split, SECURITY.md, CI Node bump | | [1.1.0](docs/changelog/v1/v1.1.0.md) | 2026-06-14 | Internationalization (zh-Hans / ja) & post-release governance | | [1.0.1](docs/changelog/v1/v1.0.1.md) | 2026-06-14 | Packaging patch — vcpkg port + Conan recipe (byte-identical lib) | @@ -85,4 +38,4 @@ Each released version is an **immutable** entry under [`docs/changelog/`](docs/c | [0.2.0](docs/changelog/v0/v0.2.0.md) | 2026-06-11 | Core memory pool — single-threaded O(1) MVP | | [0.1.0](docs/changelog/v0/v0.1.0.md) | 2026-06-10 | Build system & project skeleton | -[Unreleased]: https://github.com/danielPoloWork/pbr-cpp-memory-pool/compare/v1.1.1...HEAD +[Unreleased]: https://github.com/danielPoloWork/pbr-cpp-memory-pool/compare/v1.1.2...HEAD diff --git a/README.md b/README.md index 35fd117..ac1cd40 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![API reference](https://img.shields.io/badge/API%20reference-Doxygen-1f6feb.svg)](https://danielpolowork.github.io/pbr-cpp-memory-pool/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Standard: C++17 / ANSI C](https://img.shields.io/badge/Standard-C%2B%2B17%20%2F%20ANSI%20C-blue.svg)](docs/specs/01_spec_cpp_memory_pool.md) -[![Status: v1.1.1 stable](https://img.shields.io/badge/Status-v1.1.1%20stable-brightgreen.svg)](https://github.com/danielPoloWork/pbr-cpp-memory-pool/releases/tag/v1.1.1) +[![Status: v1.1.2 stable](https://img.shields.io/badge/Status-v1.1.2%20stable-brightgreen.svg)](https://github.com/danielPoloWork/pbr-cpp-memory-pool/releases/tag/v1.1.2) > Part of the **Purpose-Built References (PBR)** series — small, didactic, production-quality C/C++ reference implementations of high-performance building blocks. @@ -193,6 +193,8 @@ Reports for other host × compiler combinations (Linux / GCC, Linux / Clang, mac ## Status +`v1.1.2` — **maintenance** (bug fixes + documentation), a PATCH over `v1.1.1`. The public surface is unchanged — no API/ABI change. Fixes four verified, externally-reported defects (the first use of the in-repo [bug ledger](docs/bugs/), [ADR-0039](docs/adr/0039-bug-ledger-and-triage-protocol.md)): an `InstrumentedPool` growth-counter **data race** (BUG-0001), a `live_` counter **underflow** on foreign/double frees (BUG-0002), a missing `destroyed` event on move-assignment (BUG-0003), and a latent `grow_pool` **overflow** guard (BUG-0004). Also removes the redundant `docs-site` README badge and re-syncs the `zh-Hans`/`ja` translations. Release notes: [`docs/releases/v1.1.2.md`](docs/releases/v1.1.2.md). The earlier line: + `v1.1.1` — **maintenance** (documentation / process / tooling), the first post-`v1.1.0` PATCH. The shipped library is byte-identical to `v1.1.0` — no API/ABI/behaviour change. Adds the in-repo [bug ledger](docs/bugs/) + triage protocol ([ADR-0039](docs/adr/0039-bug-ledger-and-triage-protocol.md)), a [PR-metadata policy](docs/adr/0040-pull-request-metadata-policy.md), a [`SECURITY.md`](SECURITY.md), `packaging-smoke` CI for the vcpkg/Conan recipes, the [session journal](docs/journal/) ([ADR-0036](docs/adr/0036-session-journal-extraction.md)), the new-feature roadmap-placement rule ([ADR-0037](docs/adr/0037-new-feature-roadmap-placement.md)), and the per-release changelog split ([ADR-0038](docs/adr/0038-changelog-version-split.md)). Five new ADRs (0036–0040) bring the total to 40. Release notes: [`docs/releases/v1.1.1.md`](docs/releases/v1.1.1.md). The earlier line: `v1.1.0` — **internationalization & post-release governance** (Milestone 8), the first post-1.0 MINOR. Purely **additive** — the library binary is unchanged from `v1.0.x`. Documentation now ships in **Simplified Chinese (`zh-Hans`) and Japanese (`ja`)** (English stays normative — [`docs/i18n/`](docs/i18n/), [ADR-0032](docs/adr/0032-documentation-i18n-architecture.md)); the spec is English-normative ([ADR-0033](docs/adr/0033-english-as-the-spec-normative-language.md)); a [post-release maintenance protocol](docs/workflow/maintenance.md) ([ADR-0034](docs/adr/0034-post-release-maintenance-protocol.md)) governs the maintained-product phase; and an agent-runnable [`consistency lint`](tools/consistency_lint.py) ([ADR-0035](docs/adr/0035-agent-runnable-consistency-lint.md)) gates cross-artifact congruence in CI and the agent contract. Four new ADRs (0032–0035) bring the total to 35. Release notes: [`docs/releases/v1.1.0.md`](docs/releases/v1.1.0.md). The earlier line: diff --git a/docs/changelog/v1/v1.1.2.md b/docs/changelog/v1/v1.1.2.md new file mode 100644 index 0000000..a058b07 --- /dev/null +++ b/docs/changelog/v1/v1.1.2.md @@ -0,0 +1,64 @@ +# Changelog — v1.1.2 + +> Immutable release entry — part of the [project changelog](../../../CHANGELOG.md). One file per release; format and rationale in [ADR-0038](../../adr/0038-changelog-version-split.md). Released entries are never edited. + +## [1.1.2] — 2026-06-15 + +**Maintenance release.** A **PATCH** fixing four verified, third-party-reported +defects (the first real use of the in-repo bug ledger, [ADR-0039](../../../docs/adr/0039-bug-ledger-and-triage-protocol.md)) +plus the accumulated documentation work. The library's **public surface is +unchanged** from `v1.1.1` (no API/ABI change); the fixes are header-only / core +internals. Highlights: an `InstrumentedPool` **data race** on the growth counter +([BUG-0001](../../../docs/bugs/2026/06/BUG-0001-instrumented-pool-growth-counter-data-race.md)), +a `live_` counter **underflow** on foreign/double frees + an over-promising header +note ([BUG-0002](../../../docs/bugs/2026/06/BUG-0002-instrumented-pool-live-counter-underflow.md)), +a missing `destroyed` event on move-assignment +([BUG-0003](../../../docs/bugs/2026/06/BUG-0003-instrumented-pool-move-assign-missing-destroyed-event.md)), +and a latent `grow_pool` overflow guard +([BUG-0004](../../../docs/bugs/2026/06/BUG-0004-grow-pool-growth-size-overflow.md)). + +### Removed + +- **The `docs-site` CI status badge** was removed from the README header in all three + locales (English, `zh-Hans`, `ja`). The published Doxygen site is already linked from + the **API reference** badge, so the separate build-status badge was redundant. The + `docs-site.yml` workflow itself is unchanged. Documentation-only; no API change. + +### Changed + +- **`zh-Hans` / `ja` README translations re-synced to `v1.1.1`.** Carries the English + README's `v1.1.1` deltas into both locales — the `v1.1.1` status badge (and its + release-tag link) and the new `v1.1.1` status paragraph (bug ledger, PR-metadata + policy, `SECURITY.md`, `packaging-smoke` CI, session journal, the roadmap-placement + rule, and the changelog split), with the `translation-status.md` manifest re-pinned. + Documentation-only; no API change. + +### Fixed + +- **`InstrumentedPool` data race on the growth counter ([BUG-0001](../../../docs/bugs/2026/06/BUG-0001-instrumented-pool-growth-counter-data-race.md)).** + `notify_if_grew()` read and wrote the non-atomic `last_growths_` on the allocation + hot path, while the decorator is documented as safe to drive concurrently over a + thread-safe pool — a data race (UB) under `MUTEX` + dynamic growth. `last_growths_` + is now `std::atomic` and advanced with a `compare_exchange`, so the growth event is + emitted once per growth without a race. A concurrent `InstrumentedPool` case was + added to the ThreadSanitizer stress suite. Header-only; no API change. +- **`InstrumentedPool::deallocate` no longer underflows `live_` ([BUG-0002](../../../docs/bugs/2026/06/BUG-0002-instrumented-pool-live-counter-underflow.md)).** + A foreign or double-freed pointer (a no-op in the core, ADR-0012) used to decrement + the unsigned `live_` counter unconditionally, wrapping it to `SIZE_MAX` and + corrupting `stats()`. The decrement now clamps at zero. The C header's + `memory_pool_free` note was corrected to stop claiming the Decorator *detects* + double-free (it counts but cannot distinguish one). Header/doc-only; no API change. +- **`InstrumentedPool` move-assignment now emits `destroyed` for the replaced pool + ([BUG-0003](../../../docs/bugs/2026/06/BUG-0003-instrumented-pool-move-assign-missing-destroyed-event.md)).** + Move-assigning over an instrumented pool released its `Pool` and observers without + notifying `PoolEvent::destroyed`, asymmetric with the destructor. It now notifies + before reassignment. Header-only; no API change. +- **Overflow guard in `grow_pool` ([BUG-0004](../../../docs/bugs/2026/06/BUG-0004-grow-pool-growth-size-overflow.md)).** + The dynamic-growth path computed `total * (grow_factor_ - 1)` before any overflow + check, so that product could wrap `size_t` and feed the downstream `block_size` + guard an already-wrapped value. Added a `would_overflow_product` guard on the + growth-count product first, mirroring the create-path guard; on overflow the pool + falls back to fixed-mode exhaustion. Latent (not runtime-reachable — RAM exhausts + first); no API change. + +[1.1.2]: https://github.com/danielPoloWork/pbr-cpp-memory-pool/releases/tag/v1.1.2 diff --git a/docs/i18n/translation-status.md b/docs/i18n/translation-status.md index 92e6882..c74bb15 100644 --- a/docs/i18n/translation-status.md +++ b/docs/i18n/translation-status.md @@ -16,7 +16,7 @@ Status vocabulary: | Source page | Source commit | Translated at | Status | Reviewer | |-------------|:-------------:|:-------------:|:------:|----------| -| [`README.md`](../../README.md) | `6f1aa60` | `6f1aa60` | `translated` | — | +| [`README.md`](../../README.md) | `6f1aa60` | `6f1aa60` | `stale` | — | | [`docs/specs/01_spec_cpp_memory_pool.md`](../specs/01_spec_cpp_memory_pool.md) | `2e55dfa` | `2e55dfa` | `translated` | — | | [`docs/patterns/README.md`](../patterns/README.md) | `524f0cc` | `524f0cc` | `translated` | — | @@ -24,7 +24,7 @@ Status vocabulary: | Source page | Source commit | Translated at | Status | Reviewer | |-------------|:-------------:|:-------------:|:------:|----------| -| [`README.md`](../../README.md) | `6f1aa60` | `6f1aa60` | `translated` | — | +| [`README.md`](../../README.md) | `6f1aa60` | `6f1aa60` | `stale` | — | | [`docs/specs/01_spec_cpp_memory_pool.md`](../specs/01_spec_cpp_memory_pool.md) | `612f9d2` | `612f9d2` | `translated` | — | | [`docs/patterns/README.md`](../patterns/README.md) | `6c6aeb7` | `6c6aeb7` | `translated` | — | diff --git a/docs/releases/v1.1.2.md b/docs/releases/v1.1.2.md new file mode 100644 index 0000000..e82583a --- /dev/null +++ b/docs/releases/v1.1.2.md @@ -0,0 +1,43 @@ +# pbr-cpp-memory-pool v1.1.2 — Maintenance (InstrumentedPool & core bug fixes) + +A **maintenance PATCH**. `v1.1.2` fixes four verified, externally-reported defects — the **first real use of the in-repo bug ledger** ([ADR-0039](../adr/0039-bug-ledger-and-triage-protocol.md)) — plus the accumulated documentation work since `v1.1.1`. The library's **public surface is unchanged** (no API/ABI change); the fixes are header-only or core internals. + +## Fixed + +- **[BUG-0001](../../docs/bugs/2026/06/BUG-0001-instrumented-pool-growth-counter-data-race.md) — `InstrumentedPool` growth-counter data race (high).** `notify_if_grew()` read+wrote the non-atomic `last_growths_` on the allocation hot path, racing under the documented-safe `MUTEX` + dynamic-growth + concurrent configuration (UB). `last_growths_` is now `std::atomic`, advanced with a `compare_exchange` so `grew` fires once per growth without a race. A concurrent `InstrumentedPool` case was added to the ThreadSanitizer stress suite (the coverage gap that hid it). +- **[BUG-0002](../../docs/bugs/2026/06/BUG-0002-instrumented-pool-live-counter-underflow.md) — `live_` counter underflow (medium).** `deallocate()` decremented the unsigned `live_` for any non-null pointer, so a foreign or double-freed pointer (a no-op in the core, [ADR-0012](../adr/0012-foreign-pointer-and-out-of-range-pointer-policy.md)) wrapped it to `SIZE_MAX`. The decrement now clamps at zero. The C header's double-free note was corrected — the Decorator counts deallocations but does **not** detect a double-free. +- **[BUG-0003](../../docs/bugs/2026/06/BUG-0003-instrumented-pool-move-assign-missing-destroyed-event.md) — missing `destroyed` event on move-assignment (low).** Move-assigning over an instrumented pool released its `Pool` and observers without emitting `PoolEvent::destroyed`, asymmetric with the destructor. It now notifies before reassignment. +- **[BUG-0004](../../docs/bugs/2026/06/BUG-0004-grow-pool-growth-size-overflow.md) — unguarded `grow_pool` overflow (low/latent).** `total * (grow_factor_ - 1)` was computed before any overflow check; a `would_overflow_product` guard was added first, mirroring the create path. Not runtime-reachable (RAM exhausts first), so no test — a consistency fix. + +## Also in this release + +- The redundant **`docs-site` CI badge** was removed from the README header (all three locales); the published Doxygen site is still linked from the **API reference** badge. +- The **`zh-Hans` / `ja` README translations** were re-synced to the `v1.1.1` status. + +## Architecture Decision Records + +No new ADRs — `v1.1.2` is bug fixes and documentation, governed by existing decisions ([ADR-0034](../adr/0034-post-release-maintenance-protocol.md) maintenance protocol, [ADR-0039](../adr/0039-bug-ledger-and-triage-protocol.md) bug ledger). The running total stays **40**. + +## Spec Coverage Map + +**No change** — all fifteen rows remain ✅ ([ADR-0029](../adr/0029-spec-compliance-acceptance-audit.md)). The fixes correct decorator/diagnostic behaviour and a latent guard, not a spec requirement. + +## What this release does **not** contain + +- **No public-API/ABI change** — `Pool`, `TypedPool`, `PoolAllocator`, the C ABI, and the compile-time knobs are unchanged. +- **No double-free *detection*** — `v1.1.2` corrects the over-promising header note; genuine double-free detection in the Decorator remains possible future work (it would be a feature, not a patch). + +## Verifying the release + +Each platform tarball is the same complete `cmake --install` tree as `v1.1.1` (full headers + static archive + CMake package config + pkg-config `.pc`). SHA-256 checksums are in `SHA256SUMS`: + +```bash +sha256sum --check SHA256SUMS +``` + +## Links + +- Changelog entry: [`CHANGELOG.md` — `[1.1.2]`](../changelog/v1/v1.1.2.md) +- Bug ledger: [`docs/bugs/`](../../docs/bugs/) +- Maintenance protocol: [`docs/workflow/maintenance.md`](../../docs/workflow/maintenance.md) +- Previous release: [`docs/releases/v1.1.1.md`](v1.1.1.md) diff --git a/src/main/cpp/it/d4np/memorypool/version.hpp b/src/main/cpp/it/d4np/memorypool/version.hpp index a092878..b01d1e2 100644 --- a/src/main/cpp/it/d4np/memorypool/version.hpp +++ b/src/main/cpp/it/d4np/memorypool/version.hpp @@ -23,10 +23,10 @@ inline constexpr unsigned PBR_MEMORY_POOL_VERSION_MAJOR = 1; inline constexpr unsigned PBR_MEMORY_POOL_VERSION_MINOR = 1; /** Patch version component (incremented for hotfixes between milestones). */ -inline constexpr unsigned PBR_MEMORY_POOL_VERSION_PATCH = 1; +inline constexpr unsigned PBR_MEMORY_POOL_VERSION_PATCH = 2; /** Pre-formatted version string, kept in lockstep with the components above. */ -inline constexpr const char* PBR_MEMORY_POOL_VERSION_STRING = "1.1.1"; +inline constexpr const char* PBR_MEMORY_POOL_VERSION_STRING = "1.1.2"; } // namespace it::d4np::memorypool diff --git a/src/test/cpp/it/d4np/memorypool/pool_smoke_test.cpp b/src/test/cpp/it/d4np/memorypool/pool_smoke_test.cpp index 640a0be..131168a 100644 --- a/src/test/cpp/it/d4np/memorypool/pool_smoke_test.cpp +++ b/src/test/cpp/it/d4np/memorypool/pool_smoke_test.cpp @@ -56,12 +56,12 @@ TEST_CASE("version constants are consistent with the project version") { const std::string_view ver{mem::PBR_MEMORY_POOL_VERSION_STRING}; CHECK(ver.find('.') != std::string_view::npos); - // v1.1.1 — maintenance PATCH: docs/process/tooling only (bug ledger, - // PR-metadata policy, and the accumulated post-1.1.0 docs/CI entries); the - // library binary is unchanged. These constants move in lockstep with version.hpp. + // v1.1.2 — maintenance PATCH: fixes four InstrumentedPool/core defects + // (BUG-0001..0004) plus the accumulated docs entries; the library's public + // surface is unchanged. These constants move in lockstep with version.hpp. CHECK(mem::PBR_MEMORY_POOL_VERSION_MAJOR == 1U); CHECK(mem::PBR_MEMORY_POOL_VERSION_MINOR == 1U); - CHECK(mem::PBR_MEMORY_POOL_VERSION_PATCH == 1U); + CHECK(mem::PBR_MEMORY_POOL_VERSION_PATCH == 2U); } TEST_CASE("memory_pool_create / _destroy round-trip on valid arguments") {