Fixed linearizability of Mutex.unlock
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Mutex.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Mutex.kt
index 01c46f0..70663b9 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Mutex.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Mutex.kt
@@ -158,10 +158,22 @@
     // atomic unlock operation that checks that waiters queue is empty
     private inner class UnlockOp(val queue: LockFreeLinkedListHead) {
         fun helpComplete(): Boolean {
-            val success = queue.isEmpty // Note: queue cannot change anymore (so decision is consistent)
+            /*
+               Note: queue cannot change while this UnlockOp is in progress, so all concurrent attempts to
+               make a decision will reach it consistently. It does not matter what is a proposed
+               decision when this UnlockOp is not longer active, because in this case the following CAS
+               will fail anyway.
+             */
+            val success = queue.isEmpty
             val update: Any = if (success) EmptyUnlocked else queue
             STATE.compareAndSet(this@Mutex, this@UnlockOp, update)
-            return success
+            /*
+                `helpComplete` invocation from the original `unlock` invocation may be coming too late, when
+                some other thread had already helped to complete it (either successfully or not).
+                That operation was unsuccessful if `state` was restored to this `queue` reference and
+                that is what is being checked below.
+             */
+            return state !== queue
         }
     }
 }
\ No newline at end of file