Understanding Swift’s value type thread safety - 代码分析(二)
示范代码
`
func testScenarioA2() throws {
    var store: Int = 100
    DispatchQueue.concurrentPerform(iterations: 1_000_000) { _ in
        store.negate()
        _ = store
    }
}
`
开启线程race诊断后,出现以下错误
Swift access race in closure #1 (Swift.Int) -> () in UnderstandStruct.testScenarioA2() throws -> ()
查看汇编
`
0x100002a30 <+0>:   pushq  %rbp
0x100002a31 <+1>:   movq   %rsp, %rbp
0x100002a34 <+4>:   subq   $0x20, %rsp
0x100002a38 <+8>:   movq   0x8(%rbp), %rdi
0x100002a3c <+12>:  movq   %rsi, -0x10(%rbp)
0x100002a40 <+16>:  callq  0x100003bac               ; symbol stub for: __tsan_func_entry
0x100002a45 <+21>:  xorl   %esi, %esi
0x100002a47 <+23>:  leaq   -0x8(%rbp), %rax
0x100002a4b <+27>:  movq   %rax, %rdi
0x100002a4e <+30>:  movl   $0x8, %edx
0x100002a53 <+35>:  callq  0x100003c06               ; symbol stub for: memset
0x100002a58 <+40>:  xorl   %ecx, %ecx
0x100002a5a <+42>:  movl   %ecx, %esi
0x100002a5c <+44>:  movq   -0x10(%rbp), %rdx
0x100002a60 <+48>:  movq   %rdx, -0x8(%rbp)
->  0x100002a64 <+52>:  movq   %rdx, %rdi
0x100002a67 <+55>:  movl   $0x1, %edx
0x100002a6c <+60>:  movq   %rax, -0x18(%rbp)
0x100002a70 <+64>:  callq  0x100003ba6               ; symbol stub for: __tsan_external_write
0x100002a75 <+69>:  movq   -0x10(%rbp), %rax
0x100002a79 <+73>:  movq   %rax, %rdi
0x100002a7c <+76>:  callq  0x100003bbe               ; symbol stub for: __tsan_read8
0x100002a81 <+81>:  xorl   %ecx, %ecx
0x100002a83 <+83>:  movl   %ecx, %eax
0x100002a85 <+85>:  movq   -0x10(%rbp), %rdx
0x100002a89 <+89>:  subq   (%rdx), %rax
0x100002a8c <+92>:  seto   %r8b
0x100002a90 <+96>:  testb  $0x1, %r8b
0x100002a94 <+100>: movq   %rax, -0x20(%rbp)
0x100002a98 <+104>: jne    0x100002ac1               ; <+145> [inlined] Swift runtime failure: arithmetic overflow at main.swift:13
0x100002a9a <+106>: movq   -0x10(%rbp), %rdi
0x100002a9e <+110>: callq  0x100003bc4               ; symbol stub for: __tsan_write8
0x100002aa3 <+115>: movq   -0x10(%rbp), %rax
0x100002aa7 <+119>: movq   -0x20(%rbp), %rcx
0x100002aab <+123>: movq   %rcx, (%rax)
0x100002aae <+126>: movq   %rax, %rdi
0x100002ab1 <+129>: callq  0x100003bbe               ; symbol stub for: __tsan_read8
0x100002ab6 <+134>: callq  0x100003bb2               ; symbol stub for: __tsan_func_exit
0x100002abb <+139>: addq   $0x20, %rsp
0x100002abf <+143>: popq   %rbp
0x100002ac0 <+144>: retq
0x100002ac1 <+145>: ud2
0x100002ac3 <+147>: nopw   %cs:(%rax,%rax)
0x100002acd <+157>: nopl   (%rax)
`
0x100002aab <+123>: movq   %rcx, (%rax)
rax是store的地址,直接将计算结果赋给指向的地址。
结论
- 对于堆中的变量 store,系统会自动生成 begin_access 和 end_access插桩的检测竞争代码,该代码不影响实际逻辑的执行
- 对于store的值的改变,不同线程中都是一条mov指令,所以这里存在的线程竞争不会导致crash
参考
`
bb0(%0 : $Int, %1 : $Int):
debug_value %0 : $Int                           // id: %2
debug_value_addr %1 : $Int, var, name "store", argno 2 // id: %3
%4 = begin_access [modify] [unknown] %1 : $Int // users: %7, %6
// function_ref SignedNumeric.negate()
%5 = function_ref @$ss13SignedNumericPsE6negateyyF : $@convention(method) <τ_0_0 where τ_0_0 : SignedNumeric> (@inout τ_0_0) -> () // user: %6
%6 = apply %5
end_access %4 : $
%8 = begin_access [read] [unknown] %1 : $Int   // users: %10, %9
%9 = load [trivial] %8 : $Int
end_access %8 : $*Int                           // id: %10
%11 = tuple ()                                  // user: %12
return %11 : $()                                // id: %12
}
`
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号