From e670f621139c837cae9d0351bc4f55834c6df735 Mon Sep 17 00:00:00 2001 From: Alex Riedler Date: Wed, 4 Dec 2024 13:05:02 -0500 Subject: [PATCH] Workaround transient issue in JobCancellationException (#4291) Co-authored-by: Vsevolod Tolstopyatov --- kotlinx-coroutines-core/jvm/src/Exceptions.kt | 8 ++++-- .../JobCancellationExceptionSerializerTest.kt | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/kotlinx-coroutines-core/jvm/src/Exceptions.kt b/kotlinx-coroutines-core/jvm/src/Exceptions.kt index 6175595713..a7519862e0 100644 --- a/kotlinx-coroutines-core/jvm/src/Exceptions.kt +++ b/kotlinx-coroutines-core/jvm/src/Exceptions.kt @@ -61,6 +61,10 @@ internal actual class JobCancellationException public actual constructor( override fun equals(other: Any?): Boolean = other === this || other is JobCancellationException && other.message == message && other.job == job && other.cause == cause - override fun hashCode(): Int = - (message!!.hashCode() * 31 + job.hashCode()) * 31 + (cause?.hashCode() ?: 0) + + override fun hashCode(): Int { + // since job is transient it is indeed nullable after deserialization + @Suppress("UNNECESSARY_SAFE_CALL") + return (message!!.hashCode() * 31 + (job?.hashCode() ?: 0)) * 31 + (cause?.hashCode() ?: 0) + } } diff --git a/kotlinx-coroutines-core/jvm/test/JobCancellationExceptionSerializerTest.kt b/kotlinx-coroutines-core/jvm/test/JobCancellationExceptionSerializerTest.kt index c063e9457e..18c3d29db5 100644 --- a/kotlinx-coroutines-core/jvm/test/JobCancellationExceptionSerializerTest.kt +++ b/kotlinx-coroutines-core/jvm/test/JobCancellationExceptionSerializerTest.kt @@ -36,4 +36,30 @@ class JobCancellationExceptionSerializerTest : TestBase() { finish(4) } } + + @Test + fun testHashCodeAfterDeserialization() = runTest { + try { + coroutineScope { + expect(1) + throw JobCancellationException( + message = "Job Cancelled", + job = Job(), + cause = null, + ) + } + } catch (e: Throwable) { + finish(2) + val outputStream = ByteArrayOutputStream() + ObjectOutputStream(outputStream).use { + it.writeObject(e) + } + val deserializedException = + ObjectInputStream(outputStream.toByteArray().inputStream()).use { + it.readObject() as JobCancellationException + } + // verify hashCode does not fail even though Job is transient + assert(deserializedException.hashCode() != 0) + } + } }