ReSwift
ReSwift copied to clipboard
Very weird crash on release mode (whole module optimisations)
So my new app works perfectly fine in debug mode, but I've just noticed that if I build it for release mode, and so use the Swift compiler optimisations, it crashes on launch at the store init.
I have no idea if this is due to ReSwift or some other Swift compiler issue, but maybe some of you got the same issue. I'll try various build configuration and Xcode version to see if it changes anything. Of course, if I turn off code optimisation in release, it works, as in debug mode. But can't leave it out of App Store build.
I'm using Swift 4 + Xcode 9.2.
Here is the crash + the launch call tree, maybe it'll shine a light on it. Didn't had time to really dig into it, but will do now, just wanted to report.



Update: May be related to #237 But it's kinda the reverse, no crash with no optimisations, crash with it.
Relevant part of the crash log
Application Specific Information:
BUG IN CLIENT OF LIBDISPATCH: trying to lock recursively
Filtered syslog:
None found
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libdispatch.dylib 0x0000000185481590 _dispatch_gate_wait_slow$VARIANT$mp + 164
1 Glose 0x0000000102263164 0x1020d4000 + 1634660
2 Glose 0x0000000102263164 0x1020d4000 + 1634660
3 Glose 0x00000001022e57b0 0x1020d4000 + 2168752
4 Glose 0x00000001022ca94c 0x1020d4000 + 2058572
5 ReSwift 0x000000010306f39c Store._defaultDispatch(action:) + 29596 (Store.swift:123)
After some more investigation, it seems like it's the reducer code which is problematic, for some reason, if you access other part of your state (substate) in your reducer it crash. Why? Because for some other unknown reason, the switch case for actions inside the reducer function are all executed at the launch of the app when code optimisation is on.
I have no idea what's going on.
Can you provide code for a reducer that crashes the app?
I've discussed with an Apple engineer working on the Swift compiler. Basically I've turned off optimisation on two reducers with annotations
@_semantics("optimize.sil.never")
And now the app work in release mode with optimisation turned on.
Here is the code of the two problematic reducer, I'm betting my timer pattern is not very good, but this was not the problem, it continued crashing even after removing that code
https://gist.github.com/Dimillian/bda19ae81b741f14c09ad214e20a27e5
https://gist.github.com/Dimillian/044a988e72c61eaa01330b9c9b18266c
The timer is weird, but the compiler problem should be something else, sure. Did you try removing all cases except default
and then add back one by one to find out when it begins to break?
I must admit I didn't took (yet) the time to do that. But I'll for sure. Need to pinpoint the issue before shipping to production.
About the timer... well, it's there for now because I have no better place, and since I'm on a serial thread for my whole store, I have no issue. It's just not a good pattern, will fix it later :D
I've faced this issue only on 32bit device with iOS 9.3.5 with code compiled in release mode. On 64bit device it works without crashes.
Also as for today @_semantics("optimize.sil.never")
is replaced with: @_semantics(none)
.
I never got that problem again when I properly got out my store.states out of my reducers and middleware (out of everywhere actually). It was just bad coding on my part. So you should not even need those annotations in the end.
Hi, did anyone find the root cause for this problem? Because I'm experiencing the same issue.
@Dimillian what did it solve in the end? You removed all references to the state in the reducer or what was your solution?
Yes, I've removed any reference to store. in my reducers and middleware.
I'm experiencing the same issue too, and there is no reference type in my state, for now i have set optimize for speed to get over this
@strangeliu Same for me, there was no reference to the store. Had to disable the Swift compiler optimisations as well.
I encountered the same issue. I refactored my reducers and middleware, like many have done, and still experienced the issue. I had one middleware that would trigger the problem. I looked at the differences between this middleware and the others. This is how I solved it -- still not sure why this works:
When I had my middleware code inside the switch statement's case the crash would happen.
func customTicketSaga(action: Action, context: MiddlewareContext<AppState>) -> Action? {
switch action {
case _ as SaveCustomTicket:
// ... code was directly here in the case ...
case _ as CustomTicketNumberSelected:
// ... code was directly here in the case ...
default:
break
}
return action
}
let customTicketMiddleWare = createMiddleware(customTicketSaga)
When I refactored the code into functions, the problem went away.
func customTicketSaga(action: Action, context: MiddlewareContext<AppState>) -> Action? {
switch action {
case _ as SaveCustomTicket:
return saveCustomTicketSaga(action: action, context: context)
case _ as CustomTicketNumberSelected:
return customTicketNumberSelectedSaga(action: action, context: context)
default:
return action
}
}
let customTicketMiddleWare = createMiddleware(customTicketSaga)
Hi,
after a bit of investigation it seems to be related to what you do in the reducer, but it seem it is not actually a bug in ReSwift. As the reducer is just a global function you can run into race conditions when calling other parts which are not initialised yet when the reducer is being called. E.g. Our crash was actually solved by changing the way we initialised singletons which were called inside the reducer.
Therefore, check the code which is being called in the reducer and make sure it is properly initialised at the time the reducer is executed. Nasty part is that this only happens with the release build configuration and not in the debug builds...