Skip to content

Remove heap allocations from Scoped<T> reference counting#780

Draft
Copilot wants to merge 5 commits intomainfrom
copilot/implement-scope-without-heap-allocation
Draft

Remove heap allocations from Scoped<T> reference counting#780
Copilot wants to merge 5 commits intomainfrom
copilot/implement-scope-without-heap-allocation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 19, 2026

Scoped<T> was allocating on the heap for internal ref-count increments/decrements because each lifetime change created a new ReferenceCount<T> instance. This change keeps only the lifetime object allocated while moving the scope’s ref-count state to an allocation-free atomic implementation.

  • Allocation-free scoped ref counting

    • Replace Scoped<T>'s copy-on-write ReferenceCount<T> updates with a single atomic state field.
    • Encode disposed state and active reference count in that field so lifetime acquisition/release no longer allocates.
    • Preserve existing semantics for double-dispose, disposal races, and “dispose only after last lifetime” behavior.
  • Lifetime<T> support for scoped fast path

    • Extend Lifetime<T> internals to support a direct releaser callback path for Scoped<T>.
    • Keep the existing ReferenceCount<T>-based constructor for other callers unchanged.
  • Compatibility / edge cases

    • Preserve behavior for disposed default scopes created by the atomic scoped factory path.
    • Keep the public API surface additive-neutral; the change is internal to the implementation.
  • Regression coverage

    • Add a scoped allocation regression test that asserts repeated CreateLifetime() calls do not reintroduce heap allocation for internal ref counting.
var scope = new Scoped<Disposable>(new Disposable());

using var lifetime1 = scope.CreateLifetime();
using var lifetime2 = scope.CreateLifetime();

// lifetime creation still works the same for callers,
// but internal ref-count updates no longer allocate.

Copilot AI and others added 4 commits April 19, 2026 02:47
Agent-Logs-Url: https://github.com/bitfaster/BitFaster.Caching/sessions/dc652e79-402a-48ab-b01d-7744991e75c4

Co-authored-by: bitfaster <12851828+bitfaster@users.noreply.github.com>
Agent-Logs-Url: https://github.com/bitfaster/BitFaster.Caching/sessions/dc652e79-402a-48ab-b01d-7744991e75c4

Co-authored-by: bitfaster <12851828+bitfaster@users.noreply.github.com>
Agent-Logs-Url: https://github.com/bitfaster/BitFaster.Caching/sessions/dc652e79-402a-48ab-b01d-7744991e75c4

Co-authored-by: bitfaster <12851828+bitfaster@users.noreply.github.com>
@coveralls
Copy link
Copy Markdown

Coverage Status

coverage: 99.115% (+0.08%) from 99.036% — copilot/implement-scope-without-heap-allocation into main

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.

3 participants