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

Crash in destroy for AsyncChain2Sequence? #69367

Open
mansbernhardt opened this issue Oct 23, 2023 · 6 comments
Open

Crash in destroy for AsyncChain2Sequence? #69367

mansbernhardt opened this issue Oct 23, 2023 · 6 comments

Comments

@mansbernhardt
Copy link

Using 0.1.0 of the library I sometimes get a crash (EXC_BAD_ACCESS (code=1, address=0xf2575c505790)) in iterator next(), see example of call stack below (caught from Xcode while debugging). Any ideas what might cause this? In the call stack below I was using a type erasers from: https://github.com/pointfreeco/swift-concurrency-extras, such as:

extension AsyncThrowingStream where Failure == Error {
  /// Produces an `AsyncThrowingStream` from an `AsyncSequence` by consuming the sequence till it
  /// terminates, rethrowing any failure.
  ///
  /// - Parameter sequence: An async sequence.
  public init<S: AsyncSequence>(_ sequence: S) where S.Element == Element {
    var iterator: S.AsyncIterator?
    self.init {
      if iterator == nil {
        iterator = sequence.makeAsyncIterator()
      }
      return try await iterator?.next()
    }
  }
#0	0x000000019035520c in _swift_release_dealloc ()
apple/swift-async-algorithms#1	0x0000000190355f00 in bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) ()
apple/swift-async-algorithms#2	0x00000001020a1eb4 in destroy for AsyncChain2Sequence ()
apple/swift-async-algorithms#3	0x00000001020de454 in destroy for AsyncRemoveDuplicatesSequence ()
apple/swift-async-algorithms#4	0x00000001020590c8 in ___lldb_unnamed_symbol212959 ()
apple/swift-async-algorithms#5	0x0000000190355224 in _swift_release_dealloc ()
apple/swift-async-algorithms#6	0x0000000190355f00 in bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) ()
apple/swift-async-algorithms#7	0x000000021f85c23c in closure apple/swift-async-algorithms#1 in closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#8	0x000000021f873df0 in partial apply for closure apple/swift-async-algorithms#1 in closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#9	0x000000021f851b20 in withTaskCancellationHandler<τ_0_0>(operation:onCancel:) ()
apple/swift-async-algorithms#10	0x000000021f84ffa0 in withTaskCancellationHandler<τ_0_0>(operation:onCancel:) ()
apple/swift-async-algorithms#11	0x000000021f85bd74 in closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#12	0x000000021f873db0 in partial apply for closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#13	0x000000021f85c4c8 in AsyncStream.Iterator.next() ()
apple/swift-async-algorithms#14	0x000000021f873da4 in protocol witness for AsyncIteratorProtocol.next() in conformance AsyncStream<τ_0_0>.Iterator ()
apple/swift-async-algorithms#15	0x000000021f83a954 in AsyncCompactMapSequence.Iterator.next() ()
apple/swift-async-algorithms#16	0x000000021f873e00 in protocol witness for AsyncIteratorProtocol.next() in conformance AsyncCompactMapSequence<τ_0_0, τ_0_1>.Iterator ()
apple/swift-async-algorithms#17	0x000000021f865734 in dispatch thunk of AsyncIteratorProtocol.next() ()
apple/swift-async-algorithms#18	0x0000000102058e14 in closure apple/swift-async-algorithms#1 in AsyncStream.init<τ_0_0>(_:) at /Users/mansbernhardt/Library/Developer/Xcode/DerivedData/Gigset-gkrmpjneavqivdheptpucovbtxjv/SourcePackages/checkouts/swift-concurrency-extras/Sources/ConcurrencyExtras/AsyncStream.swift:59
apple/swift-async-algorithms#19	0x00000001020591b0 in partial apply for closure apple/swift-async-algorithms#1 in AsyncStream.init<τ_0_0>(_:) ()
apple/swift-async-algorithms#20	0x000000021f85c144 in closure apple/swift-async-algorithms#1 in closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#21	0x000000021f873df0 in partial apply for closure apple/swift-async-algorithms#1 in closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#22	0x000000021f851b20 in withTaskCancellationHandler<τ_0_0>(operation:onCancel:) ()
apple/swift-async-algorithms#23	0x000000021f84ffa0 in withTaskCancellationHandler<τ_0_0>(operation:onCancel:) ()
apple/swift-async-algorithms#24	0x000000021f85bd74 in closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#25	0x000000021f873db0 in partial apply for closure apple/swift-async-algorithms#1 in AsyncStream.init(unfolding:onCancel:) ()
apple/swift-async-algorithms#26	0x000000021f85c4c8 in AsyncStream.Iterator.next() ()
apple/swift-async-algorithms#27	0x000000021f873da4 in protocol witness for AsyncIteratorProtocol.next() in conformance AsyncStream<τ_0_0>.Iterator ()
apple/swift-async-algorithms#28	0x000000021f865734 in dispatch thunk of AsyncIteratorProtocol.next() ()
apple/swift-async-algorithms#29	0x0000000102059fd8 in closure apple/swift-async-algorithms#1 in AsyncThrowingStream<>.init<τ_0_0>(_:) at /Users/mansbernhardt/Library/Developer/Xcode/DerivedData/Gigset-gkrmpjneavqivdheptpucovbtxjv/SourcePackages/checkouts/swift-concurrency-extras/Sources/ConcurrencyExtras/AsyncThrowingStream.swift:12
apple/swift-async-algorithms#30	0x000000010205a340 in partial apply for closure apple/swift-async-algorithms#1 in AsyncThrowingStream<>.init<τ_0_0>(_:) ()
apple/swift-async-algorithms#31	0x000000021f85d5ec in closure apple/swift-async-algorithms#1 in closure apple/swift-async-algorithms#1 in AsyncThrowingStream.init<>(unfolding:) ()
apple/swift-async-algorithms#32	0x000000021f873de4 in partial apply for closure apple/swift-async-algorithms#1 in closure apple/swift-async-algorithms#1 in AsyncThrowingStream.init<>(unfolding:) ()
apple/swift-async-algorithms#33	0x000000021f851b20 in withTaskCancellationHandler<τ_0_0>(operation:onCancel:) ()
apple/swift-async-algorithms#34	0x000000021f84ffa0 in withTaskCancellationHandler<τ_0_0>(operation:onCancel:) ()
apple/swift-async-algorithms#35	0x000000021f85d19c in closure apple/swift-async-algorithms#1 in AsyncThrowingStream.init<>(unfolding:) ()
apple/swift-async-algorithms#36	0x000000021f873dd4 in partial apply for closure apple/swift-async-algorithms#1 in AsyncThrowingStream.init<>(unfolding:) ()
apple/swift-async-algorithms#37	0x000000021f85d9b0 in AsyncThrowingStream.Iterator.next() ()
apple/swift-async-algorithms#38	0x00000001023d54a0 in closure apple/swift-async-algorithms#3 in AsyncView.body.getter at /Users/mansbernhardt/dev/gigset-apple4/gigset/Sources/ViewUtilities/AsyncView.swift:30
apple/swift-async-algorithms#39	0x00000001023d68d8 in partial apply for closure apple/swift-async-algorithms#3 in AsyncView.body.getter ()

We are also seeing Intel Macs running 12.x with this crash logs (release builds), not sure if they might be related:

Exception Type:        EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes:       0x0000000000000001, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Reason:    Namespace SIGNAL, Code 4 Illegal instruction: 4

Thread 4 Crashed:
0   libswiftCore.dylib            	0x00007ff828033245 _assertionFailure(_:_:file:line:flags:) + 421 (AssertCommon.swift:132)
1   libswift_Concurrency.dylib    	0x00007ffb34060b64 AsyncStream._Storage.next(_:) + 532 (AsyncStreamBuffer.swift:253)
2   libswift_Concurrency.dylib    	0x00007ffb340735ec partial apply for closure apple/swift-async-algorithms#2 in AsyncStream._Storage.next() + 188 (<compiler-generated>:0)
3   libswift_Concurrency.dylib    	0x00007ffb340742e1 thunk for @callee_guaranteed @async () -> (@out A?) + 1
@FranzBusch
Copy link
Member

This looks like a crash in AsyncStream itself so moving it over to the apple/swift repo.

@FranzBusch FranzBusch transferred this issue from apple/swift-async-algorithms Oct 24, 2023
@FranzBusch
Copy link
Member

@phausler could you take a look at this

@phausler
Copy link
Member

phausler commented Oct 24, 2023

AsyncStream._Storage.next(_:) no longer has an assert in it. Previously this was asserting that the consumption did not happen on more than one task. However that assertion could not be truly held in all cases (why it was removed). So no; that is not related.

@phausler
Copy link
Member

The root cause of the reported crash seems as if it is the destruction from the unfolding; my guess is that the iterator being sent is not actually Sendable and w/o the Sendable enforcements in place it is hitting a non-thread-safe usage of the capture of var iterator: S.AsyncIterator?.

Erasing to an AnyAsyncSequence using AsyncStream as a vehicle via that type of pattern can easily lead to missteps as such. The Sendable nature of the iterator in this case is non-trivial and should be held in a critical region (for example OSAllocatedUnfairLock) if you really need this type of behavior.

@phausler
Copy link
Member

here is the "corrected" code... however it would be considerably better to wait for the some AsyncSequence<Element, Never> etc when it is available.

  public init<S: AsyncSequence>(_ sequence: S) where S.Element == Element, S: Sendable {
    let iterator = OSAllocatedUnfairLock<S.AsyncIterator?>(uncheckedState: nil)
    self.init {
      // NOTE: this has a time of check/time of use bug lurking
      // if two tasks call next concurrently the iterator will
      // be in the wrong state
      var iter = iterator.withLock { $0 }
      if iter == nil {
        iter = sequence.makeAsyncIterator()
      }
      defer { iterator.withLockUnchecked { $0 = iter } }
      return try await iter?.next()
    }
  }

@mansatgigset
Copy link

@phausler

AsyncStream.Storage.next(:) no longer has an assert in it. Previously this was asserting that the consumption did not happen on more than one task. However that assertion could not be truly held in all cases (why it was removed). So no; that is not related.

In what OS version of macOS, iOS, tvOS was this assert removed?
This happens consistently on a Mac Intel machine on Mac OS 12.x, but as we can't catch this in a debugger as there does not seem to be an easy way to debug an older Mac OS machine, and Xcode with latest Swift does not even install on that system. I have tried to reproduce this on older iOS devices as well in the iOS simulator running iOS 15.x, but there the crash does not happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants