-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Workaround for KT-58685 #3881
Workaround for KT-58685 #3881
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,12 +122,18 @@ public suspend inline fun <T> Mutex.withLock(owner: Any? = null, action: () -> T | |
callsInPlace(action, InvocationKind.EXACTLY_ONCE) | ||
} | ||
|
||
// Cannot use 'finally' in this function because of KT-58685 | ||
// See kotlinx.coroutines.sync.MutexTest.testWithLockJsMiscompilation | ||
|
||
lock(owner) | ||
try { | ||
return action() | ||
} finally { | ||
val result = try { | ||
action() | ||
} catch (e: Throwable) { | ||
unlock(owner) | ||
throw e | ||
} | ||
unlock(owner) | ||
return result | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sure you'll agree that this is an unnatural way to write this. You shouldn't trust me to remember not to "fix" the style of code accidentally unless you drop a comment!
This comment was marked as outdated.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment mentioning the issue and linking to the test case. |
||
} | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -148,4 +148,20 @@ class MutexTest : TestBase() { | |
assertFailsWith<IllegalStateException> { mutex.lock(owner) } | ||
assertFailsWith<IllegalStateException> { select { mutex.onLock(owner) {} } } | ||
} | ||
|
||
@Test | ||
fun testWithLockJsMiscompilation() = runTest { | ||
// This is a reproducer for KT-58685 | ||
// On Kotlin/JS IR, the compiler miscompiles calls to 'unlock' in an inlined finally | ||
// This is visible on the withLock function | ||
// Until the compiler bug is fixed, this test case checks that we do not suffer from it | ||
val mutex = Mutex() | ||
assertFailsWith<IndexOutOfBoundsException> { | ||
try { | ||
mutex.withLock { null } ?: throw IndexOutOfBoundsException() // should throw… | ||
} catch (e: Exception) { | ||
throw e // …but instead fails here | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need a complementary test, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we add this, won't it means that whenever it is fixed on the compiler side, it will break CI until someone rolls back this PR? I'll try to do this using the reproducer from the issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, thinking back on this, I'm not sure it's so easy. This function is inline, the bug happens at the call site. Therefore, even if the KotlinX.Coroutines team uses a compiler that doesn't have the issue anymore, a user of the library could still be using an older compiler that has the issue, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the point about CI makes sense, given that the issue is already fixed on the compiler's side, so we don't need a notification. Regarding the old compiler + new library version: we don't provide guarantees as to what happens when using a library compiled with a newer compiler than your code uses. Generally, it's error-prone. So, let's not add the tests. I wrote one just to make sure the tests that you added do correctly show that there's no problem, and sure enough, your tests do invoke the miscompilation if we keep the old form of class SemaphoreTest: TestBase() {
@Test
fun testWithPermitJsMiscompilation() = runTest {
// This is a reproducer for KT-58685
// On Kotlin/JS IR, the compiler miscompiles calls to 'release' in an inlined finally
// This is visible on the withPermit function
// Until the compiler bug is fixed, this test case checks that we do suffer from it
val semaphore = Semaphore(1)
assertFailsWith<IllegalStateException> {
try {
semaphore.withPermitMiscompiled { null } ?: throw IndexOutOfBoundsException() // should throw…
} catch (e: Exception) {
throw e // …but instead fails here
}
}
}
}
suspend inline fun <T> Semaphore.withPermitMiscompiled(action: () -> T): T {
acquire()
try {
return action()
} finally {
release()
}
} |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To recreate the behavior of
finally
, ifunlock
insidecatch
throws an exception, this exception must adde
as a suppressed one. Could you also write the corresponding test?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, sorry for the time it took me to get back to this.
I'm not sure exactly what you mean here, something like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry, I was mistaken. There's no such behavior currently, and on second thought, I don't think there should be.