orchestrion

package
v2.12.0-dev Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: Apache-2.0, BSD-3-Clause, Apache-2.0 Imports: 7 Imported by: 0

Documentation

Index

Constants

View Source
const Version = ""

The version of the orchestrion binary used to build the current binary, or blank if the current binary was not built using orchestrion.

Variables

This section is empty.

Functions

func CtxWithScopedValue added in v2.10.0

func CtxWithScopedValue(parent context.Context, key, val any) (context.Context, func())

CtxWithScopedValue is CtxWithValue with a matching scope exit: it pushes val under key and returns the derived context along with a cleanup that removes the entry this call pushed, plus anything still stacked above it.

Use it wherever the scope can close out of order. GLSPopValue takes the top of the stack, so a non-LIFO close pops an unrelated entry and strands its own, leaving a scope on the GLS after it ended.

The cleanup is goroutine-scoped (see GLSPopFunc): off the pushing goroutine it does nothing, so it cannot corrupt a foreign stack. Calling it more than once is harmless — after the first call the token is gone and the rest are no-ops.

When orchestrion is disabled this degrades to context.WithValue and a cleanup that does nothing.

func CtxWithValue

func CtxWithValue(parent context.Context, key, val any) context.Context

CtxWithValue runs context.WithValue, adds the result to the GLS slot of orchestrion, and returns it. If orchestrion is not enabled, it will run context.WithValue and return the result. Since we don't support cross-goroutine switch of the GLS we still run context.WithValue in the case we are switching goroutines.

func Enabled

func Enabled() bool

Enabled returns whether the current build was compiled with orchestrion or not.

func GLSActivate added in v2.10.0

func GLSActivate(ctxp *context.Context, key, val any, pop *GLSPopperCell, done *GLSDoneCell)

GLSActivate is woven into span/operation activation (the tracer's ContextWithSpan and dyngo's RegisterOperation). It pushes val onto the current goroutine's GLS stack under key and records a goroutine-scoped popper into pop, capturing it only on the first activation so re-activating the same span/operation does not overwrite the popper its matching GLSDeactivate will run. The captured popper closes the scope this push opened — that entry and anything still stacked above it — and is a no-op on any other goroutine, so a cross-goroutine finish can never corrupt an unrelated goroutine's stack.

First-wins and scope exit compose: a re-activated span keeps the popper from its first push, so deactivating removes the scope from where the span first became active, taking any later duplicate push of the same span with it.

done, when non-nil, holds the span's liveness cell, which is passed to contextStack.Push as the entry's cell. It follows the same first-wins rule as the popper: the first activation allocates the cell and every later activation of the same span reuses it, so all of the span's entries share one signal and Finish marks them together. When done is nil (dyngo operations, which never cross a goroutine boundary) the entry carries no cell and is never drained.

When ctxp is non-nil the parent context is wrapped (via WrapContext) so the returned context is also GLS-aware, matching the former in-source CtxWithValue. Everything is a no-op when orchestrion is disabled.

Grouping the wrap, push, popper-capture and cell allocation here keeps the injected templates a single call and the logic unit-testable in plain go test. The companions are GLSDeactivate (finish) and GLSReset (span-pool reuse).

func GLSDeactivate added in v2.10.0

func GLSDeactivate(done *GLSDoneCell, pop *GLSPopperCell)

GLSDeactivate releases a span's GLS entry on finish. It marks the liveness cell done and invokes the captured popper exactly once, clearing it so a repeated finish does not pop again.

The cell covers the case the popper cannot: after a cross-goroutine finish the popper is a no-op here, so the entry stays on the pushing goroutine's stack, where contextStack.Peek refuses to hand it out as the active span and the next contextStack.Push drops it.

Both activation orders are handled. Normally GLSActivate ran first and this just marks the cell it allocated. When Finish runs before the span is ever put into a context — the cross-goroutine order — there is no cell yet, so one is created already marked, and the GLSActivate that follows reuses it and pushes an entry that is drain-eligible on arrival. Without that, the stack would grow by one entry per record.

done and pop are the fields orchestrion injects onto the span; passing them by pointer lets injected span-finish advice deactivate in one call. done is nil for dyngo operations, which rely on the goroutine-scoped popper alone.

func GLSPopEntryFunc added in v2.10.0

func GLSPopEntryFunc(key any, token uint64) func()

GLSPopEntryFunc is GLSPopFunc for a key whose entries are independent scopes rather than nested ones: it removes only the entry it opened, leaving anything pushed above it to be closed by whatever owns it. Use this whenever the key is shared with a positional GLSPopValue exit, which would otherwise reach past its own scope once its entry had been swept. See [contextStack.PopEntry].

func GLSPopFunc added in v2.7.0

func GLSPopFunc(key any, token uint64) func()

GLSPopFunc returns a function that closes the scope token opened under key on the GLS context stack of the goroutine that called GLSPopFunc. token comes from the [contextStack.Push] that opened the scope.

The returned function is safe to call from any goroutine: it compares the current goroutine's GLS contextStack pointer with the one captured at creation time and only acts if they match (i.e., same goroutine). On a different goroutine it is a no-op, preventing accidental corruption of another goroutine's GLS state — which is also why the liveness cell on each [entry] still has to exist: a cross-goroutine finish cannot reach the foreign slice at all, so those entries are only ever cleaned up lazily.

The exit is by token, not by position, so an out-of-order close removes the entry it actually opened (plus anything still stacked above it, which was opened inside that scope) rather than whatever is on top. A token that has already been removed matches nothing, so a late or repeated call does nothing.

func GLSPopValue

func GLSPopValue(key any) any

GLSPopValue pops the value from the GLS slot of orchestrion and returns it. Using context.Context values usually does not require to pop any stack because the copy of each previous context makes the local variable in the scope disappear when the current function ends. But the GLS is a semi-global variable that can be accessed from any function in the stack, so we need to pop the value when we are done with it.

This takes the top of the stack, so it is only correct for keys whose scopes close strictly LIFO. Anything else must use CtxWithScopedValue.

func GLSReset added in v2.10.0

func GLSReset(done *GLSDoneCell, pop *GLSPopperCell)

GLSReset clears the GLS bookkeeping fields orchestrion injects onto a span so that a span returned to the tracer's pool and later reused starts clean: no stale popper, and no cell describing a scope that belonged to the previous lifecycle. It is woven into Span.clear.

Clearing done only drops the span's pointer to the cell. Stack entries keep their own, so the true GLSDeactivate stored is still visible to the drain long after the span object has been handed to an unrelated scope, and the reused span gets a fresh cell on its next activation. Resetting a bit on the span instead would flip those entries back to live — the ABA. done is nil for dyngo operations, which carry no cell.

func GLSStackDepth added in v2.7.0

func GLSStackDepth() int

GLSStackDepth returns the total number of entries in the current goroutine's GLS context stack. Returns 0 if orchestrion is not enabled. This is intended for use in tests to detect GLS leaks.

func MockGLS added in v2.7.0

func MockGLS() func()

MockGLS sets up a mock GLS for testing and returns a cleanup function that restores the original state. It enables orchestrion and configures a fresh contextStack accessible via the standard getDDGLS/setDDGLS functions.

This is intended for use by tests in packages that depend on orchestrion (e.g., internal, ddtrace/tracer). Follows the same pattern as telemetry.MockClient in internal/telemetry/globalclient.go.

Tests using MockGLS must NOT use t.Parallel, as it mutates package-level variables without synchronization.

func MockGLSPerGoroutine added in v2.7.0

func MockGLSPerGoroutine() func()

MockGLSPerGoroutine sets up per-goroutine GLS isolation for testing. Unlike MockGLS which uses a single shared GLS, this mock uses goroutine IDs to give each goroutine its own independent contextStack, simulating the real orchestrion runtime behavior where each runtime.g has its own GLS slot.

This is required for tests that spawn goroutines and need to verify cross-goroutine GLS behavior (e.g., GLSPopFunc no-op on wrong goroutine).

Tests using MockGLSPerGoroutine must NOT use t.Parallel, as it mutates package-level variables without synchronization.

func WrapContext

func WrapContext(ctx context.Context) context.Context

WrapContext returns the GLS-wrapped context if orchestrion is enabled, otherwise it returns the given parameter.

Types

type GLSDoneCell added in v2.10.0

type GLSDoneCell struct {
	// contains filtered or unexported fields
}

GLSDoneCell holds the liveness cell for a span's current GLS lifecycle. It is the type orchestrion injects as the __dd_glsDone field on Span (via add-struct-field, which requires a named type).

One *atomic.Bool cell is allocated on a span's first activation and shared by every later activation of that span, so all of the span's stack entries observe a single liveness signal and are marked done together at Finish. Each entry keeps its own pointer to the cell.

The indirection is what decouples the signal from the span. When the span pool recycles the span, GLSReset clears this field — but the stack entries still hold the cell, so the true set by GLSDeactivate outlives the span's lifecycle and the next Push drains them. The reused span starts with a nil pointer and allocates a fresh cell on its next activation, so a recycled span can never report a scope it did not open. Storing the bit on the span instead lets clear flip it back to false, which is the ABA this design removes.

The zero value is ready to use.

type GLSPopper added in v2.10.0

type GLSPopper func()

GLSPopper releases a span's GLS entry. It is the goroutine-scoped popper captured at activation (via GLSPopFunc) and stored, atomically, in a GLSPopperCell.

type GLSPopperCell added in v2.10.0

type GLSPopperCell struct {
	// contains filtered or unexported fields
}

GLSPopperCell holds a GLSPopper atomically. It is the type orchestrion injects as the popper field on Span and dyngo's operation (via add-struct-field, which requires a named type). Storing the popper in an atomic pointer makes the woven paths race-free: GLSDeactivate (woven into Span.Finish) and GLSReset (woven into Span.clear) can run concurrently on the same field when a span is finished on one goroutine while the tracer's span pool recycles it on another, and a repeated finish must run the popper at most once. The zero value is ready to use; a nil inner pointer means no popper is currently captured.

Directories

Path Synopsis
This program generates the `orchestrion.tool.go` file at `./orchestrion/all` from the root of the repository, which contains the necessary directives to facilitate onboarding of orchestrion.
This program generates the `orchestrion.tool.go` file at `./orchestrion/all` from the root of the repository, which contains the necessary directives to facilitate onboarding of orchestrion.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL