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 Error
louiscad
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