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

replaceError(with:) results in EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) #12

Closed
BrianDoig opened this issue Mar 10, 2020 · 6 comments

Comments

@BrianDoig
Copy link

The following code results in a crash when run, but operates fine if you remove the call to replaceError. This is an issue if you want to use the results of an Observable with a Subscriber who's failure is Never.

    let observable = Observable.just("Hello Combine")
            .publisher
            .replaceError(with: "Error")
            .sink(receiveValue: { (value) in
                print(value)
            })

Here is the code that the crash is occurring on (stack frame 2 is the last frame with symbols). Merely calling 'frame variable' from the debugger prompt crashes the debug session inside of Xcode. The automatic variables are as follows:

event RxSwift.Event<τ_0_0.Input>
self τ_0_0
element τ_0_0.Input

extension Subscriber where Failure == Never {
    /// Push a provided RxSwift.event onto the Combine Subscriber.
    ///
    /// - parameter event: RxSwift Event to push onto the Combine Subscriber.
    func pushRxEvent(_ event: RxSwift.Event<Input>) {
        switch event {
        case .next(let element):
            _ = receive(element)
        case .error:
            fatalError("Failure is not an option")
        case .completed:
            receive(completion: .finished)
        }
    }
}

extension Subscriber where Failure == Swift.Error {
    /// Push a provided RxSwift.event onto the Combine Subscriber.
    ///
    /// - parameter event: RxSwift Event to push onto the Combine Subscriber.
    func pushRxEvent(_ event: RxSwift.Event<Input>) {
        switch event {
        case .next(let element):
            _ = receive(element)   <----- Crashes here: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
        case .error(let error):
            receive(completion: .failure(error))
        case .completed:
            receive(completion: .finished)
        }
    }
}

Here is the full call stack upon crash.

Thread 1 Queue : com.apple.main-thread (serial)
#0	0x00007fff233b167a in Publishers.ReplaceError.Inner.receive(_:) ()
#1	0x00007fff233b1b70 in protocol witness for Subscriber.receive(_:) in conformance Publishers.ReplaceError<A>.Inner<A1> ()
#2	0x000000010802cae6 in Subscriber<>.pushRxEvent(_:) at /Users/doig/Documents/Projects/SwiftUITest/Pods/RxCombine/Sources/Rx+Combine/Subscriber+Rx.swift:35
#3	0x000000010802a5e4 in partial apply for Subscriber<>.pushRxEvent(_:) ()
#4	0x00000001081d83fc in closure #1 in ObservableType.subscribe(_:) at /Users/doig/Documents/Projects/SwiftUITest/Pods/RxSwift/RxSwift/ObservableType+Extensions.swift:23
#5	0x000000010810920f in AnonymousObserver.onCore(_:) at /Users/doig/Documents/Projects/SwiftUITest/Pods/RxSwift/RxSwift/Observers/AnonymousObserver.swift:22
#6	0x00000001081e0ea1 in ObserverBase.on(_:) at /Users/doig/Documents/Projects/SwiftUITest/Pods/RxSwift/RxSwift/Observers/ObserverBase.swift:16
#7	0x00000001081e1155 in protocol witness for ObserverType.on(_:) in conformance ObserverBase<A> ()
#8	0x00000001081b57d9 in Just.subscribe<A>(_:) at /Users/doig/Documents/Projects/SwiftUITest/Pods/RxSwift/RxSwift/Observables/Just.swift:83
#9	0x00000001081d833d in ObservableType.subscribe(_:) at /Users/doig/Documents/Projects/SwiftUITest/Pods/RxSwift/RxSwift/ObservableType+Extensions.swift:25
#10	0x000000010802a171 in RxPublisher.receive<A>(subscriber:) at /Users/doig/Documents/Projects/SwiftUITest/Pods/RxCombine/Sources/Rx+Combine/Observable+Combine.swift:43
#11	0x000000010802a455 in protocol witness for Publisher.receive<A>(subscriber:) in conformance RxPublisher<A> ()
#12	0x00007fff233c1571 in PublisherBox.receive<A>(subscriber:) ()
#13	0x00007fff233c1715 in AnyPublisher.receive<A>(subscriber:) ()
#14	0x00007fff2335eb59 in Publisher.subscribe<A>(_:) ()
#15	0x00007fff233b1023 in Publishers.ReplaceError.receive<A>(subscriber:) ()
#16	0x00007fff2335eb59 in Publisher.subscribe<A>(_:) ()
#17	0x00007fff2333709d in Publisher<>.sink(receiveValue:) ()
#18	0x0000000107114619 in ContentView.init() at /Users/doig/Documents/Projects/SwiftUITest/SwiftUITest/ContentView.swift:63
#19	0x00000001071112c2 in SceneDelegate.scene(_:willConnectTo:options:) at /Users/doig/Documents/Projects/SwiftUITest/SwiftUITest/SceneDelegate.swift:23
#20	0x00000001071116f6 in @objc SceneDelegate.scene(_:willConnectTo:options:) ()
#21	0x00007fff4761d3e0 in +[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] ()
#22	0x00007fff4808f170 in -[UIApplication _connectUISceneFromFBSScene:transitionContext:] ()
#23	0x00007fff4808f4b2 in -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] ()
#24	0x00007fff47bfa7f5 in -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] ()
#25	0x00007fff365d6165 in -[FBSSceneImpl _callOutQueue_agent_didCreateWithTransitionContext:completion:] ()
#26	0x00007fff365fc4d8 in __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke.154 ()
#27	0x00007fff365e0c45 in -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] ()
#28	0x00007fff365fc169 in __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke ()
#29	0x0000000108888d48 in _dispatch_client_callout ()
#30	0x000000010888bcb9 in _dispatch_block_invoke_direct ()
#31	0x00007fff3662237e in __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ ()
#32	0x00007fff3662206c in -[FBSSerialQueue _queue_performNextIfPossible] ()
#33	0x00007fff3662257b in -[FBSSerialQueue _performNextFromRunLoopSource] ()
#34	0x00007fff23bd4471 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#35	0x00007fff23bd439c in __CFRunLoopDoSource0 ()
#36	0x00007fff23bd3bcc in __CFRunLoopDoSources0 ()
#37	0x00007fff23bce87f in __CFRunLoopRun ()
#38	0x00007fff23bce066 in CFRunLoopRunSpecific ()
#39	0x00007fff384c0bb0 in GSEventRunModal ()
#40	0x00007fff48092d4d in UIApplicationMain ()
#41	0x00000001071108ab in main at /Users/doig/Documents/Projects/SwiftUITest/SwiftUITest/AppDelegate.swift:12
#42	0x00007fff5227ec25 in start ()
#43	0x00007fff5227ec25 in start ()

@freak4pc
Copy link
Member

freak4pc commented Mar 11, 2020

Thanks for the report! My current instinct is that this is a bug in replaceError(with:) itself, since other variations of this don't crash.

e.g.:

assertNoFailure

cancellable = Observable
    .just("Hello Combine")
    .publisher
    .assertNoFailure()
    .sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })

catch

cancellable = Observable
    .just("Hello Combine")
    .publisher
    .catch { _ -> AnyPublisher<String, Never> in
        Just("Error").eraseToAnyPublisher()
    }
    .sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })

I think we should open a bug on the Swift forums, but not sure they'll enjoy reproducing this with a third-party dependency :)

Wondering if we could reduce this to a smaller reproducible state. Any thoughts? @BrianDoig

@BrianDoig
Copy link
Author

I'm honestly not sure how to reduce it smaller than

let observable = Observable.just("Hello Combine")
        .publisher
        .replaceError(with: "Error")
        .sink(receiveValue: { (value) in
            print(value)
        })

I know very little about Combine other than it's similar to RxSwift. This is already after I reduced the problem to just Combine, RxSwift and RxCombine. We have a bunch of RxSwift based libraries and they want us to start using SwiftUI and Combine so I was looking into getting our code existing code to work with SwiftUI. I didn't even know that assertNoFailure and catch would work. At the very least I can make my own method that does the catch for me as part of publish. Along the lines of publishReplaceError(with:)

@BrianDoig
Copy link
Author

I was able to work around my problems at least using the following extension. Not sure if you want to include it in the library or follow up with Apple.

extension ObservableConvertibleType {

    /// Returns a `AnyPublisher` of the underlying Observable's Element type
    /// so the Observable pushes events to the Publisher.
    ///
    /// - returns: AnyPublisher of the underlying Observable's Element type.
    /// - note: This is a work around for a crash when using replaceError(with:)
    public func asPublisherReplaceError(with: Element) -> AnyPublisher<Self.Element, Never> {
        return self
            .publisher
            .catch({ _ -> AnyPublisher<Self.Element, Never> in
                Just(with).eraseToAnyPublisher()
            })
            .eraseToAnyPublisher()
    }
}

@freak4pc
Copy link
Member

I have a fix for this, will upload soon @BrianDoig

@freak4pc
Copy link
Member

This was solved in RxCombine 1.5.0, would really appreciate knowing if this helped you @BrianDoig

@BrianDoig
Copy link
Author

BrianDoig commented Mar 23, 2020

So... It was fixed but a second issue showed up with replaceError.

class UserViewModel: ObservableObject {
    @Published var sliderValue = 0.0
    @Published var rxswift = "Boo Hiss"
    
    var sliderString: String {
        return "\(sliderValue)"
    }
}

struct ContentView: View {

    @ObservedObject private var userViewModel: UserViewModel
    
    let relay = PublishedBehaviorRelay(initialValue: "Start")
    
    var cancelables: Set<AnyCancellable> = []
    let disposeBag = DisposeBag()
    
    init() {
        let uvm = UserViewModel()
        userViewModel = uvm
        
        // A publisher publishing numbers
        let data = 0...1000
        data
            .publisher
            .map({ "\($0)" })
            .modulated(0.01, bufferSize: data.count, scheduler: RunLoop.main)
            .eraseToAnyPublisher()
            .asObservable()
            .bind(to: relay.dataSource)
            .disposed(by: disposeBag
        )
        
        // CRASH ON THIS LINE OF CODE
        relay.publisher.assign(to: \.rxswift, on: uvm).store(in: &cancelables) 
        
    }

    var body: some View {
        VStack {
            Slider(value: $userViewModel.sliderValue)
                .padding(.horizontal, 40.0)
            VStack {
                Text("Hello, SwiftUI!")
                    .font(.largeTitle)
                    .foregroundColor(.green)
                    .padding(0.0)
                Text(userViewModel.sliderString)
                    .font(.title)
                    .foregroundColor(Color.black)
                    .multilineTextAlignment(.center)
                Text(userViewModel.rxswift)
            }
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

This code works fine with the asPublisherReplaceError(with:) but crashes in a new spot when run.

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x00007fff233b167a Combine`Combine.Publishers.ReplaceError.(Inner in _50EC2DA41500D4F648F58C2EE8939789).receive(A.Output) -> Combine.Subscribers.Demand + 314
    frame #1: 0x00007fff233b1b70 Combine`protocol witness for Combine.Subscriber.receive(A.Input) -> Combine.Subscribers.Demand in conformance Combine.Publishers.ReplaceError<A>.(Inner in _50EC2DA41500D4F648F58C2EE8939789)<A1> : Combine.Subscriber in Combine + 16
    frame #2: 0x0000000103ba2ae6 RxCombine`Subscriber<>.pushRxEvent(event=RxSwift.Event<τ_0_0.Input> @ scalar, self=<no summary available>) at Subscriber+Rx.swift:35:17
    frame #3: 0x0000000103ba05e4 RxCombine`partial apply for Subscriber<>.pushRxEvent(_:) at <compiler-generated>:0
    frame #4: 0x0000000103ca83fc RxSwift`closure #1 in ObservableType.subscribe(e=, on=0x0000000103ba05b0 RxCombine`partial apply forwarder for (extension in RxCombine):Combine.Subscriber< where A.Failure == Swift.Error>.pushRxEvent(RxSwift.Event<A.Input>) -> () at <compiler-generated>) at ObservableType+Extensions.swift:23:17
    frame #5: 0x0000000103bd920f RxSwift`AnonymousObserver.onCore(event=, self=0x0000600001b8b570) at AnonymousObserver.swift:22:21
    frame #6: 0x0000000103cb0ea1 RxSwift`ObserverBase.on(event=, self=0x0000600001b8b570) at ObserverBase.swift:16:22
    frame #7: 0x0000000103cb1155 RxSwift`protocol witness for ObserverType.on(_:) in conformance ObserverBase<A> at <compiler-generated>:0
    frame #8: 0x0000000103bec049 RxSwift`BehaviorSubject._synchronized_subscribe<Element>(observer=0x0000600001b8b570, self=0x00006000027a8120) at BehaviorSubject.swift:128:18
    frame #9: 0x0000000103beba1c RxSwift`BehaviorSubject.subscribe<Element>(observer=0x0000600001b8b570, self=0x00006000027a8120) at BehaviorSubject.swift:111:33
    frame #10: 0x0000000103ca833d RxSwift`ObservableType.subscribe(on=0x0000000103ba05b0 RxCombine`partial apply forwarder for (extension in RxCombine):Combine.Subscriber< where A.Failure == Swift.Error>.pushRxEvent(RxSwift.Event<A.Input>) -> () at <compiler-generated>, self=0x00006000027a8120) at ObservableType+Extensions.swift:25:40
    frame #11: 0x0000000103ba0171 RxCombine`RxPublisher.receive<Upstream>(subscriber=<no summary available>, self=0x0000600001590e20) at Observable+Combine.swift:43:58
    frame #12: 0x0000000103ba0455 RxCombine`protocol witness for Publisher.receive<A>(subscriber:) in conformance RxPublisher<A> at <compiler-generated>:0
    frame #13: 0x00007fff233c1571 Combine`Combine.PublisherBox.receive<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(subscriber: A1) -> () + 33
    frame #14: 0x00007fff233c1715 Combine`Combine.AnyPublisher.receive<A where A == A1.Input, B == A1.Failure, A1: Combine.Subscriber>(subscriber: A1) -> () + 53
    frame #15: 0x00007fff2335eb59 Combine`(extension in Combine):Combine.Publisher.subscribe<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(A1) -> () + 873
    frame #16: 0x00007fff233b1023 Combine`Combine.Publishers.ReplaceError.receive<A where A1: Combine.Subscriber, A.Output == A1.Input, A1.Failure == Swift.Never>(subscriber: A1) -> () + 371
    frame #17: 0x00007fff233c1571 Combine`Combine.PublisherBox.receive<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(subscriber: A1) -> () + 33
    frame #18: 0x00007fff233c1715 Combine`Combine.AnyPublisher.receive<A where A == A1.Input, B == A1.Failure, A1: Combine.Subscriber>(subscriber: A1) -> () + 53
    frame #19: 0x00007fff2335eb59 Combine`(extension in Combine):Combine.Publisher.subscribe<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(A1) -> () + 873
    frame #20: 0x00007fff2339d539 Combine`(extension in Combine):Combine.Publisher< where A.Failure == Swift.Never>.assign<A>(to: Swift.ReferenceWritableKeyPath<A1, A.Output>, on: A1) -> Combine.AnyCancellable + 265
  * frame #21: 0x00000001035e8a22 SwiftUITest`ContentView.init() at ContentView.swift:62:25
    frame #22: 0x00000001035d9847 SwiftUITest`SceneDelegate.scene(scene=0x00007fa1f0705940, session=0x0000600000083e00, connectionOptions=0x00006000015ad6c0, self=0x00006000015ad700) at SceneDelegate.swift:23:27
    frame #23: 0x00000001035d9d36 SwiftUITest`@objc SceneDelegate.scene(_:willConnectTo:options:) at <compiler-generated>:0
    frame #24: 0x00007fff4761d3e0 UIKitCore`+[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] + 1304
    frame #25: 0x00007fff4808f170 UIKitCore`-[UIApplication _connectUISceneFromFBSScene:transitionContext:] + 1018
    frame #26: 0x00007fff4808f4b2 UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 304
    frame #27: 0x00007fff47bfa7f5 UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 361
    frame #28: 0x00007fff365d6165 FrontBoardServices`-[FBSSceneImpl _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 442
    frame #29: 0x00007fff365fc4d8 FrontBoardServices`__86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke.154 + 102
    frame #30: 0x00007fff365e0c45 FrontBoardServices`-[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 220
    frame #31: 0x00007fff365fc169 FrontBoardServices`__86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke + 355
    frame #32: 0x000000010400ad48 libdispatch.dylib`_dispatch_client_callout + 8
    frame #33: 0x000000010400dcb9 libdispatch.dylib`_dispatch_block_invoke_direct + 300
    frame #34: 0x00007fff3662237e FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    frame #35: 0x00007fff3662206c FrontBoardServices`-[FBSSerialQueue _queue_performNextIfPossible] + 441
    frame #36: 0x00007fff3662257b FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 22
    frame #37: 0x00007fff23bd4471 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #38: 0x00007fff23bd439c CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #39: 0x00007fff23bd3bcc CoreFoundation`__CFRunLoopDoSources0 + 268
    frame #40: 0x00007fff23bce87f CoreFoundation`__CFRunLoopRun + 1263
    frame #41: 0x00007fff23bce066 CoreFoundation`CFRunLoopRunSpecific + 438
    frame #42: 0x00007fff384c0bb0 GraphicsServices`GSEventRunModal + 65
    frame #43: 0x00007fff48092d4d UIKitCore`UIApplicationMain + 1621
    frame #44: 0x00000001035d8e2b SwiftUITest`main at AppDelegate.swift:15:7
    frame #45: 0x00007fff5227ec25 libdyld.dylib`start + 1
    frame #46: 0x00007fff5227ec25 libdyld.dylib`start + 1
(lldb) 

Stack frame 4 is this code

    public func subscribe(_ on: @escaping (Event<Element>) -> Void)
        -> Disposable {
            let observer = AnonymousObserver { e in
                on(e)   <----- CRASH HERE
            }
            return self.asObservable().subscribe(observer)
    }

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

2 participants