Skip to content

Commit 9484f4b

Browse files
committed
Show the only way we can implement withSpan correctly inside an actor
1 parent aabe8eb commit 9484f4b

File tree

2 files changed

+137
-12
lines changed

2 files changed

+137
-12
lines changed

Sources/Tracing/Tracer.swift

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import Dispatch
16+
import Distributed
1617
@_exported import Instrumentation
1718
@_exported import ServiceContextModule
1819

@@ -382,7 +383,7 @@ public func withSpan<T>(
382383
function: String = #function,
383384
file fileID: String = #fileID,
384385
line: UInt = #line,
385-
_ operation: @Sendable (any Span) async throws -> T
386+
_ operation: (any Span) async throws -> T
386387
) async rethrows -> T {
387388
try await InstrumentationSystem.legacyTracer.withAnySpan(
388389
operationName,
@@ -477,7 +478,7 @@ public func withSpan<T>(
477478
function: String = #function,
478479
file fileID: String = #fileID,
479480
line: UInt = #line,
480-
_ operation: @Sendable (any Span) async throws -> T
481+
_ operation: (any Span) async throws -> T
481482
) async rethrows -> T {
482483
try await InstrumentationSystem.legacyTracer.withAnySpan(
483484
operationName,
@@ -491,4 +492,114 @@ public func withSpan<T>(
491492
try await operation(anySpan)
492493
}
493494
}
495+
496+
// FIXME: type checker does not understand that AnyActor shall suffice for checking isolation domains,
497+
// thus we can't declare the method once, but have to duplicate it for every actor kind:
498+
//@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
499+
//extension AnyActor {
500+
// public func withSpanWorkaround<T>(
501+
// _ operationName: String,
502+
// context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
503+
// ofKind kind: SpanKind = .internal,
504+
// at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now,
505+
// function: String = #function,
506+
// file fileID: String = #fileID,
507+
// line: UInt = #line,
508+
// _ operation: (any Span) async throws -> T
509+
// ) async rethrows -> T {
510+
// fatalError("Can't use this, must extend both types separately")
511+
// }
512+
//}
513+
514+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
515+
extension Actor {
516+
517+
public func withSpanWorkaround<T>(
518+
_ operationName: String,
519+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
520+
ofKind kind: SpanKind = .internal,
521+
at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now,
522+
function: String = #function,
523+
file fileID: String = #fileID,
524+
line: UInt = #line,
525+
_ operation: (any Span) async throws -> T
526+
) async rethrows -> T {
527+
let span = InstrumentationSystem.legacyTracer.startAnySpan(
528+
operationName,
529+
context: context(),
530+
ofKind: kind,
531+
at: instant(),
532+
function: function,
533+
file: fileID,
534+
line: line
535+
)
536+
defer { span.end() }
537+
do {
538+
return try await ServiceContext.$current.withValue(span.context) {
539+
try await operation(span)
540+
}
541+
} catch {
542+
span.recordError(error)
543+
throw error // rethrow
544+
}
545+
// // FIXME: can't use withSpan to implement this... must use startSpan and duplicate much code
546+
// try await InstrumentationSystem.legacyTracer.withAnySpan(
547+
// operationName,
548+
// at: instant(),
549+
// context: context(),
550+
// ofKind: kind,
551+
// function: function,
552+
// file: fileID,
553+
// line: line
554+
// ) { anySpan in
555+
// try await operation(anySpan)
556+
// }
557+
}
558+
}
559+
560+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
561+
extension DistributedActor {
562+
563+
public func withSpanWorkaround<T>(
564+
_ operationName: String,
565+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
566+
ofKind kind: SpanKind = .internal,
567+
at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now,
568+
function: String = #function,
569+
file fileID: String = #fileID,
570+
line: UInt = #line,
571+
_ operation: (any Span) async throws -> T
572+
) async rethrows -> T {
573+
let span = InstrumentationSystem.legacyTracer.startAnySpan(
574+
operationName,
575+
context: context(),
576+
ofKind: kind,
577+
at: instant(),
578+
function: function,
579+
file: fileID,
580+
line: line
581+
)
582+
defer { span.end() }
583+
do {
584+
return try await ServiceContext.$current.withValue(span.context) {
585+
try await operation(span)
586+
}
587+
} catch {
588+
span.recordError(error)
589+
throw error // rethrow
590+
}
591+
// // FIXME: can't use withSpan to implement this... must use startSpan and duplicate much code
592+
// try await InstrumentationSystem.legacyTracer.withAnySpan(
593+
// operationName,
594+
// at: instant(),
595+
// context: context(),
596+
// ofKind: kind,
597+
// function: function,
598+
// file: fileID,
599+
// line: line
600+
// ) { anySpan in
601+
// try await operation(anySpan)
602+
// }
603+
}
604+
}
494605
#endif

Tests/TracingTests/ActorTracingTests.swift

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
@testable @_spi(Locking) import Instrumentation
15+
@testable import Instrumentation
1616
import ServiceContextModule
1717
import Tracing
18+
import Distributed
1819
import XCTest
1920

2021
final class ActorTracingTests: XCTestCase {
@@ -27,17 +28,30 @@ final class ActorTracingTests: XCTestCase {
2728
func work() async {}
2829

2930
actor Foo {
30-
let bar: LockedValueBox<Int> = .init(0)
31+
var bar = 0
32+
33+
func foo() async {
34+
var num = 0
35+
await self.withSpanWorkaround(#function) { _ in
36+
self.bar += 1
37+
await work()
38+
num += 1
39+
}
40+
}
41+
}
42+
43+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
44+
distributed actor DistributedFoo {
45+
typealias ActorSystem = LocalTestingDistributedActorSystem
46+
47+
var bar = 0
48+
3149
func foo() async {
32-
let num: LockedValueBox<Int> = .init(0)
33-
await withSpan(#function) { _ in
34-
self.bar.withValue { bar in
35-
bar += 1
36-
}
50+
var num = 0
51+
await self.withSpanWorkaround(#function) { _ in
52+
self.bar += 1
3753
await work()
38-
num.withValue { num in
39-
num += 1
40-
}
54+
num += 1
4155
}
4256
}
4357
}

0 commit comments

Comments
 (0)