Hi everyone,
I'd like to open a discussion about adding a lightweight, public API for reading the current Swift Task's identity — something analogous to pthread_self() for threads, but for Tasks.
Background
We've been building a high-performance, always-on span capture and structured logging library for Swift (not yet open source, but will be eventually). The design targets workloads where instrumentation is on the critical path — real-time systems, trading infrastructure, game engines, audio pipelines — and where the overhead of swift-distributed-tracing or swift-log would be measurable. A full span capture (timestamp, thread ID, CPU ID, SBE-encoded structured payload, lock-free ring-buffer write) completes in ~25 ns on Apple Silicon with zero allocations and zero contention enabling always-on detailed production instrumentation for both spans and logs.
To be clear — swift-log and swift-distributed-tracing are the right choice for most Swift applications. We specifically needed always-on instrumentation for production workloads where even a NoOpTracer at ~243 ns per withSpan is measurable, and where StreamLogHandler with two metadata fields at ~1,700 ns per call would perturb the workload. Different constraints, different trade-offs.
As part of this, we capture thread ID and CPU ID on every emit. We also capture Task identity — which Swift Task produced a given span or log entry. In concurrent Swift applications this is essential for correlating spans and logs.
We achieve this today by calling swift_task_getCurrent via @_silgen_name and using the returned pointer as an opaque identity token. The pointer is never dereferenced — it's purely a cache key for a per-thread TLS lookup. On cache hit (steady state), the cost is ~2 ns. On cache miss (task transition on a thread), we read Task.name (SE-0469) once, register the name in a compact table, and all subsequent emits from that task use a cached slot ID.
This approach works well and the Task identity feature has been very valuable for production diagnostics. But we're relying on a reserved runtime symbol, and this is clearly unsupported and will eventually become an error:
warning: symbol name 'swift_task_getCurrent' is reserved for the Swift runtime and cannot be directly referenced without causing unpredictable behavior; this will become an error
Why the public APIs don't work here
The existing public surface for accessing the current Task is withUnsafeCurrentTask { }, which uses a closure. We benchmarked every plausible approach on an M4 Max (using package-benchmark):
| Approach | p50 (ns) | Retired instructions |
|---|---|---|
swift_task_getCurrent (raw runtime call) |
2 | 31 |
withUnsafeCurrentTask { } (noop closure) |
6 | 120 |
withUnsafeCurrentTask + unsafeBitCast to extract identity |
39 | 382 |
withUnsafeCurrentTask + hashValue |
60 | 534 |
Full miss: hashValue + Task.name |
109 | 1,170 |
The cheapest public-API path to get a comparable identity token is ~20x more expensive than the raw runtime call. When the entire span capture budget is ~25 ns, adding 40–60 ns for task identity alone is not viable — it would more than triple the cost of every emit.
For context, reading a hardware timestamp is a few ns, and reading the thread ID costs ~1 ns. Task identity should preferably be in the same ballpark.
What would help
A non-closure API that returns a cheap, stable task identifier. Something like:
extension Task {
/// A lightweight identifier for the current task, or nil if not
/// in a task context. Unique for the task's lifetime. O(1), no allocation.
public static var currentIdentifier: Task.Identifier? { get }
}
Requirements
- ~1–5 ns — comparable to a TLS read
- Non-allocating — no ARC traffic
- Stable — same value for the task's lifetime
- @inlinable-friendly — so libraries can inline it into client call sites
It does not need to be globally unique (pointer reuse after task deallocation is fine — same as thread IDs), and it does not need to carry semantic meaning beyond equality comparison.
Broader applicability
While our specific use case is high-performance tracing, I think a cheap task identifier would be useful more broadly:
- Profilers and performance tools that need to attribute work to tasks
- Lock-free data structures that use task identity for ownership tracking
- Diagnostic logging that wants to tag output with task identity without the overhead of the closure-based API
- Custom executors that need to track which task is running
The underlying runtime primitive (swift_task_getCurrent) already exists and does exactly the right thing. The ask is really about making it — or something equivalent — part of the supported public API surface.
I've filed a GitHub issue (Provide a cheap, stable Task identity primitive for high-performance instrumentation · Issue #89030 · swiftlang/swift · GitHub) to track this also. Happy to provide more data or discuss trade-offs.
Joakim