Mark
02/26/2024, 5:33 AMjdk.internal.misc.Unsafe.getReferenceVolatile
when calling trySend on a BufferedChannel (actually I’m using SingleShotEventBus). Is this something that can be mitigated?Mark
02/26/2024, 5:34 AMmain (runnable):tid=1 systid=22340
#00 pc 0x4bf790 libart.so (art::DumpNativeStack + 108) (BuildId: 02bec5940be704b863f6514fc7d81c41)
#01 pc 0x4b2940 libart.so (art::Thread::DumpStack const + 388) (BuildId: 02bec5940be704b863f6514fc7d81c41)
#02 pc 0x4b2280 libart.so (art::DumpCheckpoint::Run + 164) (BuildId: 02bec5940be704b863f6514fc7d81c41)
#03 pc 0x3c8c9c libart.so (art::Thread::RunCheckpointFunction + 148) (BuildId: 02bec5940be704b863f6514fc7d81c41)
#04 pc 0x5afae0 libart.so (artTestSuspendFromCode + 232) (BuildId: 02bec5940be704b863f6514fc7d81c41)
#05 pc 0x35501c libart.so (art_quick_test_suspend + 156) (BuildId: 02bec5940be704b863f6514fc7d81c41)
at jdk.internal.misc.Unsafe.getReferenceVolatile(Native method)
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.get(AtomicReferenceFieldUpdater.java:480)
at kotlinx.coroutines.JobSupport.getState$kotlinx_coroutines_core(JobSupport.kt:164)
at kotlinx.coroutines.JobSupport.isActive(JobSupport.kt)
at kotlinx.coroutines.AbstractCoroutine.isActive(AbstractCoroutine.kt)
at kotlinx.coroutines.channels.ProducerCoroutine.isActive(Produce.kt)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:96)
at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.java:65)
at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:241)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:159)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:470)
at kotlinx.coroutines.CancellableContinuationImpl.completeResume(CancellableContinuationImpl.kt:586)
at kotlinx.coroutines.channels.BufferedChannelKt.tryResume0(BufferedChannel.kt:2904)
at kotlinx.coroutines.channels.BufferedChannelKt.access$tryResume0(BufferedChannel.kt:1)
at kotlinx.coroutines.channels.BufferedChannel$BufferedChannelIterator.tryResumeHasNext(BufferedChannel.kt:1694)
at kotlinx.coroutines.channels.BufferedChannel.tryResumeReceiver(BufferedChannel.kt:642)
at kotlinx.coroutines.channels.BufferedChannel.updateCellSend(BufferedChannel.kt:458)
at kotlinx.coroutines.channels.BufferedChannel.access$updateCellSend(BufferedChannel.kt:36)
at kotlinx.coroutines.channels.BufferedChannel.trySend-JP2dKIU(BufferedChannel.kt:3307)louiscad
02/26/2024, 10:07 AMCancellationException without rethrowing it.louiscad
02/26/2024, 10:08 AMMark
02/26/2024, 10:09 AMMark
02/26/2024, 10:13 AMcatch(Exception) I need to separate out the CancelationException and rethrow, or only under certain conditions?louiscad
02/26/2024, 10:13 AMlouiscad
02/26/2024, 10:14 AMMark
02/26/2024, 10:18 AMCancelationException. Am I missing something?louiscad
02/26/2024, 10:18 AMlouiscad
02/26/2024, 10:19 AMlouiscad
02/26/2024, 10:19 AMMark
02/26/2024, 10:19 AMlouiscad
02/26/2024, 10:20 AMMark
02/26/2024, 10:29 AMlouiscad
02/26/2024, 10:31 AMMark
02/26/2024, 12:49 PMCancelationException thrown from a ContentProvider implementation to its client? At the moment, I rethrow it as an OperationCanceledException which I guess could be a problem.louiscad
02/26/2024, 12:51 PMMark
02/26/2024, 1:05 PMlouiscad
02/26/2024, 1:20 PMMark
02/26/2024, 1:27 PMMark
02/26/2024, 1:28 PMlouiscad
02/26/2024, 1:29 PMlouiscad
02/26/2024, 1:30 PMMark
02/26/2024, 1:32 PMException and Errorlouiscad
02/26/2024, 1:32 PMlouiscad
02/26/2024, 1:33 PMMark
02/26/2024, 1:33 PMlouiscad
02/26/2024, 1:34 PMSam
02/26/2024, 1:34 PMlouiscad
02/26/2024, 1:34 PMVsevolod Tolstopyatov [JB]
02/26/2024, 1:37 PMCancellationException (NB: it's not like "there is no issues" but rather "We haven't seen much internally and we don't have enough external reports").
If you could report us things like "hey, here is my code that was really hard to troubleshoot. It's because I've caught CE and the child component got stuck", I would really appreciate it 😊louiscad
02/26/2024, 1:40 PMVsevolod Tolstopyatov [JB]
02/26/2024, 1:42 PMMark
02/26/2024, 1:42 PMVsevolod Tolstopyatov [JB]
02/26/2024, 1:45 PMwhile (job.isActive) doWorkUnconditionally , but other from that it's hard to pinpoint the potential problemlouiscad
02/26/2024, 1:48 PMWhat do you believe is the worst pitfall of the ones remaining in Modern Android Development?
I would reply: "this issue with unintended breakage of cancellation and structured concurrency", because: • It's very sneaky • Hard to debug • Affects the whole app • You expect kotlinx.coroutines to be all about safety • It changes the way one needs to think about exceptions, without that being properly documented (and who reads doc? Well I do, but you know…)
louiscad
02/26/2024, 1:50 PMlouiscad
02/26/2024, 2:03 PMlouiscad
02/26/2024, 2:04 PMlouiscad
02/26/2024, 2:04 PMVsevolod Tolstopyatov [JB]
02/26/2024, 2:04 PMlouiscad
02/26/2024, 4:55 PMkotlinx.coroutines there…
Could this be related to that issue? blob thinking fastlouiscad
02/26/2024, 4:56 PMkevin.cianfarini
02/27/2024, 11:25 PMException without rethrowing CE is dangerous.
We've had many, many issues regarding this. It's really hard to enforce on a team that isn't solely composed of senior engineerskevin.cianfarini
02/27/2024, 11:26 PMkevin.cianfarini
02/27/2024, 11:28 PMlouiscad
02/28/2024, 8:03 AM@Deprecated
expect class CancellationException : Exception
@Deprecated
actual typealias CancellationException
class CancellationSignal : Throwable()louiscad
02/28/2024, 8:06 AM@Deprecated
fun withTimeout(…)
@Deprecated
class TimeoutCancellationException
🫡kevin.cianfarini
02/28/2024, 8:32 PMcatch (e: Exception).
try {
yield()
checkNotNull(someField) { "It was null! Scary!" }
} catch (e: IllegalStateException) {
logger.log(e)
// BUG, we didn't rethrow CancellationException
}kevin.cianfarini
02/28/2024, 8:34 PMkevin.cianfarini
02/28/2024, 8:39 PM// Coroutines < 2.0.0
@Deprecated("Use CancellationSignal")
open class CancellationException : IllegalStateException
class CancellationSignal : CancellationException()
// Coroutines >= 2.0.0
class CancellationSignal : Throwable()louiscad
02/28/2024, 8:44 PMdelay(something) or awaitWhathever() ?kevin.cianfarini
02/28/2024, 8:45 PMMark
02/29/2024, 5:31 AMCancellationException curiosity because sometimes you want to catch everything in order to do some tidy-up (that is only required when something goes wrong) for example deleting a file that has been partially written to. So I’m not sure how much moving the Exception in the class hierarchy will help. I suppose if there was some kind of finallyForThrowable block (or if the finally block was passed a nullable Throwable), then that would work.louiscad
02/29/2024, 10:34 AMNullPointerException being thrown instead, which is ambiguous, so not great, best to just have a custom Throwable type IMOMark
02/29/2024, 1:35 PMfinallyForThrowable bit). Alternatively, the finally block could receive a Throwable? arg to indicate what was thrown (in the case of non-null), or null for when the try finished normally. Like with finally normally, you wouldn’t be throwing anything from there. It’s messy though because is the throwable the one thrown from the try or one potentially thrown from a catch?kevin.cianfarini
02/29/2024, 7:08 PMcatch everything in order to do some tidy-up (that is only required when something goes wrong) for example deleting a file that has been partially written to.In a scenario like this you should probably be rethrowing the exception, regardless of its type.
try {
aThing()
} catch (e: Throwable) {
doPartialTidyUp()
throw e
}Mark
03/01/2024, 2:42 AMlouiscad
03/01/2024, 10:25 AMuse { } extensions do all I need. Then I need to catch `Exception`s general or specific. I can't think of any case where I would want to catch all Error subclasses or a random Throwable subclass that is not an Exception.
catch (e: Throwable) is not something I'd want to see in my codebases, because it's almost certain it's not handling errors properly.Mark
03/02/2024, 12:16 PMkevin.cianfarini
03/02/2024, 1:32 PMMark
03/02/2024, 2:40 PMThrowable is exactly what Louis said he doesn’t want.louiscad
03/02/2024, 4:07 PMMark
03/03/2024, 3:32 AMfinally I was suggesting. You don’t need to think about rethrowing. It’s done for you.kevin.cianfarini
03/03/2024, 7:21 PMuse.Mark
03/04/2024, 2:45 AMuse in this case? You still need to either use a try/catch and rethrow or use a messy boolean to indicate whether the block finished normally or not. I think the cleanest solution would be for the finally block to be passed a boolean to indicate whether the try completed normally or not.louiscad
03/04/2024, 9:42 AMthrow t). If you find a proper name for a function taking 2 lambdas, running the second one on Throwable before rethrowing, or a name for a Result<T> extension that calls a block with the Throwable then rethrows (to be used after runCatching, then go for it!Mark
03/04/2024, 9:53 AMfinally is not necessary. It can easily be achieved by calling a local tidyup function and rethrowing as necessary.louiscad
03/04/2024, 9:55 AMtry {
doPossiblyThrowingStuff()
} intercept (t: Throwable) {
cleanup() // Won't suffice in case of process death!
}louiscad
03/04/2024, 9:56 AMrunCatching {
doPossiblyThrowingStuff()
}.intercept<Throwable> {
cleanup() // Won't suffice in case of process death!
}louiscad
03/04/2024, 9:57 AMintercept rethrows after running its blocklouiscad
03/04/2024, 9:57 AMT (from Result<T>)Mark
03/04/2024, 9:59 AMlouiscad
03/04/2024, 9:59 AMMark
03/04/2024, 10:09 AMfun <T> Result<T>.interceptFailure(failureBlock: () -> Unit): T {
if (this.isFailure) {
failureBlock()
throw exceptionOrNull()!!
}
return getOrThrow()
}louiscad
03/04/2024, 10:21 AMkevin.cianfarini
03/19/2024, 1:12 PMIllegalStateException but did not rethrow CancellationException. The existence of utilities like checkNotNull in Kotlin popularized the use of general exceptions like ISE, and that directly conflicts with how cancellation should propagate through a system. Perhaps we should raise this as an issue on the repo?kevin.cianfarini
03/19/2024, 2:09 PM