Skip to content

Add O(1) indexed service slots to execution_context#249

Merged
mvandeberg merged 1 commit intocppalliance:developfrom
mvandeberg:pr/indexed-services
Apr 1, 2026
Merged

Add O(1) indexed service slots to execution_context#249
mvandeberg merged 1 commit intocppalliance:developfrom
mvandeberg:pr/indexed-services

Conversation

@mvandeberg
Copy link
Copy Markdown
Contributor

@mvandeberg mvandeberg commented Apr 1, 2026

find_service() and use_service() previously acquired a mutex and walked an O(n) linked list on every call. This becomes a bottleneck when services are looked up frequently at runtime (e.g. per-request timer construction). Add a fixed-size array of 32 atomic service pointers indexed by a per-type slot ID, giving lock-free O(1) reads on the fast path. The linked-list registry remains as a fallback for overflow.

Summary by CodeRabbit

  • Performance Improvements

    • Added a lock-free fast path for service lookup using per-type slot IDs, speeding access to commonly used services and reducing contention in concurrent workloads.
  • Bug Fixes

    • Ensures service slot entries are cleared when a context is destroyed to avoid stale pointers and improve reliability.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d12880ea-3e3d-4ae8-84f3-e4a6b0ff645b

📥 Commits

Reviewing files that changed from the base of the PR and between 0323257 and d00a033.

⛔ Files ignored due to path filters (2)
  • doc/unlisted/execution-contexts.adoc is excluded by !**/doc/**
  • test/unit/ex/execution_context.cpp is excluded by !**/test/**
📒 Files selected for processing (3)
  • include/boost/capy/detail/service_slot.hpp
  • include/boost/capy/ex/execution_context.hpp
  • src/ex/execution_context.cpp
✅ Files skipped from review due to trivial changes (1)
  • include/boost/capy/detail/service_slot.hpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/ex/execution_context.cpp
  • include/boost/capy/ex/execution_context.hpp

📝 Walkthrough

Walkthrough

A per-type slot-ID generator (service_slot<T>()) was added and execution_context gained a fixed-size atomic slots_[] array (capacity 32). Fast-path atomic lookups/publishes use these slots (acquire/release); fallback remains mutex-protected. Slots are cleared on destruction.

Changes

Cohort / File(s) Summary
Slot Mechanism Infrastructure
include/boost/capy/detail/service_slot.hpp
New header: inline std::atomic<std::size_t> next_service_slot and template<class T> std::size_t service_slot() noexcept to assign stable per-type slot IDs.
Execution Context Interface
include/boost/capy/ex/execution_context.hpp
Added fixed-size atomic slots_[] array (capacity 32); find_service<T>(), use_service<T>(), and make_service<T, Args...>() attempt atomic fast-path using detail::service_slot<T>() (acquire loads) and include new static_assert validating key_type& convertibility; factory constructor extended to accept/store slot indices; added includes for detail/service_slot and <atomic>.
Execution Context Implementation
src/ex/execution_context.cpp
destroy() clears slots_ (relaxed); use_service_impl(factory&) and make_service_impl(factory&) publish resolved/created service* into up to two slot indices when in-bounds using memory_order_release; fast-path publication also applied on early-return paths.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Context as execution_context
    participant SlotMech as service_slot<T>
    participant Slots as slots_[]
    participant Mutex as Mutex+Registry
    participant Factory as factory

    Client->>Context: find_service<T>()
    Context->>SlotMech: compute slot id
    SlotMech-->>Context: slot index
    Context->>Slots: atomic load slots_[index] (acquire)
    alt non-null pointer
        Slots-->>Context: service*
        Context-->>Client: return service*
    else null
        Context->>Mutex: lock + lookup/create
        Mutex-->>Context: service* or create via Factory
        alt created or found
            Context->>Slots: atomic store slots_[index] (release)
            Context-->>Client: return service*
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped to add a per-type key,
Tiny slots for services, quick and free,
Atomic beats and mutex friends in queue,
Release, acquire—now lookups flew!
A rabbit's cheer for faster rendezvous 🥕

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add O(1) indexed service slots to execution_context' directly and concisely describes the main change: introducing indexed service slots with O(1) lookup performance to the execution_context component.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cppalliance-bot
Copy link
Copy Markdown

cppalliance-bot commented Apr 1, 2026

An automated preview of the documentation is available at https://249.capy.prtest3.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-04-01 21:56:48 UTC

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
include/boost/capy/ex/execution_context.hpp (1)

228-235: Don't consume cache slots on negative probes.

Line 228 assigns a process-wide slot ID the first time find_service<T>() or has_service<T>() probes T, even when no T will ever be registered. With Line 571 capping the cache at 32 entries, a feature-probe pass over absent services can permanently push later hot services onto the linked-list path in every fresh context. A non-allocating peek here, with slot assignment deferred until successful registration or backfill, would preserve the fast-path budget for actual services.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/capy/ex/execution_context.hpp` around lines 228 - 235,
find_service<T>()/has_service<T>() currently calls detail::service_slot<T>()
which assigns a process-wide slot on first probe and wastes cache entries;
change these probe sites to use a non-allocating probe API (e.g., implement and
call detail::service_slot_peek<T>() or add an allocate=false parameter to
detail::service_slot<T>()) that only returns an existing slot index (or
sentinel) without creating one, then keep the existing slot-index-based
fast-path using slots_ and max_service_slots; defer actual slot assignment to
the registration/backfill paths so only successful registrations consume slots.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@include/boost/capy/ex/execution_context.hpp`:
- Around line 269-292: The fast-path in use_service<T>() publishes T under its
key_type when get_key<T>::value is true but lacks the same safety sanity check
make_service performs; add a compile-time and/or runtime check in the
impl/factory path to ensure T& is convertible to key_type& before publishing
under get_key<T>::type (i.e. when get_key<T>::value is true), e.g. use
static_assert(std::is_convertible<T*, get_key<T>::type*>::value, "...") or an
equivalent SFINAE constraint in the impl constructor (the factory that uses
detail::type_id<T>() and detail::type_id<get_key<T>::type> and
detail::service_slot<T>), so malformed services cannot be registered and later
mis-cast by find_service<key_type>() or by a slot hit.

In `@src/ex/execution_context.cpp`:
- Around line 58-59: The teardown currently clears the linked-list registry
(head_) before nulling slots_, which can let the fast path hand out a slot
pointer being deleted; move the loop that nulls each entry in slots_ (the
for(auto& s : slots_) s.store(nullptr, std::memory_order_relaxed)) so it runs
before the delete/teardown loop and before/at the same time as head_ is made
empty, ensuring slots_ and head_ transition to empty together and preventing
re-entrancy from seeing partially-destroyed services.

---

Nitpick comments:
In `@include/boost/capy/ex/execution_context.hpp`:
- Around line 228-235: find_service<T>()/has_service<T>() currently calls
detail::service_slot<T>() which assigns a process-wide slot on first probe and
wastes cache entries; change these probe sites to use a non-allocating probe API
(e.g., implement and call detail::service_slot_peek<T>() or add an
allocate=false parameter to detail::service_slot<T>()) that only returns an
existing slot index (or sentinel) without creating one, then keep the existing
slot-index-based fast-path using slots_ and max_service_slots; defer actual slot
assignment to the registration/backfill paths so only successful registrations
consume slots.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f14bded4-e961-4404-82ec-c7e1ca116d26

📥 Commits

Reviewing files that changed from the base of the PR and between 8c2ca12 and 0323257.

⛔ Files ignored due to path filters (2)
  • doc/unlisted/execution-contexts.adoc is excluded by !**/doc/**
  • test/unit/ex/execution_context.cpp is excluded by !**/test/**
📒 Files selected for processing (3)
  • include/boost/capy/detail/service_slot.hpp
  • include/boost/capy/ex/execution_context.hpp
  • src/ex/execution_context.cpp

@cppalliance-bot
Copy link
Copy Markdown

cppalliance-bot commented Apr 1, 2026

GCOVR code coverage report https://249.capy.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://249.capy.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://249.capy.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-04-01 22:10:35 UTC

find_service<T>() and use_service<T>() previously acquired a mutex and
walked an O(n) linked list on every call. This becomes a bottleneck when
services are looked up frequently at runtime (e.g. per-request timer
construction). Add a fixed-size array of 32 atomic service pointers
indexed by a per-type slot ID, giving lock-free O(1) reads on the fast
path. The linked-list registry remains as a fallback for overflow.
@mvandeberg mvandeberg force-pushed the pr/indexed-services branch from 0323257 to d00a033 Compare April 1, 2026 21:53
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 94.11765% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.46%. Comparing base (bf08032) to head (d00a033).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
src/ex/execution_context.cpp 88.88% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #249      +/-   ##
===========================================
- Coverage    92.50%   92.46%   -0.04%     
===========================================
  Files          170      172       +2     
  Lines         9348     9414      +66     
===========================================
+ Hits          8647     8705      +58     
- Misses         701      709       +8     
Flag Coverage Δ
linux 92.38% <ø> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
include/boost/capy/detail/service_slot.hpp 100.00% <100.00%> (ø)
include/boost/capy/ex/execution_context.hpp 98.41% <100.00%> (+0.33%) ⬆️
src/ex/execution_context.cpp 93.18% <88.88%> (-1.11%) ⬇️

... and 3 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bf08032...d00a033. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mvandeberg mvandeberg merged commit d6a8e0c into cppalliance:develop Apr 1, 2026
39 of 40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants