Skip to content

Commit 876d711

Browse files
authored
Adopt Swift 6.0 #isolation; Resolves async closure behavior in withSpan (#148)
1 parent 72d831a commit 876d711

File tree

5 files changed

+264
-2
lines changed

5 files changed

+264
-2
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ let package = Package(
88
.library(name: "Tracing", targets: ["Tracing"]),
99
],
1010
dependencies: [
11-
.package(url: "https://github.com/apple/swift-service-context.git", from: "1.0.0"),
11+
.package(url: "https://github.com/apple/swift-service-context.git", from: "1.1.0"),
1212
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
1313
],
1414
targets: [

Sources/Tracing/Tracer.swift

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,42 @@ public func withSpan<T>(
315315
/// - instant: the time instant at which the span started
316316
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
317317
/// - kind: The ``SpanKind`` of the new ``Span``.
318+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
318319
/// - function: The function name in which the span was started
319320
/// - fileID: The `fileID` where the span was started.
320321
/// - line: The file line where the span was started.
321322
/// - operation: The operation that this span should be measuring
322323
/// - Returns: the value returned by `operation`
323324
/// - Throws: the error the `operation` has thrown (if any)
325+
#if swift(>=6.0)
326+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext
327+
public func withSpan<T, Instant: TracerInstant>(
328+
_ operationName: String,
329+
at instant: @autoclosure () -> Instant,
330+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
331+
ofKind kind: SpanKind = .internal,
332+
isolation: isolated(any Actor)? = #isolation,
333+
function: String = #function,
334+
file fileID: String = #fileID,
335+
line: UInt = #line,
336+
_ operation: (any Span) async throws -> T
337+
) async rethrows -> T {
338+
try await InstrumentationSystem.legacyTracer.withAnySpan(
339+
operationName,
340+
at: instant(),
341+
context: context(),
342+
ofKind: kind,
343+
function: function,
344+
file: fileID,
345+
line: line
346+
) { anySpan in
347+
try await operation(anySpan)
348+
}
349+
}
350+
#endif
351+
352+
@_disfavoredOverload
353+
@available(*, deprecated, message: "Prefer #isolation version of this API")
324354
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext
325355
public func withSpan<T, Instant: TracerInstant>(
326356
_ operationName: String,
@@ -360,13 +390,42 @@ public func withSpan<T, Instant: TracerInstant>(
360390
/// - Parameters:
361391
/// - operationName: The name of the operation being traced. This may be a handler function, database call, ...
362392
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
363-
/// - kind: The ``SpanKind`` of the new ``Span``.
393+
/// - ofKind: The ``SpanKind`` of the new ``Span``.
394+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
364395
/// - function: The function name in which the span was started
365396
/// - fileID: The `fileID` where the span was started.
366397
/// - line: The file line where the span was started.
367398
/// - operation: The operation that this span should be measuring
368399
/// - Returns: the value returned by `operation`
369400
/// - Throws: the error the `operation` has thrown (if any)
401+
#if swift(>=6.0)
402+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext
403+
public func withSpan<T>(
404+
_ operationName: String,
405+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
406+
ofKind kind: SpanKind = .internal,
407+
isolation: isolated(any Actor)? = #isolation,
408+
function: String = #function,
409+
file fileID: String = #fileID,
410+
line: UInt = #line,
411+
_ operation: (any Span) async throws -> T
412+
) async rethrows -> T {
413+
try await InstrumentationSystem.legacyTracer.withAnySpan(
414+
operationName,
415+
at: DefaultTracerClock.now,
416+
context: context(),
417+
ofKind: kind,
418+
function: function,
419+
file: fileID,
420+
line: line
421+
) { anySpan in
422+
try await operation(anySpan)
423+
}
424+
}
425+
#endif
426+
427+
@_disfavoredOverload
428+
@available(*, deprecated, message: "Prefer #isolation version of this API")
370429
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext
371430
public func withSpan<T>(
372431
_ operationName: String,
@@ -407,12 +466,42 @@ public func withSpan<T>(
407466
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
408467
/// - kind: The ``SpanKind`` of the new ``Span``.
409468
/// - instant: the time instant at which the span started
469+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
410470
/// - function: The function name in which the span was started
411471
/// - fileID: The `fileID` where the span was started.
412472
/// - line: The file line where the span was started.
413473
/// - operation: The operation that this span should be measuring
414474
/// - Returns: the value returned by `operation`
415475
/// - Throws: the error the `operation` has thrown (if any)
476+
#if swift(>=6.0)
477+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
478+
public func withSpan<T>(
479+
_ operationName: String,
480+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
481+
ofKind kind: SpanKind = .internal,
482+
at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now,
483+
isolation: isolated(any Actor)? = #isolation,
484+
function: String = #function,
485+
file fileID: String = #fileID,
486+
line: UInt = #line,
487+
_ operation: (any Span) async throws -> T
488+
) async rethrows -> T {
489+
try await InstrumentationSystem.legacyTracer.withAnySpan(
490+
operationName,
491+
at: instant(),
492+
context: context(),
493+
ofKind: kind,
494+
function: function,
495+
file: fileID,
496+
line: line
497+
) { anySpan in
498+
try await operation(anySpan)
499+
}
500+
}
501+
#endif
502+
503+
@_disfavoredOverload
504+
@available(*, deprecated, message: "Prefer #isolation version of this API")
416505
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
417506
public func withSpan<T>(
418507
_ operationName: String,

Sources/Tracing/TracerProtocol+Legacy.swift

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,48 @@ extension LegacyTracer {
300300
/// - operationName: The name of the operation being traced. This may be a handler function, database call, ...
301301
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
302302
/// - kind: The ``SpanKind`` of the new ``Span``.
303+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
303304
/// - function: The function name in which the span was started
304305
/// - fileID: The `fileID` where the span was started.
305306
/// - line: The file line where the span was started.
306307
/// - operation: The operation that this span should be measuring
307308
/// - Returns: the value returned by `operation`
308309
/// - Throws: the error the `operation` has thrown (if any)
310+
#if swift(>=6.0)
311+
public func withAnySpan<T, Instant: TracerInstant>(
312+
_ operationName: String,
313+
at instant: @autoclosure () -> Instant,
314+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
315+
ofKind kind: SpanKind = .internal,
316+
isolation: isolated(any Actor)? = #isolation,
317+
function: String = #function,
318+
file fileID: String = #fileID,
319+
line: UInt = #line,
320+
_ operation: (any Tracing.Span) async throws -> T
321+
) async rethrows -> T {
322+
let span = self.startAnySpan(
323+
operationName,
324+
at: instant(),
325+
context: context(),
326+
ofKind: kind,
327+
function: function,
328+
file: fileID,
329+
line: line
330+
)
331+
defer { span.end() }
332+
do {
333+
return try await ServiceContext.$current.withValue(span.context) {
334+
try await operation(span)
335+
}
336+
} catch {
337+
span.recordError(error)
338+
throw error // rethrow
339+
}
340+
}
341+
#endif
342+
343+
@_disfavoredOverload
344+
@available(*, deprecated, message: "Prefer #isolation version of this API")
309345
public func withAnySpan<T, Instant: TracerInstant>(
310346
_ operationName: String,
311347
at instant: @autoclosure () -> Instant,
@@ -354,12 +390,47 @@ extension LegacyTracer {
354390
/// - operationName: The name of the operation being traced. This may be a handler function, database call, ...
355391
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
356392
/// - kind: The ``SpanKind`` of the new ``Span``.
393+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
357394
/// - function: The function name in which the span was started
358395
/// - fileID: The `fileID` where the span was started.
359396
/// - line: The file line where the span was started.
360397
/// - operation: The operation that this span should be measuring
361398
/// - Returns: the value returned by `operation`
362399
/// - Throws: the error the `operation` has thrown (if any)
400+
#if swift(>=6.0)
401+
public func withAnySpan<T>(
402+
_ operationName: String,
403+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
404+
ofKind kind: SpanKind = .internal,
405+
isolation: (any Actor)? = #isolation,
406+
function: String = #function,
407+
file fileID: String = #fileID,
408+
line: UInt = #line,
409+
_ operation: (any Tracing.Span) async throws -> T
410+
) async rethrows -> T {
411+
let span = self.startAnySpan(
412+
operationName,
413+
at: DefaultTracerClock.now,
414+
context: context(),
415+
ofKind: kind,
416+
function: function,
417+
file: fileID,
418+
line: line
419+
)
420+
defer { span.end() }
421+
do {
422+
return try await ServiceContext.$current.withValue(span.context) {
423+
try await operation(span)
424+
}
425+
} catch {
426+
span.recordError(error)
427+
throw error // rethrow
428+
}
429+
}
430+
#endif
431+
432+
@_disfavoredOverload
433+
@available(*, deprecated, message: "Prefer #isolation version of this API")
363434
public func withAnySpan<T>(
364435
_ operationName: String,
365436
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
@@ -524,12 +595,41 @@ extension Tracer {
524595
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
525596
/// - kind: The ``SpanKind`` of the new ``Span``.
526597
/// - instant: the time instant at which the span started
598+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
527599
/// - function: The function name in which the span was started
528600
/// - fileID: The `fileID` where the span was started.
529601
/// - line: The file line where the span was started.
530602
/// - operation: The operation that this span should be measuring
531603
/// - Returns: the value returned by `operation`
532604
/// - Throws: the error the `operation` has thrown (if any)
605+
#if swift(>=6.0)
606+
public func withAnySpan<T>(
607+
_ operationName: String,
608+
at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now,
609+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
610+
ofKind kind: SpanKind = .internal,
611+
isolation: (any Actor)? = #isolation,
612+
function: String = #function,
613+
file fileID: String = #fileID,
614+
line: UInt = #line,
615+
@_inheritActorContext @_implicitSelfCapture _ operation: (any Tracing.Span) async throws -> T
616+
) async rethrows -> T {
617+
try await self.withSpan(
618+
operationName,
619+
context: context(),
620+
ofKind: kind,
621+
at: instant(),
622+
function: function,
623+
file: fileID,
624+
line: line
625+
) { span in
626+
try await operation(span)
627+
}
628+
}
629+
#endif
630+
631+
@_disfavoredOverload
632+
@available(*, deprecated, message: "Prefer #isolation version of this API")
533633
public func withAnySpan<T>(
534634
_ operationName: String,
535635
at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now,

Sources/Tracing/TracerProtocol.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,47 @@ extension Tracer {
235235
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
236236
/// - kind: The ``SpanKind`` of the new ``Span``.
237237
/// - instant: the time instant at which the span started
238+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
238239
/// - function: The function name in which the span was started
239240
/// - fileID: The `fileID` where the span was started.
240241
/// - line: The file line where the span was started.
241242
/// - operation: The operation that this span should be measuring
242243
/// - Returns: the value returned by `operation`
243244
/// - Throws: the error the `operation` has thrown (if any)
245+
#if swift(>=6.0)
246+
public func withSpan<T>(
247+
_ operationName: String,
248+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
249+
ofKind kind: SpanKind = .internal,
250+
isolation: (any Actor)? = #isolation,
251+
function: String = #function,
252+
file fileID: String = #fileID,
253+
line: UInt = #line,
254+
@_inheritActorContext @_implicitSelfCapture _ operation: (Self.Span) async throws -> T
255+
) async rethrows -> T {
256+
let span = self.startSpan(
257+
operationName,
258+
context: context(),
259+
ofKind: kind,
260+
at: DefaultTracerClock.now,
261+
function: function,
262+
file: fileID,
263+
line: line
264+
)
265+
defer { span.end() }
266+
do {
267+
return try await ServiceContext.$current.withValue(span.context) {
268+
try await operation(span)
269+
}
270+
} catch {
271+
span.recordError(error)
272+
throw error // rethrow
273+
}
274+
}
275+
#endif
276+
277+
@_disfavoredOverload
278+
@available(*, deprecated, message: "Prefer #isolation version of this API")
244279
public func withSpan<T>(
245280
_ operationName: String,
246281
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
@@ -287,12 +322,48 @@ extension Tracer {
287322
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
288323
/// - kind: The ``SpanKind`` of the new ``Span``.
289324
/// - instant: the time instant at which the span started
325+
/// - isolation: Defaulted parameter for inheriting isolation of calling actor
290326
/// - function: The function name in which the span was started
291327
/// - fileID: The `fileID` where the span was started.
292328
/// - line: The file line where the span was started.
293329
/// - operation: The operation that this span should be measuring
294330
/// - Returns: the value returned by `operation`
295331
/// - Throws: the error the `operation` has thrown (if any)
332+
#if swift(>=6.0)
333+
public func withSpan<T>(
334+
_ operationName: String,
335+
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,
336+
ofKind kind: SpanKind = .internal,
337+
at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now,
338+
isolation: (any Actor)? = #isolation,
339+
function: String = #function,
340+
file fileID: String = #fileID,
341+
line: UInt = #line,
342+
@_inheritActorContext @_implicitSelfCapture _ operation: (Self.Span) async throws -> T
343+
) async rethrows -> T {
344+
let span = self.startSpan(
345+
operationName,
346+
context: context(),
347+
ofKind: kind,
348+
at: instant(),
349+
function: function,
350+
file: fileID,
351+
line: line
352+
)
353+
defer { span.end() }
354+
do {
355+
return try await ServiceContext.$current.withValue(span.context) {
356+
try await operation(span)
357+
}
358+
} catch {
359+
span.recordError(error)
360+
throw error // rethrow
361+
}
362+
}
363+
#endif
364+
365+
@_disfavoredOverload
366+
@available(*, deprecated, message: "Prefer #isolation version of this API")
296367
public func withSpan<T>(
297368
_ operationName: String,
298369
context: @autoclosure () -> ServiceContext = .current ?? .topLevel,

Tests/TracingTests/ActorTracingTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ final class ActorTracingTests: XCTestCase {
2222
super.tearDown()
2323
InstrumentationSystem.bootstrapInternal(nil)
2424
}
25+
26+
func test() {}
2527
}
2628

2729
func work() async {}

0 commit comments

Comments
 (0)