Crash in Store.send #1098
-
Hi, I did have these crashes in production(crash1.crash, crash2.crash) and I don't understand how to fix them because I have no way to reproduce them. If you have some ideas 🤷♂️ I will be glad to hear them. One way to help solve this kind of issue would be to have TCA What do you think about that? Thanks, |
Beta Was this translation helpful? Give feedback.
Replies: 9 comments
-
@mackoj OK if I convert this to a discussion? I think until we can reproduce and determine if this is a bug with the library or not there's not a ton we can do here. |
Beta Was this translation helpful? Give feedback.
-
Ah, you already opened #1096. Cool to close this issue in favor of discussion there then? |
Beta Was this translation helpful? Give feedback.
-
Hi @mackoj, my guess is, as usual, a stack overflow with your app's state. I'm not expert in reading crash logs, but the arrow in
seems to point in this direction, as well as the first frame for the first crash:
This can happen if your app's state grows beyond the stack size (which is 1MB for the main thread on iOS). This kind of crash can be difficult to reproduce. You should try to fill you state with large values if possible (arrays, string and dictionaries don't count, as they're already allocated on the heap). If you find a way to reproduce the issue, you can try to mitigate it by boxing branches of your state as reference types. There are several solutions. The simplest/dirtiest is to use a 1-value array for the storage, and use a computed property to provide access to the original field. You can also implement your own |
Beta Was this translation helpful? Give feedback.
-
Sorry for the closing and opening @stephencelis but the #1096 is not the exact same question so maybe we should convert this one into a discussion ? |
Beta Was this translation helpful? Give feedback.
-
@tgrapperon thanks for the answer it might be the issue but the problem is I don't know where in the app it crash. This is why having a way to know what is the next action to be executed could help to add a Box to the state or fix the issue entirely. |
Beta Was this translation helpful? Give feedback.
-
Reposting what I wrote in #1096: I would suggest to use a boxing property wrapper to try to mitigate potential stack overflows. You will probably find discussion on the topic if you search for "stack overflow" in this repository (not on stackoveflow.com!). You can find some example of boxing property wrappers in #488 for example. But you will need to be able to reproduce the issue first, otherwise you will only have to guess in the void. As I'm looking more closely to the stack traces, you have very deep Combine recursions with a repeating pattern, so maybe the problem comes from here and not your app's state. You have maybe an infinite loop somewhere, but it's hard to tell without more information. |
Beta Was this translation helpful? Give feedback.
-
Infinite loop was my first guess and I did get a log of all the actions perform before the crash(but due to the crash maybe not all of them were recorded) and it not seems to be a infinite loop. I like the idea of "stack overflow" I already add one before and fix it with the The main issue as you explained is how to find where it is and maybe adding some hooks like explain in #1096 could help. I will ship the next version with my fork of TCA with the hooks and some remote logging to try to know where to fix. @propertyWrapper
public struct CoW<T> {
private final class Ref {
var val: T
init(_ v: T) { val = v }
}
private var ref: Ref
public init(wrappedValue: T) { ref = Ref(wrappedValue) }
public var wrappedValue: T {
get { ref.val }
set {
if !isKnownUniquelyReferenced(&ref) {
ref = Ref(newValue)
return
}
ref.val = newValue
}
}
}
//Restore automatic protocol conformance:
extension CoW: Equatable where T: Equatable {
public static func == (lhs: CoW<T>, rhs: CoW<T>) -> Bool {
lhs.wrappedValue == rhs.wrappedValue
}
}
extension CoW: Hashable where T: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(wrappedValue)
}
}
extension CoW: Decodable where T: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = try container.decode(T.self)
self = CoW(wrappedValue: value)
}
}
extension CoW: Encodable where T: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue)
}
} |
Beta Was this translation helpful? Give feedback.
-
I will use Instrument and try to find my 5 biggest graph of object and Box them 🤷♂️. |
Beta Was this translation helpful? Give feedback.
-
I did have this issue before... #752 😭 |
Beta Was this translation helpful? Give feedback.
Hi @mackoj, my guess is, as usual, a stack overflow with your app's state. I'm not expert in reading crash logs, but the arrow in
seems to point in this direction, as well as the first frame for the first crash: