调度计时器源中的数据竞争

时间:2017-01-26 16:52:32

标签: ios swift macos grand-central-dispatch thread-sanitizer

ThreadSanitizer检测到macOS上运行的以下Swift程序中的数据争用:

import Dispatch


class Foo<T> {
    var value: T?
    let queue = DispatchQueue(label: "Foo syncQueue")
    init(){}
    func complete(value: T) {
        queue.sync {
            self.value = value
        }
    }

    static func completeAfter(_ delay: Double, value: T) -> Foo<T> {
        let returnedFoo = Foo<T>()
        let queue = DispatchQueue(label: "timerEventHandler")
        let timer = DispatchSource.makeTimerSource(queue: queue)
        timer.setEventHandler {
            returnedFoo.complete(value: value)
            timer.cancel()
        }
        timer.scheduleOneshot(deadline: .now() + delay)
        timer.resume()
        return returnedFoo
    }
}



func testCompleteAfter() {

    let foo = Foo<Int>.completeAfter(0.1, value: 1)
    sleep(10)
}

testCompleteAfter()

在iOS模拟器上运行时,ThreadSanitizer不会检测到竞争。

ThreadSanitizer输出:

WARNING: ThreadSanitizer: data race (pid=71596)
  Read of size 8 at 0x7d0c0000eb48 by thread T2:
    #0 block_destroy_helper.5 main.swift (DispatchTimerSourceDataRace+0x0001000040fb)
    #1 _Block_release <null>:38 (libsystem_blocks.dylib+0x000000000951)

  Previous write of size 8 at 0x7d0c0000eb48 by main thread:
    #0 block_copy_helper.4 main.swift (DispatchTimerSourceDataRace+0x0001000040b0)
    #1 _Block_copy <null>:38 (libsystem_blocks.dylib+0x0000000008b2)
    #2 testCompleteAfter() -> () main.swift:40 (DispatchTimerSourceDataRace+0x000100003981)
    #3 main main.swift:44 (DispatchTimerSourceDataRace+0x000100002250)

  Location is heap block of size 48 at 0x7d0c0000eb20 allocated by main thread:
    #0 malloc <null>:144 (libclang_rt.tsan_osx_dynamic.dylib+0x00000004188a)
    #1 _Block_copy <null>:38 (libsystem_blocks.dylib+0x000000000873)
    #2 testCompleteAfter() -> () main.swift:40 (DispatchTimerSourceDataRace+0x000100003981)
    #3 main main.swift:44 (DispatchTimerSourceDataRace+0x000100002250)

  Thread T2 (tid=3107318, running) created by thread T-1
    [failed to restore the stack]

SUMMARY: ThreadSanitizer: data race main.swift in block_destroy_helper.5

代码有什么可疑之处吗?

1 个答案:

答案 0 :(得分:0)

来自@Rob的评论让我再次思考这个问题。我想出了静态函数2017-01-27 11:38:16.678 INFO ???? --- [cluster-ClusterId{value='588b777760866a17135c54b5', description='null'}-ndc-dv-mongo1.local:27017] o.m.d.cluster : Exception in monitor thread while connecting to server ndc-dv-mongo1.local:27017 com.mongodb.MongoSecurityException: Exception authenticating MongoCredential{mechanism=null, userName='UserTest', source='dbtest_batch', password=<hidden>, mechanismProperties={}} at com.mongodb.connection.SaslAuthenticator.wrapInMongoSecurityException(SaslAuthenticator.java:157) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator.access$200(SaslAuthenticator.java:37) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator$1.run(SaslAuthenticator.java:66) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator$1.run(SaslAuthenticator.java:44) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator.doAsSubject(SaslAuthenticator.java:162) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator.authenticate(SaslAuthenticator.java:44) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.DefaultAuthenticator.authenticate(DefaultAuthenticator.java:32) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.InternalStreamConnectionInitializer.authenticateAll(InternalStreamConnectionInitializer.java:109) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.InternalStreamConnectionInitializer.initialize(InternalStreamConnectionInitializer.java:46) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.InternalStreamConnection.open(InternalStreamConnection.java:116) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:113) [mongodb-driver-core-3.4.1.jar:?] at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101] Caused by: com.mongodb.MongoCommandException: Command failed with error 18: 'Authentication failed.' on server ndc-dv-mongo1.local:27017. The full response is { "ok" : 0.0, "errmsg" : "Authentication failed.", "code" : 18, "codeName" : "AuthenticationFailed" } at com.mongodb.connection.CommandHelper.createCommandFailureException(CommandHelper.java:170) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.CommandHelper.receiveCommandResult(CommandHelper.java:123) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.CommandHelper.executeCommand(CommandHelper.java:32) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator.sendSaslStart(SaslAuthenticator.java:117) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator.access$000(SaslAuthenticator.java:37) ~[mongodb-driver-core-3.4.1.jar:?] at com.mongodb.connection.SaslAuthenticator$1.run(SaslAuthenticator.java:50) ~[mongodb-driver-core-3.4.1.jar:?] ... 9 more 的以下修改 - ThreadSanitizer对*)感到满意:

completeAfter

此更改确保对static func completeAfter(_ delay: Double, value: T) -> Foo<T> { let returnedFoo = Foo<T>() let queue = DispatchQueue(label: "timerEventHandler") queue.async { let timer = DispatchSource.makeTimerSource(queue: queue) timer.setEventHandler { returnedFoo.complete(value: value) timer.cancel() } timer.scheduleOneshot(deadline: .now() + delay) timer.resume() } return returnedFoo } 的所有访问都将在队列timer中执行,队列queue尝试以此方式同步计时器。尽管如此,我的“真实”代码中的相同解决方案无法使用此解决方案,这可能是由于其他外部因素造成的。

*)我们永远不应该想,我们的代码没有比赛,因为ThreadSanitizer没有检测到。可能存在恰好“擦除”潜在数据竞争的外部因素(例如,调度lib恰好在同一线程上执行两个具有冲突访问权限的块 - 并且不会发生数据争用)