Check cancellation when starting unconfined coroutine

Fixes #621
diff --git a/common/kotlinx-coroutines-core-common/src/Dispatched.kt b/common/kotlinx-coroutines-core-common/src/Dispatched.kt
index ccb8d84..83eed65 100644
--- a/common/kotlinx-coroutines-core-common/src/Dispatched.kt
+++ b/common/kotlinx-coroutines-core-common/src/Dispatched.kt
@@ -43,8 +43,9 @@
             _state = CompletedExceptionally(exception)
             resumeMode = MODE_ATOMIC_DEFAULT
             dispatcher.dispatch(context, this)
-        } else
+        } else {
             resumeUndispatchedWithException(exception)
+        }
     }
 
     @Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stack
@@ -54,8 +55,11 @@
             _state = value
             resumeMode = MODE_CANCELLABLE
             dispatcher.dispatch(context, this)
-        } else
-            resumeUndispatched(value)
+        } else {
+            if (!resumeCancelled()) {
+                resumeUndispatched(value)
+            }
+        }
     }
 
     @Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stack
@@ -65,8 +69,22 @@
             _state = CompletedExceptionally(exception)
             resumeMode = MODE_CANCELLABLE
             dispatcher.dispatch(context, this)
-        } else
-            resumeUndispatchedWithException(exception)
+        } else {
+            if (!resumeCancelled()) {
+                resumeUndispatchedWithException(exception)
+            }
+        }
+    }
+
+    @Suppress("NOTHING_TO_INLINE")
+    inline fun resumeCancelled(): Boolean {
+        val job = context[Job]
+        if (job != null && !job.isActive) {
+            resumeWithException(job.getCancellationException())
+            return true
+        }
+
+        return false
     }
 
     @Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stack
diff --git a/common/kotlinx-coroutines-core-common/test/ExperimentalDispatchModeTest.kt b/common/kotlinx-coroutines-core-common/test/ExperimentalDispatchModeTest.kt
new file mode 100644
index 0000000..fc93628
--- /dev/null
+++ b/common/kotlinx-coroutines-core-common/test/ExperimentalDispatchModeTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.coroutines.experimental
+
+import kotlin.test.*
+
+class ExperimentalDispatchModeTest : TestBase() {
+    @Test
+    fun testUnconfinedCancellation() = runTest {
+        val parent = Job()
+        launch(parent) {
+            expect(1)
+            parent.cancel()
+            launch(Dispatchers.Unconfined) {
+                expectUnreached()
+            }
+
+        }.join()
+        finish(2)
+    }
+
+    @Test
+    fun testUnconfinedCancellationState() = runTest {
+        val parent = Job()
+        launch(parent) {
+            expect(1)
+            parent.cancel()
+            val job = launch(Dispatchers.Unconfined) {
+                expectUnreached()
+            }
+
+            assertTrue(job.isCancelled)
+            assertTrue(job.isCompleted)
+            assertFalse(job.isActive)
+        }.join()
+        finish(2)
+    }
+
+    @Test
+    fun testUnconfinedCancellationLazy() = runTest {
+        val parent = Job()
+        launch(parent) {
+            expect(1)
+            val job = launch(Dispatchers.Unconfined, start = CoroutineStart.LAZY) {
+                expectUnreached()
+            }
+            job.invokeOnCompletion { expect(2) }
+            assertFalse(job.isCompleted)
+
+            parent.cancel()
+            job.join()
+        }.join()
+        finish(3)
+    }
+
+    @Test
+    fun testUndispatchedCancellation() = runTest {
+        val parent = Job()
+        launch(parent) {
+            expect(1)
+            parent.cancel()
+            launch(start = CoroutineStart.UNDISPATCHED) {
+                expect(2)
+                yield()
+                expectUnreached()
+            }
+
+        }.join()
+        finish(3)
+    }
+
+    @Test
+    fun testCancelledAtomicUnconfined() = runTest {
+        val parent = Job()
+        launch(parent) {
+            expect(1)
+            parent.cancel()
+            launch(Dispatchers.Unconfined, start = CoroutineStart.ATOMIC) {
+                expect(2)
+                yield()
+                expectUnreached()
+            }
+        }.join()
+        finish(3)
+    }
+
+
+    @Test
+    fun testCancelledWithContextUnconfined() = runTest {
+        val parent = Job()
+        launch(parent) {
+            expect(1)
+            parent.cancel()
+            withContext(Dispatchers.Unconfined) {
+                expectUnreached()
+            }
+        }.join()
+        finish(2)
+    }
+}
\ No newline at end of file