ReSwift icon indicating copy to clipboard operation
ReSwift copied to clipboard

Very weird crash on release mode (whole module optimisations)

Open Dimillian opened this issue 7 years ago • 16 comments

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.

screen shot 2017-11-08 at 10 08 47 screen shot 2017-11-08 at 10 08 52 screen shot 2017-11-08 at 10 12 21

Dimillian avatar Nov 08 '17 09:11 Dimillian

Update: May be related to #237 But it's kinda the reverse, no crash with no optimisations, crash with it.

Dimillian avatar Nov 08 '17 09:11 Dimillian

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)

Dimillian avatar Nov 08 '17 09:11 Dimillian

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.

Dimillian avatar Nov 09 '17 08:11 Dimillian

Can you provide code for a reducer that crashes the app?

DivineDominion avatar Nov 09 '17 13:11 DivineDominion

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

Dimillian avatar Nov 16 '17 09:11 Dimillian

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?

DivineDominion avatar Nov 16 '17 15:11 DivineDominion

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.

Dimillian avatar Nov 16 '17 16:11 Dimillian

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

Dimillian avatar Nov 16 '17 16:11 Dimillian

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).

azhabort avatar Jan 10 '19 12:01 azhabort

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.

Dimillian avatar Jan 10 '19 15:01 Dimillian

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?

dp-1a avatar Jan 21 '19 15:01 dp-1a

Yes, I've removed any reference to store. in my reducers and middleware.

Dimillian avatar Jan 21 '19 15:01 Dimillian

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 avatar Jan 27 '19 12:01 strangeliu

@strangeliu Same for me, there was no reference to the store. Had to disable the Swift compiler optimisations as well.

dp-1a avatar Jan 28 '19 08:01 dp-1a

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)

woelmer avatar Aug 01 '19 18:08 woelmer

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...

dp-1a avatar Aug 02 '19 06:08 dp-1a