EXC_BAD_INSTRUCTION with Combine throttle

Hello, I got an exception while I am trying .throttle operation in Combine. Am I misusing something or is it supposed to be unstable now?

  • Xcode Version 11.0 (11A419c)
  • macOS 10.15 Beta (19A558d)

Here's full source code to reproduce.

import Foundation
import Combine
let testQ = DispatchQueue(label: "TestQ")
let testSubject = PassthroughSubject<String,Never>()
let testPipeline = testSubject
    .throttle(for: .seconds(1), scheduler: testQ, latest: true)
    .sink(receiveValue: { print($0) })
for _ in 0..<1000000 {
    DispatchQueue.global().async {
        testSubject.send("AAAA!!!!") // <------ Debugger stops at here.
    }
}
RunLoop.main.run()

Error:

Thread 2: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

Stack trace.

Thread 1 Queue : com.apple.main-thread (serial)
Thread 2 Queue : com.apple.root.default-qos (concurrent)
#0	0x00007fff6d219485 in specialized static Strideable.< infix(_:_:) ()
#1	0x00007fff6d217d29 in protocol witness for static Comparable.>= infix(_:_:) in conformance OS_dispatch_queue.SchedulerTimeType ()
#2	0x00007fff354d78de in Publishers.Throttle.Inner.receive(_:) ()
#3	0x00007fff354d8940 in protocol witness for Subscriber.receive(_:) in conformance Publishers.Throttle<A, B>.Inner<A1> ()
#4	0x00007fff3546a9d3 in AnySubscriberBox.receive(_:) ()
#5	0x00007fff354760e0 in PassthroughSubject.Conduit.offer(_:) ()
#6	0x00007fff35475e28 in PassthroughSubject.send(_:) ()
#7	0x0000000100001f63 in closure #2 in  at /Users/henry/TestingWorkshop/experiment/CombineThrottleIssue2/Sources/CombineThrottleIssue2/main.swift:18
#8	0x0000000100001fbd in thunk for @escaping @callee_guaranteed () -> () ()
#9	0x00000001003a2793 in _dispatch_call_block_and_release ()
#10	0x00000001003a376f in _dispatch_client_callout ()
#11	0x00000001003a5dd9 in _dispatch_queue_override_invoke ()
#12	0x00000001003b7331 in _dispatch_root_queue_drain ()
#13	0x00000001003b7da3 in _dispatch_worker_thread2 ()
#14	0x000000010042f04d in _pthread_wqthread ()
#15	0x000000010042eef3 in start_wqthread ()
Enqueued from com.apple.main-thread (Thread 1) Queue : com.apple.main-thread (serial)
#0	0x00000001003a83cf in dispatch_async ()
#1	0x00007fff6d213a82 in OS_dispatch_queue.async(group:qos:flags:execute:) ()
#2	0x0000000100001708 in main at /Users/henry/TestingWorkshop/experiment/CombineThrottleIssue2/Sources/CombineThrottleIssue2/main.swift:17
#3	0x00007fff6d7bf2a5 in start ()
1 Like

I have the same issue with Xcode 11.3.

Has anyone succeeded to overcome it since September?

I ended up using RunLoop as a scheduler instead of a DispatchQueue. It seems like implementation of throttle operator relies on a Timer somehow.

1 Like

Thanks Artem for the tip, this helped me fix my problem. I had a hard time tracking down a crash some of my users reported in the field, so I'll add some context here and hopefully help others find this solution.

I was receiving crash logs in crashlytics which I could not reproduce myself. The logs simply told me:
EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). All crashes happened on iOS 13.1 versions (13.1.1, 13.1.2 and 13.1.3), both on iOS and iPadOS.

It seemed to happen on the sink call, but really it was the throttle, which was indeed using a DispatchQueue as scheduler. Changing the trottle to use a RunLoop.main (not RunLoop.current) as scheduler fixed the issue and also seems to work fine on newer versions of iOS.

Thanks for this!! I had a similar problem with a Publisher subclass, but receiving on main did the trick.

FWIW, downstream (testSubject.send) needs to be synchronized. You can't concurrently call this function. Same with upstream request (subscription.request)

It's been previously stated that Subject.send is actually thread-safe:

1 Like