Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-7712] Sync dispatch queue block crash #50252

Closed
iby opened this issue May 17, 2018 · 4 comments
Closed

[SR-7712] Sync dispatch queue block crash #50252

iby opened this issue May 17, 2018 · 4 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.

Comments

@iby
Copy link

iby commented May 17, 2018

Previous ID SR-7712
Radar None
Original Reporter @iby
Type Bug
Status Closed
Resolution Duplicate

Attachment: Download

Additional Detail from JIRA
Votes 0
Component/s
Labels Bug
Assignee None
Priority Medium

md5: b7d38fbfc062d88b004dbf06d1f89d7c

duplicates:

  • SR-1042 Make "lazy var" threadsafe

Issue Description:

While working on a stateful semaphore class I came across a floating crash. I can't really tell what goes wrong, but it seems when sync block is invoked on a queue from another concurrent queue it sometimes can't handle it. This works in most cases, pretty much always in production. But during heavy testing it's easy to crash.

@belkadan
Copy link
Contributor

Running your test program with Thread Sanitizer enabled very quickly turns up several data races:

==================
WARNING: ThreadSanitizer: data race (pid=76133)
  Read of size 8 at 0x7b0c00000110 by thread T1:
    #&#8203;0 main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue <null>:1062848 (main:x86_64+0x100002a15)
    #&#8203;1 main.StatefulSemaphore.isWaiting.getter : Swift.Bool <null>:1062848 (main:x86_64+0x100003067)
    #&#8203;2 closure #&#8203;1 in  <null>:1062848 (main:x86_64+0x100004bec)
    #&#8203;3 thunk for @callee_guaranteed (@unowned Int) -> () <null>:1062848 (libswiftDispatch.dylib:x86_64+0xb346)
    #&#8203;4 _dispatch_client_callout2 <null>:1062848 (libdispatch.dylib:x86_64+0xc1d0)

  Previous write of size 8 at 0x7b0c00000110 by main thread:
    #&#8203;0 main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue <null>:1062848 (main:x86_64+0x100002a33)
    #&#8203;1 main.StatefulSemaphore.isWaiting.getter : Swift.Bool <null>:1062848 (main:x86_64+0x100003067)
    #&#8203;2 closure #&#8203;1 in  <null>:1062848 (main:x86_64+0x100004bec)
    #&#8203;3 thunk for @callee_guaranteed (@unowned Int) -> () <null>:1062848 (libswiftDispatch.dylib:x86_64+0xb346)
    #&#8203;4 _dispatch_client_callout2 <null>:1062848 (libdispatch.dylib:x86_64+0xc1d0)
    #&#8203;5 static DispatchQueue.concurrentPerform(iterations:execute:) <null>:1062848 (libswiftDispatch.dylib:x86_64+0xb2e8)
    #&#8203;6 start <null>:1062848 (libdyld.dylib:x86_64+0x1014)

  Location is heap block of size 40 at 0x7b0c000000f0 allocated by main thread:
    #&#8203;0 malloc <null>:1062880 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x4998a)
    #&#8203;1 swift_slowAlloc <null>:1062880 (libswiftCore.dylib:x86_64+0x2d9928)
    #&#8203;2 StatefulSemaphore.__allocating_init() <null>:1062880 (main:x86_64+0x100001d5d)
    #&#8203;3 main <null>:1062880 (main:x86_64+0x100001c39)

  Thread T1 (tid=4553039, running) is a GCD worker thread

SUMMARY: ThreadSanitizer: data race (main:x86_64+0x100002a15) in main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue
==================
==================
WARNING: ThreadSanitizer: data race (pid=76133)
  Read of size 8 at 0x7b0c00000110 by thread T3:
    #&#8203;0 main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue <null>:2136000 (main:x86_64+0x100002a15)
    #&#8203;1 main.StatefulSemaphore.isWaiting.getter : Swift.Bool <null>:2136000 (main:x86_64+0x100003067)
    #&#8203;2 closure #&#8203;1 in  <null>:2136000 (main:x86_64+0x100004bec)
    #&#8203;3 thunk for @callee_guaranteed (@unowned Int) -> () <null>:2136000 (libswiftDispatch.dylib:x86_64+0xb346)
    #&#8203;4 _dispatch_client_callout2 <null>:2136000 (libdispatch.dylib:x86_64+0xc1d0)

  Previous write of size 8 at 0x7b0c00000110 by main thread:
    #&#8203;0 main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue <null>:2136000 (main:x86_64+0x100002a33)
    #&#8203;1 main.StatefulSemaphore.isWaiting.getter : Swift.Bool <null>:2136000 (main:x86_64+0x100003067)
    #&#8203;2 closure #&#8203;1 in  <null>:2136000 (main:x86_64+0x100004bec)
    #&#8203;3 thunk for @callee_guaranteed (@unowned Int) -> () <null>:2136000 (libswiftDispatch.dylib:x86_64+0xb346)
    #&#8203;4 _dispatch_client_callout2 <null>:2136000 (libdispatch.dylib:x86_64+0xc1d0)
    #&#8203;5 static DispatchQueue.concurrentPerform(iterations:execute:) <null>:2136000 (libswiftDispatch.dylib:x86_64+0xb2e8)
    #&#8203;6 start <null>:2136000 (libdyld.dylib:x86_64+0x1014)

  Location is heap block of size 40 at 0x7b0c000000f0 allocated by main thread:
    #&#8203;0 malloc <null>:2136032 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x4998a)
    #&#8203;1 swift_slowAlloc <null>:2136032 (libswiftCore.dylib:x86_64+0x2d9928)
    #&#8203;2 StatefulSemaphore.__allocating_init() <null>:2136032 (main:x86_64+0x100001d5d)
    #&#8203;3 main <null>:2136032 (main:x86_64+0x100001c39)

  Thread T3 (tid=4553041, running) is a GCD worker thread

SUMMARY: ThreadSanitizer: data race (main:x86_64+0x100002a15) in main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue
==================
==================
WARNING: ThreadSanitizer: data race (pid=76133)
  Read of size 8 at 0x7b0c00000100 by thread T6:
    #&#8203;0 closure #&#8203;1 in StatefulSemaphore.continue(_:) <null>:3208224 (main:x86_64+0x1000042e0)
    #&#8203;1 partial apply for closure #&#8203;1 in StatefulSemaphore.continue(_:) <null>:3208224 (main:x86_64+0x10000561b)
    #&#8203;2 reabstraction thunk helper from @escaping @callee_guaranteed () -> () to @escaping @callee_unowned @convention(block) () -> () <null>:3208224 (main:x86_64+0x1000044d0)
    #&#8203;3 __tsan::invoke_and_release_block(void*) <null>:3208224 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x650ab)
    #&#8203;4 _dispatch_client_callout <null>:3208224 (libdispatch.dylib:x86_64+0x1db7)

  Previous write of size 8 at 0x7b0c00000100 by thread T1:
    #&#8203;0 closure #&#8203;2 in StatefulSemaphore.wait(timeout:) <null>:3208224 (main:x86_64+0x100003f76)
    #&#8203;1 partial apply for closure #&#8203;2 in StatefulSemaphore.wait(timeout:) <null>:3208224 (main:x86_64+0x1000054db)
    #&#8203;2 thunk for @callee_guaranteed () -> () <null>:3208224 (main:x86_64+0x100004090)
    #&#8203;3 __tsan::invoke_and_release_block(void*) <null>:3208224 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x650ab)
    #&#8203;4 _dispatch_client_callout <null>:3208224 (libdispatch.dylib:x86_64+0x1db7)
    #&#8203;5 StatefulSemaphore.wait(timeout:) <null>:3208224 (main:x86_64+0x100003872)
    #&#8203;6 closure #&#8203;1 in  <null>:3208224 (main:x86_64+0x100004cbe)
    #&#8203;7 thunk for @callee_guaranteed (@unowned Int) -> () <null>:3208224 (libswiftDispatch.dylib:x86_64+0xb346)
    #&#8203;8 _dispatch_client_callout2 <null>:3208224 (libdispatch.dylib:x86_64+0xc1d0)

  Location is heap block of size 40 at 0x7b0c000000f0 allocated by main thread:
    #&#8203;0 malloc <null>:3208256 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x4998a)
    #&#8203;1 swift_slowAlloc <null>:3208256 (libswiftCore.dylib:x86_64+0x2d9928)
    #&#8203;2 StatefulSemaphore.__allocating_init() <null>:3208256 (main:x86_64+0x100001d5d)
    #&#8203;3 main <null>:3208256 (main:x86_64+0x100001c39)

  Thread T6 (tid=4553063, running) is a GCD worker thread

  Thread T1 (tid=4553039, running) is a GCD worker thread

SUMMARY: ThreadSanitizer: data race (main:x86_64+0x1000042e0) in closure #&#8203;1 in StatefulSemaphore.continue(_:)
==================
==================
WARNING: ThreadSanitizer: data race (pid=76133)
  Read of size 8 at 0x7b0c00000110 by thread T2:
    #&#8203;0 main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue <null>:1599424 (main:x86_64+0x100002a15)
    #&#8203;1 main.StatefulSemaphore.isWaiting.getter : Swift.Bool <null>:1599424 (main:x86_64+0x100003067)
    #&#8203;2 closure #&#8203;1 in  <null>:1599424 (main:x86_64+0x100004bec)
    #&#8203;3 thunk for @callee_guaranteed (@unowned Int) -> () <null>:1599424 (libswiftDispatch.dylib:x86_64+0xb346)
    #&#8203;4 _dispatch_client_callout2 <null>:1599424 (libdispatch.dylib:x86_64+0xc1d0)

  Previous write of size 8 at 0x7b0c00000110 by main thread:
    #&#8203;0 main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue <null>:1599424 (main:x86_64+0x100002a33)
    #&#8203;1 main.StatefulSemaphore.isWaiting.getter : Swift.Bool <null>:1599424 (main:x86_64+0x100003067)
    #&#8203;2 closure #&#8203;1 in  <null>:1599424 (main:x86_64+0x100004bec)
    #&#8203;3 thunk for @callee_guaranteed (@unowned Int) -> () <null>:1599424 (libswiftDispatch.dylib:x86_64+0xb346)
    #&#8203;4 _dispatch_client_callout2 <null>:1599424 (libdispatch.dylib:x86_64+0xc1d0)
    #&#8203;5 static DispatchQueue.concurrentPerform(iterations:execute:) <null>:1599424 (libswiftDispatch.dylib:x86_64+0xb2e8)
    #&#8203;6 start <null>:1599424 (libdyld.dylib:x86_64+0x1014)

  Location is heap block of size 40 at 0x7b0c000000f0 allocated by main thread:
    #&#8203;0 malloc <null>:1599456 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x4998a)
    #&#8203;1 swift_slowAlloc <null>:1599456 (libswiftCore.dylib:x86_64+0x2d9928)
    #&#8203;2 StatefulSemaphore.__allocating_init() <null>:1599456 (main:x86_64+0x100001d5d)
    #&#8203;3 main <null>:1599456 (main:x86_64+0x100001c39)

  Thread T2 (tid=4553040, running) is a GCD worker thread

SUMMARY: ThreadSanitizer: data race (main:x86_64+0x100002a15) in main.StatefulSemaphore.(queue in _1A61A3E7348A44FC8FF8726B26BEB4ED).getter : __ObjC.DispatchQueue
==================
ThreadSanitizer: reported 4 warnings

If your program still fails after running TSan-clean, please reopen this bug.

@iby
Copy link
Author

iby commented May 17, 2018

@belkadan Shouldn't this be handled by the queue? All reads and writes are synchronized on that queue to be specifically thread safe, this is what confused me in the first place.

One particular thing that stands out is that the sanitizer reports return self.queue.sync { line, not return self.primitive = nil line as I'd expect. As far as I know queue itself is thread safe, so what this might be about?

I've gone though several options, including adding .barrier flag, which shouldn't (and doesn't seem) to have an effect on a serial queue. I'm a little lost on this. Is there something very obvious missing or am I doing something silly?

@belkadan
Copy link
Contributor

I didn't really read your code, just ran with TSan. Now that I am reading it, I think the problem is that the queue variable itself is not synchronized, which is SR-1042.

@iby
Copy link
Author

iby commented May 17, 2018

No way!!![]( Yes, that totally seems to be the reason. Thanks for the insight)

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.
Projects
None yet
Development

No branches or pull requests

2 participants