Infinite recursion in observeSwitchToLatest()
datwelk opened this issue · comments
I occasionally receive crash reports with an infinite recursion in observeSwitchToLatest()
.
0 libswiftCore.dylib 0x00000001cc435978 getCache(swift::TargetTypeContextDescriptor<swift::InProcess> const&) + 8
1 LLLLLLLL 0x00000001027def08 __swift_instantiateGenericMetadata (<compiler-generated>:0)
2 LLLLLLLL 0x000000010281c038 ReactiveSwift.Signal.(Core in _6DF632AE8A9288C3EAD8EFDF3D3AF99E).send(ReactiveSwift.Signal<A, B>.Event) -> () (<compiler-generated>:0)
3 LLLLLLLL 0x000000010281b064 ReactiveSwift.Signal.Observer.send(ReactiveSwift.Signal<A, B>.Event) -> () (Signal.Observer.swift:111)
4 LLLLLLLL 0x00000001027f9780 generic partial specialization <Signature = @escaping @convention(thin) @convention(method) <A, B><A1 where B: Swift.Error, A1 == ReactiveSwift.Lock> (@in_guaranteed ReactiveSwift.Signal<A, B>.Event, @guaranteed ReactiveSwift.Signal<A, B>.(Core in _6DF632AE8A9288C3EAD8EFDF3D3AF99E)<ReactiveSwift.Lock>) -> ()> of ReactiveSwift.Signal.(Core in _6DF632AE8A9288C3EAD8EFDF3D3AF99E).send(ReactiveSwift.Signal<A, B>.Event) -> () (Signal.swift:124)
5 LLLLLLLL 0x000000010281b064 ReactiveSwift.Signal.Observer.send(ReactiveSwift.Signal<A, B>.Event) -> () (Signal.Observer.swift:111)
6 LLLLLLLL 0x00000001027f2db8 closure #3 (ReactiveSwift.Signal<A.Value, B>.Event) -> () in closure #1 (ReactiveSwift.Signal<A.Value, B>, ReactiveSwift.Disposable) -> () in closure #1 (ReactiveSwift.Signal<A, B>.Event) -> () in (extension in ReactiveSwift):ReactiveSwift.Signal< where A: ReactiveSwift.SignalProducerConvertible, B == A.Error>.(observeSwitchToLatest in _6345D8752C3E65AA1118F4C784F9873D)(ReactiveSwift.Signal<A.Value, B>.Observer, ReactiveSwift.SerialDisposable) -> ReactiveSwift.Disposable? (Flatten.swift:677)
7 LLLLLLLL 0x000000010281b064 ReactiveSwift.Signal.Observer.send(ReactiveSwift.Signal<A, B>.Event) -> () (Signal.Observer.swift:111)
8 LLLLLLLL 0x00000001027f9780 generic partial specialization <Signature = @escaping @convention(thin) @convention(method) <A, B><A1 where B: Swift.Error, A1 == ReactiveSwift.Lock> (@in_guaranteed ReactiveSwift.Signal<A, B>.Event, @guaranteed ReactiveSwift.Signal<A, B>.(Core in _6DF632AE8A9288C3EAD8EFDF3D3AF99E)<ReactiveSwift.Lock>) -> ()> of ReactiveSwift.Signal.(Core in _6DF632AE8A9288C3EAD8EFDF3D3AF99E).send(ReactiveSwift.Signal<A, B>.Event) -> () (Signal.swift:124)
9 LLLLLLLL 0x000000010281b064 ReactiveSwift.Signal.Observer.send(ReactiveSwift.Signal<A, B>.Event) -> () (Signal.Observer.swift:111)
From looking at the source code of ReactiveSwift, this seems only possible from using flatten(.latest) or flatMap(.latest)
.
In my codebase, I have identified three places that use flatten(.latest)
. The primary suspect has been refactored to using Combine. Unfortunately this did not solve the problem. The other two suspects do not seem suspicious to me:
- Uses
flatMapError
to return a new producer, up to 3 times, after a delay of 10 seconds:
Foo.produce().flatMapError {
if retry < 3 {
return SignalProducer.timer(interval: .seconds(10), on: QueueScheduler.main)
.take(first: 1)
.flatMap(.latest) {
return Foo.produce()
}
}
}
- Similarly, recursively returns a new producer instance until a condition is met:
func recursiveProducer() -> {
return Foo.producer()
.flatMap(.latest) {
return recursiveProducer()
}
}
The condition is checked inside Foo.producer()
, if the condition is met the returned producer will complete immediately without sending a value. Before the condition is met, a producer is returned that emits one value and completes, or an error.
I am using ReactiveSwift 6.6.1. The stack trace is attached. Is either of the use-cases above the culprit, or am I missing something else?
I'm starting to think that a Swift compiler optimization (bug) might be causing this. Never seen the bug in debug builds (no optimization), but I do see it in release builds (-Os optimization)
Hello @datwelk,
Have you got any solution for this issue? I am also facing same kind of issue in our project.
Thank you.
Yes the issue arises when using a recursive signal without delay in between.