Introduce ThreadLocal.asContextElement()

* Move implementation to internal package
* Add guide section
diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt
index 6307e06..500d3de 100644
--- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt
+++ b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt
@@ -446,6 +446,11 @@
 	public static fun plus (Lkotlinx/coroutines/experimental/ThreadContextElement;Lkotlin/coroutines/experimental/CoroutineContext;)Lkotlin/coroutines/experimental/CoroutineContext;
 }
 
+public final class kotlinx/coroutines/experimental/ThreadContextElementKt {
+	public static final fun asContextElement (Ljava/lang/ThreadLocal;Ljava/lang/Object;)Lkotlinx/coroutines/experimental/ThreadContextElement;
+	public static synthetic fun asContextElement$default (Ljava/lang/ThreadLocal;Ljava/lang/Object;ILjava/lang/Object;)Lkotlinx/coroutines/experimental/ThreadContextElement;
+}
+
 public final class kotlinx/coroutines/experimental/ThreadPoolDispatcher : kotlinx/coroutines/experimental/ExecutorCoroutineDispatcherBase {
 	public fun close ()V
 	public fun getExecutor ()Ljava/util/concurrent/Executor;
diff --git a/core/kotlinx-coroutines-core/src/CoroutineContext.kt b/core/kotlinx-coroutines-core/src/CoroutineContext.kt
index 247ba50..2fcd014 100644
--- a/core/kotlinx-coroutines-core/src/CoroutineContext.kt
+++ b/core/kotlinx-coroutines-core/src/CoroutineContext.kt
@@ -4,7 +4,6 @@
 
 package kotlinx.coroutines.experimental
 
-import java.util.*
 import kotlinx.coroutines.experimental.internal.*
 import kotlinx.coroutines.experimental.scheduling.*
 import java.util.concurrent.atomic.*
diff --git a/core/kotlinx-coroutines-core/src/ThreadContextElement.kt b/core/kotlinx-coroutines-core/src/ThreadContextElement.kt
index 140c9c3..b43497d 100644
--- a/core/kotlinx-coroutines-core/src/ThreadContextElement.kt
+++ b/core/kotlinx-coroutines-core/src/ThreadContextElement.kt
@@ -12,36 +12,42 @@
  * every time the coroutine with this element in the context is resumed on a thread.
  *
  * Implementations of this interface define a type [S] of the thread-local state that they need to store on
- * resume of a coroutine and restore later on suspend and the infrastructure provides the corresponding storage.
+ * resume of a coroutine and restore later on suspend. The infrastructure provides the corresponding storage.
  *
  * Example usage looks like this:
  *
  * ```
- * // declare thread local variable holding MyData
- * private val myThreadLocal = ThreadLocal<MyData?>()
- *
- * // declare context element holding MyData
- * class MyElement(val data: MyData) : ThreadContextElement<MyData?> {
+ * // Appends "name" of a coroutine to a current thread name when coroutine is executed
+ * class CoroutineName(val name: String) : ThreadContextElement<String> {
  *     // declare companion object for a key of this element in coroutine context
- *     companion object Key : CoroutineContext.Key<MyElement>
+ *     companion object Key : CoroutineContext.Key<CoroutineName>
  *
  *     // provide the key of the corresponding context element
- *     override val key: CoroutineContext.Key<MyElement>
+ *     override val key: CoroutineContext.Key<CoroutineName>
  *         get() = Key
  *
  *     // this is invoked before coroutine is resumed on current thread
- *     override fun updateThreadContext(context: CoroutineContext): MyData? {
- *         val oldState = myThreadLocal.get()
- *         myThreadLocal.set(data)
- *         return oldState
+ *     override fun updateThreadContext(context: CoroutineContext): String {
+ *         val previousName = Thread.currentThread().name
+ *         Thread.currentThread().name = "$previousName # $name"
+ *         return previousName
  *     }
  *
  *     // this is invoked after coroutine has suspended on current thread
- *     override fun restoreThreadContext(context: CoroutineContext, oldState: MyData?) {
- *         myThreadLocal.set(oldState)
+ *     override fun restoreThreadContext(context: CoroutineContext, oldState: String) {
+ *         Thread.currentThread().name = oldState
  *     }
  * }
+ *
+ * // Usage
+ * launch(UI + CoroutineName("Progress bar coroutine")) { ... }
  * ```
+ *
+ * Every time this coroutine is resumed on a thread, UI thread name is updated to
+ * "UI thread original name # Progress bar coroutine" and the thread name is restored to the original one when
+ * this coroutine suspends.
+ *
+ * To use [ThreadLocal] variable within the coroutine use [ThreadLocal.asContextElement][asContextElement] function.
  */
 public interface ThreadContextElement<S> : CoroutineContext.Element {
     /**
@@ -67,87 +73,44 @@
     public fun restoreThreadContext(context: CoroutineContext, oldState: S)
 }
 
-private val ZERO = Symbol("ZERO")
-
-// Used when there are >= 2 active elements in the context
-private class ThreadState(val context: CoroutineContext, n: Int) {
-    private var a = arrayOfNulls<Any>(n)
-    private var i = 0
-
-    fun append(value: Any?) { a[i++] = value }
-    fun take() = a[i++]
-    fun start() { i = 0 }
-}
-
-// Counts ThreadContextElements in the context
-// Any? here is Int | ThreadContextElement (when count is one)
-private val countAll =
-    fun (countOrElement: Any?, element: CoroutineContext.Element): Any? {
-        if (element is ThreadContextElement<*>) {
-            val inCount = countOrElement as? Int ?: 1
-            return if (inCount == 0) element else inCount + 1
-        }
-        return countOrElement
-    }
-
-// Find one (first) ThreadContextElement in the context, it is used when we know there is exactly one
-private val findOne =
-    fun (found: ThreadContextElement<*>?, element: CoroutineContext.Element): ThreadContextElement<*>? {
-        if (found != null) return found
-        return element as? ThreadContextElement<*>
-    }
-
-// Updates state for ThreadContextElements in the context using the given ThreadState
-private val updateState =
-    fun (state: ThreadState, element: CoroutineContext.Element): ThreadState {
-        if (element is ThreadContextElement<*>) {
-            state.append(element.updateThreadContext(state.context))
-        }
-        return state
-    }
-
-// Restores state for all ThreadContextElements in the context from the given ThreadState
-private val restoreState =
-    fun (state: ThreadState, element: CoroutineContext.Element): ThreadState {
-        @Suppress("UNCHECKED_CAST")
-        if (element is ThreadContextElement<*>) {
-            (element as ThreadContextElement<Any?>).restoreThreadContext(state.context, state.take())
-        }
-        return state
-    }
-
-internal fun updateThreadContext(context: CoroutineContext): Any? {
-    val count = context.fold(0, countAll)
-    @Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS")
-    return when {
-        count === 0 -> ZERO // very fast path when there are no active ThreadContextElements
-        //    ^^^ identity comparison for speed, we know zero always has the same identity
-        count is Int -> {
-            // slow path for multiple active ThreadContextElements, allocates ThreadState for multiple old values
-            context.fold(ThreadState(context, count), updateState)
-        }
-        else -> {
-            // fast path for one ThreadContextElement (no allocations, no additional context scan)
-            @Suppress("UNCHECKED_CAST")
-            val element = count as ThreadContextElement<Any?>
-            element.updateThreadContext(context)
-        }
-    }
-}
-
-internal fun restoreThreadContext(context: CoroutineContext, oldState: Any?) {
-    when {
-        oldState === ZERO -> return // very fast path when there are no ThreadContextElements
-        oldState is ThreadState -> {
-            // slow path with multiple stored ThreadContextElements
-            oldState.start()
-            context.fold(oldState, restoreState)
-        }
-        else -> {
-            // fast path for one ThreadContextElement, but need to find it
-            @Suppress("UNCHECKED_CAST")
-            val element = context.fold(null, findOne) as ThreadContextElement<Any?>
-            element.restoreThreadContext(context, oldState)
-        }
-    }
-}
+/**
+ * Wraps [ThreadLocal] into [ThreadContextElement]. The resulting [ThreadContextElement]
+ * maintains the given [value] of the given [ThreadLocal] for coroutine regardless of the actual thread its is resumed on.
+ * By default [ThreadLocal.get] is used as a value for the thread-local variable, but it can be overridden with [value] parameter.
+ *
+ * Example usage looks like this:
+ *
+ * ```
+ * val myThreadLocal = ThreadLocal<String?>()
+ * ...
+ * println(myThreadLocal.get()) // Prints "null"
+ * launch(CommonPool + myThreadLocal.asContextElement(initialValue = "foo")) {
+ *   println(myThreadLocal.get()) // Prints "foo"
+ *   withContext(UI) {
+ *     println(myThreadLocal.get()) // Prints "foo", but it's on UI thread
+ *   }
+ * }
+ * println(myThreadLocal.get()) // Prints "null"
+ * ```
+ *
+ * Note that the context element does not track modifications of the thread-local variable, for example:
+ *
+ * ```
+ * myThreadLocal.set("main")
+ * withContext(UI) {
+ *   println(myThreadLocal.get()) // Prints "main"
+ *   myThreadLocal.set("UI")
+ * }
+ * println(myThreadLocal.get()) // Prints "main", not "UI"
+ * ```
+ *
+ * Use `withContext` to update the corresponding thread-local variable to a different value, for example:
+ *
+ * ```
+ * withContext(myThreadLocal.asContextElement("foo")) {
+ *     println(myThreadLocal.get()) // Prints "foo"
+ * }
+ * ```
+ */
+public fun <T> ThreadLocal<T>.asContextElement(value: T = get()): ThreadContextElement<T> =
+    ThreadLocalElement(value, this)
diff --git a/core/kotlinx-coroutines-core/src/internal/ThreadContext.kt b/core/kotlinx-coroutines-core/src/internal/ThreadContext.kt
new file mode 100644
index 0000000..abee55b
--- /dev/null
+++ b/core/kotlinx-coroutines-core/src/internal/ThreadContext.kt
@@ -0,0 +1,122 @@
+package kotlinx.coroutines.experimental.internal
+
+import kotlinx.coroutines.experimental.*
+import kotlin.coroutines.experimental.*
+
+
+private val ZERO = Symbol("ZERO")
+
+// Used when there are >= 2 active elements in the context
+private class ThreadState(val context: CoroutineContext, n: Int) {
+    private var a = arrayOfNulls<Any>(n)
+    private var i = 0
+
+    fun append(value: Any?) { a[i++] = value }
+    fun take() = a[i++]
+    fun start() { i = 0 }
+}
+
+// Counts ThreadContextElements in the context
+// Any? here is Int | ThreadContextElement (when count is one)
+private val countAll =
+    fun (countOrElement: Any?, element: CoroutineContext.Element): Any? {
+        if (element is ThreadContextElement<*>) {
+            val inCount = countOrElement as? Int ?: 1
+            return if (inCount == 0) element else inCount + 1
+        }
+        return countOrElement
+    }
+
+// Find one (first) ThreadContextElement in the context, it is used when we know there is exactly one
+private val findOne =
+    fun (found: ThreadContextElement<*>?, element: CoroutineContext.Element): ThreadContextElement<*>? {
+        if (found != null) return found
+        return element as? ThreadContextElement<*>
+    }
+
+// Updates state for ThreadContextElements in the context using the given ThreadState
+private val updateState =
+    fun (state: ThreadState, element: CoroutineContext.Element): ThreadState {
+        if (element is ThreadContextElement<*>) {
+            state.append(element.updateThreadContext(state.context))
+        }
+        return state
+    }
+
+// Restores state for all ThreadContextElements in the context from the given ThreadState
+private val restoreState =
+    fun (state: ThreadState, element: CoroutineContext.Element): ThreadState {
+        @Suppress("UNCHECKED_CAST")
+        if (element is ThreadContextElement<*>) {
+            (element as ThreadContextElement<Any?>).restoreThreadContext(state.context, state.take())
+        }
+        return state
+    }
+
+internal fun updateThreadContext(context: CoroutineContext): Any? {
+    val count = context.fold(0, countAll)
+    @Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS")
+    return when {
+        count === 0 -> ZERO // very fast path when there are no active ThreadContextElements
+        //    ^^^ identity comparison for speed, we know zero always has the same identity
+        count is Int -> {
+            // slow path for multiple active ThreadContextElements, allocates ThreadState for multiple old values
+            context.fold(ThreadState(context, count), updateState)
+        }
+        else -> {
+            // fast path for one ThreadContextElement (no allocations, no additional context scan)
+            @Suppress("UNCHECKED_CAST")
+            val element = count as ThreadContextElement<Any?>
+            element.updateThreadContext(context)
+        }
+    }
+}
+
+internal fun restoreThreadContext(context: CoroutineContext, oldState: Any?) {
+    when {
+        oldState === ZERO -> return // very fast path when there are no ThreadContextElements
+        oldState is ThreadState -> {
+            // slow path with multiple stored ThreadContextElements
+            oldState.start()
+            context.fold(oldState, restoreState)
+        }
+        else -> {
+            // fast path for one ThreadContextElement, but need to find it
+            @Suppress("UNCHECKED_CAST")
+            val element = context.fold(null, findOne) as ThreadContextElement<Any?>
+            element.restoreThreadContext(context, oldState)
+        }
+    }
+}
+
+// top-level data class for a nicer out-of-the-box toString representation and class name
+private data class ThreadLocalKey(private val threadLocal: ThreadLocal<*>) : CoroutineContext.Key<ThreadLocalElement<*>>
+
+internal class ThreadLocalElement<T>(
+    private val value: T,
+    private val threadLocal: ThreadLocal<T>
+) : ThreadContextElement<T> {
+    override val key: CoroutineContext.Key<*> = ThreadLocalKey(threadLocal)
+
+    override fun updateThreadContext(context: CoroutineContext): T {
+        val oldState = threadLocal.get()
+        threadLocal.set(value)
+        return oldState
+    }
+
+    override fun restoreThreadContext(context: CoroutineContext, oldState: T) {
+        threadLocal.set(oldState)
+    }
+
+    // this method is overridden to perform value comparison (==) on key
+    override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext {
+        return if (this.key == key) EmptyCoroutineContext else this
+    }
+
+    // this method is overridden to perform value comparison (==) on key
+    public override operator fun <E : CoroutineContext.Element> get(key: CoroutineContext.Key<E>): E? =
+        @Suppress("UNCHECKED_CAST")
+        if (this.key == key) this as E else null
+
+    override fun toString(): String = "ThreadLocal(value=$value, threadLocal = $threadLocal)"
+}
diff --git a/core/kotlinx-coroutines-core/test/ThreadContextElementTest.kt b/core/kotlinx-coroutines-core/test/ThreadContextElementTest.kt
index 0c670f2..a8b17d2 100644
--- a/core/kotlinx-coroutines-core/test/ThreadContextElementTest.kt
+++ b/core/kotlinx-coroutines-core/test/ThreadContextElementTest.kt
@@ -52,6 +52,39 @@
         job.join()
         assertNull(myThreadLocal.get())
     }
+
+
+    @Test
+    fun testWithContext() = runTest {
+        expect(1)
+        newSingleThreadContext("withContext").use {
+            val data = MyData()
+            async(CommonPool + MyElement(data)) {
+                assertSame(data, myThreadLocal.get())
+                expect(2)
+
+                val newData = MyData()
+                async(it + MyElement(newData)) {
+                    assertSame(newData, myThreadLocal.get())
+                    expect(3)
+                }.await()
+
+                withContext(it + MyElement(newData)) {
+                    assertSame(newData, myThreadLocal.get())
+                    expect(4)
+                }
+
+                async(it) {
+                    assertNull(myThreadLocal.get())
+                    expect(5)
+                }.await()
+
+                expect(6)
+            }.await()
+        }
+
+        finish(7)
+    }
 }
 
 class MyData
diff --git a/core/kotlinx-coroutines-core/test/ThreadLocalTest.kt b/core/kotlinx-coroutines-core/test/ThreadLocalTest.kt
new file mode 100644
index 0000000..b932e75
--- /dev/null
+++ b/core/kotlinx-coroutines-core/test/ThreadLocalTest.kt
@@ -0,0 +1,199 @@
+
+package kotlinx.coroutines.experimental
+
+import org.junit.*
+import org.junit.Test
+import kotlin.coroutines.experimental.*
+import kotlin.test.*
+
+@Suppress("RedundantAsync")
+class ThreadLocalTest : TestBase() {
+    private val stringThreadLocal = ThreadLocal<String?>()
+    private val intThreadLocal = ThreadLocal<Int?>()
+    private val executor = newFixedThreadPoolContext(1, "threadLocalTest")
+
+    @After
+    fun tearDown() {
+        executor.close()
+    }
+
+    @Test
+    fun testThreadLocal() = runTest {
+        assertNull(stringThreadLocal.get())
+        val deferred = async(CommonPool + stringThreadLocal.asContextElement("value")) {
+            assertEquals("value", stringThreadLocal.get())
+            withContext(executor) {
+                assertEquals("value", stringThreadLocal.get())
+            }
+            assertEquals("value", stringThreadLocal.get())
+        }
+
+        assertNull(stringThreadLocal.get())
+        deferred.await()
+        assertNull(stringThreadLocal.get())
+    }
+
+    @Test
+    fun testThreadLocalInitialValue() = runTest {
+        intThreadLocal.set(42)
+        val deferred = async(CommonPool + intThreadLocal.asContextElement(239)) {
+            assertEquals(239, intThreadLocal.get())
+            withContext(executor) {
+                assertEquals(239, intThreadLocal.get())
+            }
+            assertEquals(239, intThreadLocal.get())
+        }
+
+        deferred.await()
+        assertEquals(42, intThreadLocal.get())
+    }
+
+    @Test
+    fun testMultipleThreadLocals() = runTest {
+        stringThreadLocal.set("test")
+        intThreadLocal.set(314)
+
+        val deferred = async(CommonPool
+                + intThreadLocal.asContextElement(value = 239) + stringThreadLocal.asContextElement(value = "pew")) {
+            assertEquals(239, intThreadLocal.get())
+            assertEquals("pew", stringThreadLocal.get())
+
+            withContext(executor) {
+                assertEquals(239, intThreadLocal.get())
+                assertEquals("pew", stringThreadLocal.get())
+            }
+
+            assertEquals(239, intThreadLocal.get())
+            assertEquals("pew", stringThreadLocal.get())
+        }
+
+        deferred.await()
+        assertEquals(314, intThreadLocal.get())
+        assertEquals("test", stringThreadLocal.get())
+    }
+
+    @Test
+    fun testConflictingThreadLocals() = runTest {
+        intThreadLocal.set(42)
+
+        val deferred = async(CommonPool
+                + intThreadLocal.asContextElement(1)) {
+            assertEquals(1, intThreadLocal.get())
+
+            withContext(executor + intThreadLocal.asContextElement(42)) {
+                assertEquals(42, intThreadLocal.get())
+            }
+
+            assertEquals(1, intThreadLocal.get())
+
+            val deferred = async(coroutineContext + intThreadLocal.asContextElement(53)) {
+                assertEquals(53, intThreadLocal.get())
+            }
+
+            deferred.await()
+            assertEquals(1, intThreadLocal.get())
+
+            val deferred2 = async(executor) {
+                assertNull(intThreadLocal.get())
+            }
+
+            deferred2.await()
+            assertEquals(1, intThreadLocal.get())
+        }
+
+        deferred.await()
+        assertEquals(42, intThreadLocal.get())
+    }
+
+    @Test
+    fun testThreadLocalModification() = runTest {
+        stringThreadLocal.set("main")
+
+        val deferred = async(CommonPool
+                + stringThreadLocal.asContextElement("initial")) {
+            assertEquals("initial", stringThreadLocal.get())
+
+            stringThreadLocal.set("overridden") // <- this value is not reflected in the context, so it's not restored
+
+            withContext(executor + stringThreadLocal.asContextElement("ctx")) {
+                assertEquals("ctx", stringThreadLocal.get())
+            }
+
+            val deferred = async(coroutineContext + stringThreadLocal.asContextElement("async")) {
+                assertEquals("async", stringThreadLocal.get())
+            }
+
+            deferred.await()
+            assertEquals("initial", stringThreadLocal.get()) // <- not restored
+        }
+
+        deferred.await()
+        assertEquals("main", stringThreadLocal.get())
+    }
+
+
+
+    private data class Counter(var cnt: Int)
+    private val myCounterLocal = ThreadLocal<Counter>()
+
+    @Test
+    fun testThreadLocalModificationMutableBox() = runTest {
+        myCounterLocal.set(Counter(42))
+
+        val deferred = async(CommonPool
+                + myCounterLocal.asContextElement(Counter(0))) {
+            assertEquals(0, myCounterLocal.get().cnt)
+
+            // Mutate
+            myCounterLocal.get().cnt = 71
+
+            withContext(executor + myCounterLocal.asContextElement(Counter(-1))) {
+                assertEquals(-1, myCounterLocal.get().cnt)
+                ++myCounterLocal.get().cnt
+            }
+
+            val deferred = async(coroutineContext + myCounterLocal.asContextElement(Counter(31))) {
+                assertEquals(31, myCounterLocal.get().cnt)
+                ++myCounterLocal.get().cnt
+            }
+
+            deferred.await()
+            assertEquals(71, myCounterLocal.get().cnt)
+        }
+
+        deferred.await()
+        assertEquals(42, myCounterLocal.get().cnt)
+    }
+
+    @Test
+    fun testWithContext() = runTest {
+        expect(1)
+        newSingleThreadContext("withContext").use {
+            val data = 42
+            async(CommonPool + intThreadLocal.asContextElement(42)) {
+
+                assertSame(data, intThreadLocal.get())
+                expect(2)
+
+                async(it + intThreadLocal.asContextElement(31)) {
+                    assertEquals(31, intThreadLocal.get())
+                    expect(3)
+                }.await()
+
+                withContext(it + intThreadLocal.asContextElement(2)) {
+                    assertSame(2, intThreadLocal.get())
+                    expect(4)
+                }
+
+                async(it) {
+                    assertNull(intThreadLocal.get())
+                    expect(5)
+                }.await()
+
+                expect(6)
+            }.await()
+        }
+
+        finish(7)
+    }
+}
diff --git a/core/kotlinx-coroutines-core/test/guide/example-context-11.kt b/core/kotlinx-coroutines-core/test/guide/example-context-11.kt
new file mode 100644
index 0000000..4d43911
--- /dev/null
+++ b/core/kotlinx-coroutines-core/test/guide/example-context-11.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
+package kotlinx.coroutines.experimental.guide.context11
+
+import kotlinx.coroutines.experimental.*
+import kotlin.coroutines.experimental.*
+
+val threadLocal = ThreadLocal<String?>() // declare thread-local variable
+
+fun main(args: Array<String>) = runBlocking<Unit> {
+    threadLocal.set("main")
+    println("Pre-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+    val job = launch(CommonPool + threadLocal.asContextElement(value = "launch"), start = CoroutineStart.UNDISPATCHED) {
+        println("Launch start, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+        yield()
+        println("After yield, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+    }
+    job.join()
+    println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+}
diff --git a/core/kotlinx-coroutines-core/test/guide/test/GuideTest.kt b/core/kotlinx-coroutines-core/test/guide/test/GuideTest.kt
index ec527a6..82958a2 100644
--- a/core/kotlinx-coroutines-core/test/guide/test/GuideTest.kt
+++ b/core/kotlinx-coroutines-core/test/guide/test/GuideTest.kt
@@ -268,6 +268,16 @@
     }
 
     @Test
+    fun testKotlinxCoroutinesExperimentalGuideContext11() {
+        test("KotlinxCoroutinesExperimentalGuideContext11") { kotlinx.coroutines.experimental.guide.context11.main(emptyArray()) }.verifyLinesFlexibleThread(
+            "Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'",
+            "Launch start, current thread: Thread[main @coroutine#2,5,main], thread local value: 'launch'",
+            "After yield, current thread: Thread[ForkJoinPool.commonPool-worker-1 @coroutine#2,5,main], thread local value: 'launch'",
+            "Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'"
+        )
+    }
+
+    @Test
     fun testKotlinxCoroutinesExperimentalGuideExceptions01() {
         test("KotlinxCoroutinesExperimentalGuideExceptions01") { kotlinx.coroutines.experimental.guide.exceptions01.main(emptyArray()) }.verifyExceptions(
             "Throwing exception from launch",
diff --git a/coroutines-guide.md b/coroutines-guide.md
index bfdbe8c..c1d8679 100644
--- a/coroutines-guide.md
+++ b/coroutines-guide.md
@@ -67,6 +67,7 @@
   * [Parental responsibilities](#parental-responsibilities)
   * [Naming coroutines for debugging](#naming-coroutines-for-debugging)
   * [Cancellation via explicit job](#cancellation-via-explicit-job)
+  * [Thread-local data](#thread-local-data)
 * [Exception handling](#exception-handling)
   * [Exception propagation](#exception-propagation)
   * [CoroutineExceptionHandler](#coroutineexceptionhandler)
@@ -1266,6 +1267,64 @@
 since it is synchronous, but this joining ability is useful when building backend services to ensure bounded 
 resource usage.
 
+### Thread-local data
+
+Sometimes it is very convenient to have an ability to pass some thread-local data, but, for coroutines, which 
+are not bound to any particular thread, it is hard to achieve it manually without writing a lot of boilerplate.
+
+For [`ThreadLocal`](https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html), 
+[asContextElement] is here for the rescue. It creates an additional context element, 
+which keep the value of the given `ThreadLocal` and restores it every time the coroutine switches its context.
+
+It is easy to demonstrate it in action:
+
+<!--- INCLUDE
+import kotlin.coroutines.experimental.*
+-->
+
+```kotlin
+val threadLocal = ThreadLocal<String?>() // declare thread-local variable
+
+fun main(args: Array<String>) = runBlocking<Unit> {
+    threadLocal.set("main")
+    println("Pre-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+    val job = launch(CommonPool + threadLocal.asContextElement(value = "launch"), start = CoroutineStart.UNDISPATCHED) {
+        println("Launch start, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+        yield()
+        println("After yield, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+    }
+    job.join()
+    println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
+}
+```                                                                                         
+
+> You can get full code [here](core/kotlinx-coroutines-core/test/guide/example-context-11.kt)
+
+The output of this example is:
+
+```text
+Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'
+Launch start, current thread: Thread[main @coroutine#2,5,main], thread local value: 'launch'
+After yield, current thread: Thread[ForkJoinPool.commonPool-worker-1 @coroutine#2,5,main], thread local value: 'launch'
+Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'
+```
+
+<!--- TEST FLEXIBLE_THREAD -->
+
+Note how thread-local value is restored properly, no matter on what thread the coroutine is executed. 
+`ThreadLocal` has first-class support and can be used with any primitive `kotlinx.corotuines` provides.
+It has one key limitation: when thread-local is mutated, a new value is not propagated to the coroutine caller 
+(as context element cannot track all `ThreadLocal` object accesses) and updated value is lost on the next suspension.
+Use [withContext] to update the value of the thread-local in a coroutine, see [asContextElement] for more details. 
+
+Alternatively, a value can be stored in a mutable box like `class Counter(var i: Int)`, which is, in turn, 
+is stored in a thread-local variable. However, in this case you are fully responsible to synchronize 
+potentially concurrent modifications to the variable in this box.
+
+For advanced usage, for example for integration with logging MDC, transactional contexts or any other libraries
+which internally use thread-locals for passing data, see documentation for [ThreadContextElement] interface 
+that should be implemented. 
+
 ## Exception handling
 
 <!--- INCLUDE .*/example-exceptions-([0-9]+).kt
@@ -2776,6 +2835,8 @@
 [newCoroutineContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/new-coroutine-context.html
 [CoroutineName]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-name/index.html
 [Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job.html
+[asContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/java.lang.-thread-local/as-context-element.html
+[ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-thread-context-element/index.html
 [CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-exception-handler/index.html
 [kotlin.coroutines.experimental.CoroutineContext.cancelChildren]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/kotlin.coroutines.experimental.-coroutine-context/cancel-children.html
 [CompletableDeferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-completable-deferred/index.html