-
Notifications
You must be signed in to change notification settings - Fork 566
EXC_BAD_ACCESS when releasing lambda #2052
Comments
What if you freeze() the lambda before the call, as described in https:/JetBrains/kotlin-native/blob/master/CONCURRENCY.md#object-transfer-and-freezing? |
I seem to have faced the same issue: delegate returns a lambda, which is supposed to be called in another thread by a framework. I do get Here is obj-c sample https:/TextureGroup/Texture/blob/master/examples/ASViewController/Sample/ViewController.m:
It's ported to kotlin native as follows:
|
Could you please provide self-contained reproducer? |
I'm using this commit
My working dir is Here is git diff: patch.diff.zip Additionally
Build steps:
In order to reproduce the crash comment
|
Have you called
|
No. I've just tried to call
Is it a supposed way to use |
Yes. |
So this way it works for you? |
Yes. |
@brettwillis what about you, could you please elaborate a bit, if freezing help you? |
@olonho thank you for your support, I'll give In the meantime may I ask, will
If so, I fear that this would effectively freeze the entire business logic of my application. |
So could you only pass to the worker state which is either immutable or shall belong to worker only? Otherwise, your program has race, which is essentially detected for you by K/N. |
Well I use the "listener" lambda to emit items into a I guess we'll find out when I can give it a try :) |
Note that one could use AtomicReference to pass immutable state between workers in the way similar to channel. |
Hi @olonho, so I gave it a try. As I suspected, when I freeze the lambda, I get invalid mutability errors all the way down the chain of my program. Curiously the first of such errors is on an instance of I have worked around this by explicitly retaining a reference to the lambda in my code, and ensuring it gets released only after the Firestore backend has released its reference. This way I ensure it gets released on the main thread. However this required a hack to the Firestore backend code (because it releases the reference asynchronously), so not a great solution at all. This use case would seem to me to be quite common (that is, converting callbacks to How would you consider solving this in Kotlin/Native? Would it be solved when multi threading is properly implemented? |
Please provide reproducer, so we could give meaningful feedback. There's no AtomicRef in Kotlin/Native, only AtomicReference, and its update operation can work on frozen values, for example
will print Not sure what do you mean by "when multi threading is properly implemented" - it is already there, and not much changes planned here. Just concurrency in Kotlin/Native provides automated concurrency correctness analysis, so hidden bugs and races are explicit. |
I was referring to kotlinx.coroutines issue 462. I gather from what you are saying that the issue is not with Kotlin/Native, but incomplete coroutines implementation on the Kotlin/Native platform, am I correct?
So the kotlinx.atomicfu implementation of AtomicRef doesn’t work once frozen, I must use AtomicReference. Is there any plan to unify those classes? To implement my use-case using AtomicReference, do you suggest that I use AtomicReference to pass the channel into the lambda, or actually use AtomicReference instead of the channel? If it’s the latter, could you provide an example of how to receive from the AtomicReference in an asynchronous/suspending way? |
I'm using Kotlin 1.3.0-rc-57, Kotlin/Native 0.9.2-4008. It seems the crash appears in releasing kotlin lambda. The crashing devices are iOS 10.3.3, 11.4.0, 11.4.1, 12.0.0 ... so I think it is not specific iOS version, but I can't reproduce it in my development devices. My code is below:
|
@irgaly the exact circumstances of this crash aren't clear without a reproducer, however I have an assumption and the next release is likely to include the fix that may help. |
@irgaly Have you tried Kotlin 1.3.0? |
I have updated to Kotlin 1.3.0-rc-202 / Kotlin Native 1.0.0 last week. |
This version includes the fix. Do you still encounter the same issue with |
I have updated to Kotlin 1.3.0, reverted the workaround which averted the crash, and unfortunately I was still able to reproduce the problem. I know you're still waiting for a reproducer but my first priority is heading towards a first release and my workaround works for now. |
@brettwillis as far as I remember, in your case a crash happens when a lambda is released on worker thread. Do I understand correctly that the lambda is not frozen? |
Correct. The lambda is released on a worker thread (controlled by the Firebase 3rd party library), and it is not frozen because that also freezes the captured variables (such as the The workaround is to modify the Firebase library to ensure the lambda is released on the main thread. |
There is an alternative workaround which doesn't involve Firebase library modification.
But in this case |
Thank you for this suggestion. Because the Firebase backend removes the "listener" (lambda) asynchronously, I can't actually know when the backend has finished. Therefore when I remove the listener and dispose Is there any way to check inside |
No.
If you dispose |
@SvyatoslavScherbina |
@irgaly Thank you for the feedback 😄 |
We were facing a similar problem with Firebase. class FirebaseFactory: Factory {
let db = Firestore.firestore()
func listener(block: @escaping (Any) -> KotlinUnit) -> Removable {
return FirebaseListener(db: db, success: block)
}
}
class FirebaseListener: Removable {
var listener: ListenerRegistration?
init(db: Firestore, success: @escaping (Any) -> KotlinUnit) {
listener = db.collection("users").addSnapshotListener { snapshot, error in
if let snapshot = snapshot {
_ = success(snapshot.documents)
}
}
}
func remove() {
listener?.remove()
}
} If we remove the listener, the application crashes: We can prevent a crash by releasing completion block (created by K/N) in a few seconds after remove was called. Freezing the completion block doesn't solve this issue. You can reproduce crash using: the sample project |
Yes, I can confirm that freezing doesn't help here. We are considering improving the situation with releasing objects from other threads. |
@SvyatoslavScherbina |
Fix will be shipped with Kotlin 1.3.60. |
I am using Firebase Firestore on iOS from Kotlin native. I pass a Kotlin lambda to -addSnapshotListener:, and the Firestore library asynchronously calls that lambda on the main thread.
This all works fine.
Now, when the "snapshot listener" is removed,
EXC_BAD_ACCESS
error happens. This seems to happen when the lambda is released on a worker thread.This is the call stack:
How can I figure out how to fix this?
The text was updated successfully, but these errors were encountered: