-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Open
Labels
TSanFor issues in the Thread Sanitizer itselfFor issues in the Thread Sanitizer itselfactorFeature → concurrency: `actor` declarationsFeature → concurrency: `actor` declarationsasync & awaitFeature → concurrency: asynchronous function aka the async/await patternFeature → concurrency: asynchronous function aka the async/await patternbugA deviation from expected or documented behavior. Also: expected but undesirable behavior.A deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfThe Swift compiler itselfconcurrencyFeature: umbrella label for concurrency language featuresFeature: umbrella label for concurrency language featuresfound by tsanFlag: An issue found by the Thread SanitizerFlag: An issue found by the Thread Sanitizerunexpected behaviorBug: Unexpected behavior or incorrect outputBug: Unexpected behavior or incorrect output
Description
Previous ID | SR-15599 |
Radar | None |
Original Reporter | Cameron (JIRA User) |
Type | Bug |
Environment
macOS Version 12.1 (21C52)
Xcode Version 13.2 (13C90)
Additional Detail from JIRA
Votes | 1 |
Component/s | swift |
Labels | Bug |
Assignee | None |
Priority | Medium |
md5: e537527687d15564192ad661b44dddad
Issue Description:
I am seeing what I believe are unexpected thread sanitizer issues with the following actor code.
Here's the actor:
public actor Example<Input: Hashable, Output> {
private var inProgress = [Input: Task<Output, Error>]()
private let transform: (Input) async throws -> Output
public init(_ transform: @escaping (Input) async throws -> Output) {
self.transform = transform
}
public func transform(_ x: Input) async throws -> Output {
if let task = inProgress[x] {
return try await task.value
} else {
let task = Task {
try await transform(x) // the races are reported on this line
}
inProgress[x] = task
defer {
inProgress[x] = nil
}
return try await task.value
}
}
}
Here's the test code that demonstrates the issues:
import XCTest
import MyLibrary
final class MyLibraryTests: XCTestCase {
func testExample() async throws {
let example = Example { (x: Int) -> String in
String(x)
}
let value = try await example.transform(1)
XCTAssertEqual(value, "1")
}
}
Here's my testing output:
Test Suite 'All tests' started at 2021-12-15 10:26:19.904
Test Suite 'MyLibraryTests.xctest' started at 2021-12-15 10:26:19.907
Test Suite 'MyLibraryTests' started at 2021-12-15 10:26:19.908
Test Case '-[MyLibraryTests.MyLibraryTests testExample]' started.
==================
WARNING: ThreadSanitizer: data race (pid=83691)
Read of size 8 at 0x7b6000020458 by thread T3:
#​0 (1) suspend resume partial function for closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0x9f1e)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Previous write of size 8 at 0x7b6000020458 by thread T1:
#​0 closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0x9dc3)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Location is heap block of size 1016 at 0x7b6000020400 allocated by thread T1:
#​0 malloc <null>:3 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x51d9a)
#​1 swift::StackAllocator<1000ul>::getSlabForAllocation(unsigned long) <null>:2 (libswift_Concurrency.dylib:x86_64+0x32769)
#​2 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Thread T3 (tid=1111954, running) is a GCD worker thread
Thread T1 (tid=1111953, running) is a GCD worker thread
SUMMARY: ThreadSanitizer: data race (MyLibraryTests:x86_64+0x9f1e) in (1) suspend resume partial function for closure #​1 in Example.transform(_:)+0x4e
==================
ThreadSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.
==================
WARNING: ThreadSanitizer: data race (pid=83691)
Write of size 8 at 0x7b6000020438 by thread T3:
#​0 (1) suspend resume partial function for closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0x9f3c)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Previous write of size 8 at 0x7b6000020438 by thread T1:
#​0 closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0x9e14)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Location is heap block of size 1016 at 0x7b6000020400 allocated by thread T1:
#​0 malloc <null>:3 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x51d9a)
#​1 swift::StackAllocator<1000ul>::getSlabForAllocation(unsigned long) <null>:2 (libswift_Concurrency.dylib:x86_64+0x32769)
#​2 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Thread T3 (tid=1111954, running) is a GCD worker thread
Thread T1 (tid=1111953, running) is a GCD worker thread
SUMMARY: ThreadSanitizer: data race (MyLibraryTests:x86_64+0x9f3c) in (1) suspend resume partial function for closure #​1 in Example.transform(_:)+0x6c
==================
ThreadSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.
==================
WARNING: ThreadSanitizer: data race (pid=83691)
Read of size 8 at 0x7b6000020460 by thread T3:
#​0 (1) suspend resume partial function for closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0xa03b)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Previous write of size 8 at 0x7b6000020460 by thread T1:
#​0 closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0x9dae)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Location is heap block of size 1016 at 0x7b6000020400 allocated by thread T1:
#​0 malloc <null>:3 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x51d9a)
#​1 swift::StackAllocator<1000ul>::getSlabForAllocation(unsigned long) <null>:2 (libswift_Concurrency.dylib:x86_64+0x32769)
#​2 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Thread T3 (tid=1111954, running) is a GCD worker thread
Thread T1 (tid=1111953, running) is a GCD worker thread
SUMMARY: ThreadSanitizer: data race (MyLibraryTests:x86_64+0xa03b) in (1) suspend resume partial function for closure #​1 in Example.transform(_:)+0x16b
==================
ThreadSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.
==================
WARNING: ThreadSanitizer: data race (pid=83691)
Read of size 8 at 0x7b6000020450 by thread T3:
#​0 (1) suspend resume partial function for closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0xa050)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Previous write of size 8 at 0x7b6000020450 by thread T1:
#​0 closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0x9dd8)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Location is heap block of size 1016 at 0x7b6000020400 allocated by thread T1:
#​0 malloc <null>:3 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x51d9a)
#​1 swift::StackAllocator<1000ul>::getSlabForAllocation(unsigned long) <null>:2 (libswift_Concurrency.dylib:x86_64+0x32769)
#​2 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Thread T3 (tid=1111954, running) is a GCD worker thread
Thread T1 (tid=1111953, running) is a GCD worker thread
SUMMARY: ThreadSanitizer: data race (MyLibraryTests:x86_64+0xa050) in (1) suspend resume partial function for closure #​1 in Example.transform(_:)+0x180
==================
ThreadSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.
==================
WARNING: ThreadSanitizer: data race (pid=83691)
Read of size 8 at 0x7b6000020468 by thread T3:
#​0 (2) await resume partial function for closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0xa180)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Previous write of size 8 at 0x7b6000020468 by thread T1:
#​0 closure #​1 in Example.transform(_:) <null>:2 (MyLibraryTests:x86_64+0x9e73)
#​1 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Location is heap block of size 1016 at 0x7b6000020400 allocated by thread T1:
#​0 malloc <null>:3 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x51d9a)
#​1 swift::StackAllocator<1000ul>::getSlabForAllocation(unsigned long) <null>:2 (libswift_Concurrency.dylib:x86_64+0x32769)
#​2 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x2d913)
Thread T3 (tid=1111954, running) is a GCD worker thread
Thread T1 (tid=1111953, running) is a GCD worker thread
SUMMARY: ThreadSanitizer: data race (MyLibraryTests:x86_64+0xa180) in (2) await resume partial function for closure #​1 in Example.transform(_:)+0xe0
==================
ThreadSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.
Test Case '-[MyLibraryTests.MyLibraryTests testExample]' passed (4.684 seconds).
Test Suite 'MyLibraryTests' passed at 2021-12-15 10:26:24.594.
Executed 1 test, with 0 failures (0 unexpected) in 4.684 (4.686) seconds
Test Suite 'MyLibraryTests.xctest' passed at 2021-12-15 10:26:24.595.
Executed 1 test, with 0 failures (0 unexpected) in 4.684 (4.688) seconds
Test Suite 'All tests' passed at 2021-12-15 10:26:24.596.
Executed 1 test, with 0 failures (0 unexpected) in 4.684 (4.692) seconds
ThreadSanitizer: reported 5 warnings
Program ended with exit code: 66
Metadata
Metadata
Assignees
Labels
TSanFor issues in the Thread Sanitizer itselfFor issues in the Thread Sanitizer itselfactorFeature → concurrency: `actor` declarationsFeature → concurrency: `actor` declarationsasync & awaitFeature → concurrency: asynchronous function aka the async/await patternFeature → concurrency: asynchronous function aka the async/await patternbugA deviation from expected or documented behavior. Also: expected but undesirable behavior.A deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itselfThe Swift compiler itselfconcurrencyFeature: umbrella label for concurrency language featuresFeature: umbrella label for concurrency language featuresfound by tsanFlag: An issue found by the Thread SanitizerFlag: An issue found by the Thread Sanitizerunexpected behaviorBug: Unexpected behavior or incorrect outputBug: Unexpected behavior or incorrect output