Fixed IllegalStateException in select that concurrently selects, which
was due a problem with Job concurrent cancel and dispose of handle.
The bug was introduced by addition of Cancelling state for a Job.
Original stack trace in select was:
java.lang.IllegalStateException: Check failed.
at kotlinx.coroutines.experimental.internal.LockFreeLinkedListNode.remove(LockFreeLinkedList.kt:236)
at kotlinx.coroutines.experimental.JobSupport.removeNode$kotlinx_coroutines_core(Job.kt:734)
at kotlinx.coroutines.experimental.JobNode.dispose(Job.kt:995)
at kotlinx.coroutines.experimental.selects.SelectBuilderImpl.initCancellability(Select.kt:294)
at kotlinx.coroutines.experimental.selects.SelectBuilderImpl.getResult(Select.kt:273)
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt
index 97daf79..ff93585 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt
@@ -227,13 +227,14 @@
// ------ removeXXX ------
/**
- * Removes this node from the list. Returns `true` when removed successfully.
+ * Removes this node from the list. Returns `true` when removed successfully, or `false` if the node was already
+ * removed or if it was not added to any list in the first place.
*/
public open fun remove(): Boolean {
while (true) { // lock-free loop on next
val next = this.next
if (next is Removed) return false // was already removed -- don't try to help (original thread will take care)
- check(next !== this) // sanity check -- can be true for sentinel nodes only, but they are never removed
+ if (next === this) return false // was not even added
val removed = (next as Node).removed()
if (NEXT.compareAndSet(this, next, removed)) {
// was removed successfully (linearized remove) -- fixup the list