-
Notifications
You must be signed in to change notification settings - Fork 1.9k
SIGSEGV on Android 7 #1683
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I boiled it further down by removing the dependency on coroutines-android and using the GlobalScope: class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch {
while (true) {
delay(100)
combine(flowOf(Unit), flowOf(Unit)) { _ -> Unit }.collect()
}
}
}
}
|
Most probably an R8 issue generating code that those device improperly handle as a few other cases. You should report to https://issuetracker.google.com/issues?q=componentid:326788%20status:open they are quite reactive and if the user base is high enough they might add a workaround. |
Thanks for the detailed write-up! It could be because of the fact that for crossinlined coroutines (and flow has a lot of crossinline) pretty unusual bytecode (-> dex) shape is produced and some of the manufacturers could have missed it in their test suites. So as a (really limited) workaround, you can try to minimize the number of invoked |
My current workaround is to create my own combine functions which are based on rxjava: /**
* Because of a bug in android 7 we can't use the regular combine functions and fallback to RxJava
* @see [https://github.com/Kotlin/kotlinx.coroutines/issues/1683]
*/
@PublishedApi
internal class Wrapper<T>(val value: T)
@PublishedApi
internal inline fun <T, R> combine(vararg sources: Flow<T>, crossinline transform: suspend (List<T>) -> R): Flow<R> {
val flowables = sources.map { it.map { value -> Wrapper(value) }.asFlowable() }
return Flowable
.combineLatest(flowables) {
@Suppress("UNCHECKED_CAST")
it.toList() as List<Wrapper<T>>
}
.asFlow()
.map { wrappedValues ->
val unwrapped = wrappedValues.map { it.value }
transform(unwrapped)
}
}
inline fun <T1, T2, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
crossinline transform: suspend (T1, T2) -> R
): Flow<R> = combine(flow, flow2) { args ->
@Suppress("UNCHECKED_CAST")
transform(
args[0] as T1,
args[1] as T2
)
}
... With that in place all native crashes are gone. |
Hi, we have exactly the same problem on a lot of Devices (processor Mediatek, Android 7) with a big combine function in our viewmodel ( 6 Flows combined (one combine with 4 Flows and one Flow which is a combine of 2 Flows) ). I can reproduce this bug on a Doogee Mix Lite. What is your advice please ? Use other operator ? Add a debounce, buffer ? I don't want to use Rx 😅 thanks |
@PierreFourreau try to extract |
Closing, please keep track on the https://issuetracker.google.com/issues/145569946 (thanks Paul for filing this!) |
I have written a non-rx version: /**
* Because of a bug in android 7 we can't use the regular combine functions and have to use our own.
* @see [https://github.com/Kotlin/kotlinx.coroutines/issues/1683]
*/
@PublishedApi
internal object UnInitialized
@PublishedApi
internal inline fun <T, R> combine(vararg sources: Flow<T>, crossinline transform: suspend (List<T>) -> R): Flow<R> {
return channelFlow {
val values = Array<Any?>(sources.size) { UnInitialized }
coroutineScope {
sources.forEachIndexed { index, flow ->
launch {
flow.collect { value ->
values[index] = value
if (values.all { it !== UnInitialized }) {
@Suppress("UNCHECKED_CAST")
send(transform(values.toList() as List<T>))
}
}
}
}
}
}
} |
Is your crash reproducible on every launch, or just the first one? We're facing a similar SIGSEG in libart.so, but on the first launch after update. Wondering if it's also got to do with R8. |
On every launch. |
@PaulWoitaschek in your minimized repro, I see that |
I guess with my suggestion, the assumption is that your use case only requires combining two |
@qwwdfsad, let's say I have a class that returns a class ObserveFoo(val dep1: Dep1, val dep2: Dep2, …, val depN: DepN) {
operator fun invoke(): Flow<Foo> {
return combine( // Real combine function from kotlinx.coroutines.flow.combine
dep1.observe(),
dep2.observe(),
…
depN.observe()
)
}
} Would this still run the risk of producing native crashes on some Android devices based on what we currently know? I am currently migrating some existing RxJava code over to Coroutines/Flow but I want to make sure I do not introduce native crashes as a result. Thank you! |
@mhernand40 Unfortunately, we do not know. The stable reproducer is required in order to check such a hypothesis |
* Rework Flow.zip operator: improve its performance by 40%, collect one of the upstreams in the same coroutine as emitter * Rework Flow.combine * Get rid of two code paths * Get rid of accidental O(N^2) where N is the number of flows that caused #2296 * Get rid of select that hits performance hard, improving performance by 50% in the pessimistic case * Get rid of crossinlines in API and implementation to fix Android issues * Make combine fairer and its results less surprising in sequential scenarios * Improve stacktrace recovery and stackwalking for SafeCollector, flowOn and zip operators * Update JMH Fixes #1743 Fixes #1683 Fixes #2296
The |
Issue
After updating my application I faced a lot of crashes on android 7 devices. I couldn't reproduce it on my nexus or on the emulator so I bought one of the crashing devices.
After spending half of the day I finally managed to find the root cause and I can reproduce it in a hello world project.
Sample
Crash
This leads to a sigsegv
I currently see about 1200 affected users..
Versions
Affected Devices
Note
Take a look at the last crash, maybe that's an indicator for what's going wrong. That one is no native crash but an android 7 only issue.
The text was updated successfully, but these errors were encountered: