Skip to content

Nothrow: Substitutes for standard C++ containers and memory resources, with pluggable pointer storage policies#272

Open
MichaelSteffens17 wants to merge 49 commits into
eclipse-score:mainfrom
MichaelSteffens17:main
Open

Nothrow: Substitutes for standard C++ containers and memory resources, with pluggable pointer storage policies#272
MichaelSteffens17 wants to merge 49 commits into
eclipse-score:mainfrom
MichaelSteffens17:main

Conversation

@MichaelSteffens17

@MichaelSteffens17 MichaelSteffens17 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Part of #167

MichaelSteffens17 and others added 30 commits June 1, 2026 10:47
…r comparisons.

  1. Emplace — true in-place construction

   - Added a Node(parent, std::in_place_t, Args&&...) constructor and AllocateNode. Emplace to construct the value_type directly in allocated node memory
   - Emplace now allocates the node first, constructs in-place, then searches the tree; destroys the node if a duplicate key is found
   - Eliminates the intermediate stack temporary + move that the old implementation used

  2. clear() — no freed-pointer comparisons

   - Replaced the previous-tracking traversal (which compared freed addresses — UB per [basic.stc]/4) with a descend-to-leaf approach
   - Each iteration descends left/right until hitting a leaf, nulls the parent's link, then destroys the leaf and backs up to the parent
   - All pointer comparisons are against live objects only
…o container algorithm without UB. Add combined benchmark.
- Shared insert-position search (FindInsertPosition)
- Consolidated key comparison (CompareKey) used in insert/find
- Added specialized 2-arg Emplace(...) overloads to avoid pre-construct-and-destroy on duplicates
- RebalanceFrom(..., stop_on_stable_height) early-exit for insert flows
… final equality check, and reused that for insert-position search (removing extra compare/branch work). I also added a std::greater<int> regression test in score/shm/map_test.cpp.
…plus OrAbort variants), with std-like neighbor checks and fallback.

Sorted fast path via maintained leftmost_/rightmost_ and boundary-aware insertion checks, used in Create(range) and Clone() with rolling hint.
Udated score/shm/BENCHMARK.md with the full latest suite results from the current codebase.
…hrow with pointer box renames.

Move score/shm/ to score/nothrow/ to reflect that these containers and
memory resources are nothrow std-container substitutes, not
shared-memory-specific.

Rename pointer storage types to reflect their role as encoding boxes:
- OffsetPtr -> OffsetBox
- NullableOffsetPtr -> NullableOffsetBox
- DirectPtr -> RawBox (serves as both Ptr and NullablePtr)
- ShmPointerPolicy -> OffsetBoxPolicy
- ShmDirectPointerPolicy -> RawBoxPolicy
- offset_ptr.h -> pointer_box.h

Update all include paths, header guards, namespaces, BUILD targets,
documentation, and example code.

Add reverse iterators (rbegin/rend/crbegin/crend), ordered lookup
methods (count, lower_bound, upper_bound, equal_range), iterator-based
erase overloads (const_iterator and range), and equality operators.

Fix GetOrInsertDefault to use Emplace instead of constructing an
intermediate value_type, avoiding an unnecessary key copy.

All new members follow the naming conventions from score/shm/README.md:
non-allocating operations use standard snake_case spelling.
Updated the benchmark analysis section to clarify the overhead cause and corrected terminology from 'OffsetBox' to 'OffsetPtr'.

Signed-off-by: MichaelSteffens17 <michael.steffens@vector.com>
…ffsetBox.

Signed-off-by: MichaelSteffens17 <michael.steffens@vector.com>
Signed-off-by: MichaelSteffens17 <michael.steffens@vector.com>
Signed-off-by: MichaelSteffens17 <michael.steffens@vector.com>
Signed-off-by: MichaelSteffens17 <michael.steffens@vector.com>
…irly to NoBoundsCheck timings. Add boost::interprocess container and completed score::memory::shared::Map benchmarks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MichaelSteffens17

MichaelSteffens17 commented Jun 17, 2026 via email

Copy link
Copy Markdown
Contributor Author

Provision a Map's node storage directly from an external buffer, bypassing
the templated allocator the same way Vector::CreateWithBuffer does. The map
manages an internal free-list pool over the buffer: nodes are carved on
insert and recycled on erase, bounding the map to a fixed node count.

All pool state is position-independent -- the cell base is stored through the
pointer policy (offset-encoded by default) and the free-list head/capacity are
indices -- so a buffer-backed map with OffsetSlotPolicy is relocatable and can
be both read and mutated across processes mapping the same segment, without the
vtable, raw buffer pointer, and process-local upstream singleton that a
polymorphic MemoryResource would drag along.

- map.h: PoolCell union + CreateWithBuffer(), uses_buffer(), cell_size(),
  cell_alignment(), required_bytes(); pool-routed AllocateNode/DestroyNode;
  move ctor/assignment extended to carry pool state.
- map_test.cpp: tests for empty/insert/find, capacity bound, freed-cell reuse,
  move, and OrAbort-on-exhaustion death test.
- bounded_containers.h: convert the shared-memory example bundle to the
  allocator-free buffer API.
- README.md: document the cross-process map mutation capability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MichaelSteffens17 and others added 3 commits June 25, 2026 17:30
The child now inserts new keys into the parent's map, exercising node
allocation from the shared free-list pool across processes (not just in-place
value updates). The parent observes the grown map (size 3 -> 5) after remapping
the segment, confirming pooled allocation is position-independent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A buffer not aligned to cell_alignment() would misalign the pooled nodes.
That is a caller precondition violation, so trap it with std::abort() like the
map's other contract checks rather than returning an error. Add a death test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reflect the child's new insertion step: the README now describes node
allocation from the shared free-list pool across processes (map grows 3 -> 5),
not just in-place value updates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MichaelSteffens17 and others added 7 commits July 13, 2026 09:52
The error/violation categorization was defined only inside the component
request, a proposal-stage document. Give it a durable home as a dedicated
architecture document (doc__nothrow_failure_handling, realizes
wp__component_arch) so requirements, README, and the CR can reference one
normative definition instead of duplicating it:

- architecture/failure_handling.rst: full model -- category definitions, the
  API-choice rule, and the out-of-scope rationale for process termination.
- architecture/index.rst: reference the model from Design Principles and add
  the toctree entry.
- index.rst (CR): condense the section to a summary plus a :need: reference.
- score/nothrow/README.md: cross-reference the model so code-side readers
  find the defined terms behind the API conventions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The four requirements that encode the failure categories now state which
category they realize and link doc__nothrow_failure_handling: alloc_fail,
create, and mutate_result realize the error category; or_abort realizes the
violation category. pointer_policy and asil are unrelated and unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The architectural motivation for error-as-default was buried inside the
out-of-scope termination subsection. Restructure failure_handling.rst:

- New section "Memory need and memory budget are separated": budget is sized
  by configuration at integration time while need arises at run time; input
  validation at trust boundaries happens in a different component than the
  allocation; resources are shared to limit config parameters, coupling
  consumers; consumption logic evolves independently in reusable parts. The
  resulting asymmetry -- practically impossible to prove sizing correct by
  testing, easy for an attacker to trigger exhaustion -- is why allocation
  failure is a reachable runtime condition (error) by default. The per-request
  recovery example moves here as the payoff.
- "Choosing between the two APIs" now closes with the co-located-budget
  criterion: only where the separations are deliberately eliminated is a
  violation classification legitimate.
- "Out of scope: process-level termination" shrinks to a pure scope statement.
- CR: summary names the need/budget separation; Security Impact cites the
  model for the DoS argument instead of restating it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Side-by-side comparison in the C++ standardization-paper style, requested in
the component review (issue eclipse-score#167 sync). Eight scenarios, each mapped onto the
failure handling model, with the standard-container cell degrading from
fragile (masked capacity defects, pseudo-recoverable at()) through terminating
(construction, growth, attacker-sized input) to inexpressible (pre-flight
budget check, failure-path testing, shared-memory placement).

Ground rules keep it honest: the standard cell always shows the strongest
available attempt (std::pmr, reserve-first, boost::interprocess for SHM), and
every row states the no-exceptions environment assumption.

Scoped explicitly as an input to the planned "when to use which container"
guideline, not the guideline itself -- that must additionally weigh the
pointer-slot policy choice and third-party interop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aints

Add the "when to use which container" guideline requested in the component
review (doc__nothrow_usage_guidelines), covering both decisions:

- Container choice: standard by default where its failure semantics are
  acceptable; score::nothrow when allocation failure must be a contained,
  testable error, the process must survive memory pressure, a budget contract
  should trap at the defect site, or state is placed in shared memory. Within
  nothrow, Result APIs by default, OrAbort only for co-located budgets.
- Pointer policy: OffsetSlotPolicy by default -- one generic container type
  passed by reference serves both shared-memory IPC and process-local buffers
  (e.g. socket-backed) without adapting application code to the transport,
  at measured ~1-14% worst-case overhead (BENCHMARK.md), far below the
  alternatives (type duplication, erasure, barrier-based fancy pointers).
  RawSlotPolicy only for closed single-address-space subsystems with a
  profiled hot path, accepting the type fork it causes.
- Decision summary table as quick reference.

Also reword the "not general-purpose replacements" claim in the comparison
document and the guideline: it stated an organizational rollout constraint
(std required at third-party boundaries, targeted adoption) as if it were a
property of the types, which our own failure analysis contradicts. The claims
now describe where the types can be deployed today, not their generality.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…the CR

Clarifies the scope questions raised in the issue eclipse-score#167 allocator meeting
(2026-06-25) and discharges its TODO to document usage examples in the
component request. New CR section "Scope and Relation to Communication
Modelling", placed after Rationale:

- nothrow types are library types linked into an executable, not a platform
  service or communication stack.
- The only interprocess contract is the binary representation of container
  state in shared memory (standard layout, offset pointer slots, bounded
  objects with dynamic content). PMR memory management does not cross that
  boundary; provisioning stays with the memory-aware side (CreateWithBuffer),
  the allocator being type-compatibility decoration in that scenario.
- Two separable aspects: stable binary representation (ABI) and API (error
  propagation, provisioning hidden behind a generic allocator, IPC-unaware
  interfaces without payload copying or rebuilds).
- No relation to communication modelling (AUTOSAR-AP style): score's decision
  not to model executables/components is neither changed nor depended on. The
  types can appear as elements in generator output of such a model, and work
  equally in pure API-driven configurations.
- Usage scenarios from the review: event (sender-owned pool, one-to-many),
  method (one-to-one, ownership passing intra-process incl. C++/Rust via
  opaque resource references), gateway serialization (SOME/IP).

Architecture overview gets a one-line pointer to the CR scope section.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add resource_scopes.cpp, the failure-handling decision rule in runnable form
and the executable companion to the API aspect of the CR scope section (the
SHM demo covers the binary-representation aspect):

- Co-located budget: local buffer + MonotonicBufferResource in the consuming
  scope, populated with OrAbort -- failure could only be a bug and traps at
  the defect site.
- Dislocated budget: main() sizes the process-wide default resource centrally
  (standing in for integration-time configuration); business logic returns
  score::Result, an oversized request is rejected as a contained error, and
  the process keeps serving. Also demonstrates SetDefaultResource, previously
  unexercised outside tests.

Reference the runnable examples from the CR (scope section and How to Teach
This), the usage guidelines, and example/README.md, discharging the review
TODO to document usage examples in the component request.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scenario 8 (shared-memory placement) contradicted the "failure semantics
only" claim. The note now states failure semantics as the main focus and
scopes scenario 8: the pointer-slot policy affects container construction and
cross-process synchronization, but not API use or failure robustness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants