Skip to content

Add ThreadContext.invalidate() to flip record valid byte in place - #19

Merged
ivoanjo merged 1 commit into
polarsignals:otel-thread-ctx-wipfrom
szegedi:otel-thread-ctx-invalidate
Aug 10, 2026
Merged

Add ThreadContext.invalidate() to flip record valid byte in place#19
ivoanjo merged 1 commit into
polarsignals:otel-thread-ctx-wipfrom
szegedi:otel-thread-ctx-invalidate

Conversation

@szegedi

@szegedi szegedi commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Adds ThreadContext.invalidate(). Writes 0 into the record's valid header byte in place.

Why

When an SDK finishes a span, clearContext() on the current async-context frame detaches the ThreadContext only from that frame. Sibling and detached-continuation frames that already inherited the reference keep holding the same JS object (and with it the same underlying native record) so if one of those gets reactivated for some reason an eBPF reader sampling those contexts still sees the finished span's trace / span IDs as active. This is admittedly not a frequent concern, but it is still a good idea to invalidate the record when we know its span is ended.

Every async-context frame holding the same ThreadContext reference observes the same shared record buffer, so a single invalidate() drops the record out of scope for every such frame at once. Readers see valid=0 and MUST ignore the record per OTEP-4947.

Test plan

  • Docker test suite (./test/run-in-docker.sh) — 48/48 passing.
  • Three new test cases: valid byte flips in place, idempotent, appendAttributes after invalidate mutates attrs_data but keeps valid=0.

When an SDK finishes a span, calling clearContext() on the current
async-context frame detaches the ThreadContext only from that frame.
Sibling and detached-continuation frames that already inherited the
reference keep holding the same JS object — and with it the same
underlying native record — so an out-of-process reader sampling those
threads still sees the finished span's trace / span IDs as active.

invalidate() writes 0 to the record's `valid` header byte in place,
using the same volatile+atomic_signal_fence protocol the constructor
and AppendAttributes() use for header bytes readers may race with.
Because every async-context frame holding this ThreadContext
reference observes the same shared record buffer, a single
invalidate() drops the record out of scope for every such frame at
once — readers see valid=0 and MUST ignore the record per OTEP-4947.

The method is idempotent, safe under repeated calls, and orthogonal
to attrs_data mutation: appendAttributes after invalidate is still
observable in the record bytes, but readers honor the valid=0 flag
regardless.
@szegedi
szegedi marked this pull request as ready for review August 6, 2026 14:52
@szegedi

szegedi commented Aug 7, 2026

Copy link
Copy Markdown
Author

@umanwizard thanks for the approval! May I ask you to merge it for me?

@ivoanjo ivoanjo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍 LGTM

@ivoanjo
ivoanjo merged commit 9f06caf into polarsignals:otel-thread-ctx-wip Aug 10, 2026
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