From 2f953dc35b96afcdec772e4d09de25c49b4ea1f3 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 01:46:34 +0800 Subject: [PATCH 01/15] add CoroutineLocal --- .../cn/tursom/core/datastruct/ArrayMap.kt | 505 +++++++++--------- .../core/datastruct/ComparableArrayMap.kt | 27 + .../core/datastruct/ParallelArrayMap.kt | 203 +++++++ .../core/stream/impl/JavaInputStreamProxy.kt | 15 + .../tursom/utils/coroutine/CoroutineLocal.kt | 39 ++ .../utils/coroutine/CoroutineLocalContext.kt | 43 ++ .../utils/coroutine/CoroutineLocalTest.kt | 32 ++ 7 files changed, 618 insertions(+), 246 deletions(-) create mode 100644 src/main/kotlin/cn/tursom/core/datastruct/ComparableArrayMap.kt create mode 100644 src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt create mode 100644 src/main/kotlin/cn/tursom/core/stream/impl/JavaInputStreamProxy.kt create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt create mode 100644 utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt diff --git a/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt b/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt index 34cb6d6..d7dd2f7 100644 --- a/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt +++ b/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt @@ -1,271 +1,284 @@ package cn.tursom.core.datastruct +import cn.tursom.core.cast + @Suppress("MemberVisibilityCanBePrivate") -class ArrayMap, V>(initialCapacity: Int = 16) : SimpMap { - @Volatile - private var arr: Array?> = Array(initialCapacity) { null } - @Volatile - private var end = 0 +open class ArrayMap(initialCapacity: Int = 16) : SimpMap { + @Volatile + protected var arr: Array?> = Array(initialCapacity) { null } - override val size: Int get() = end - override val entries: MutableSet> = EntrySet(this) - override val keys: MutableSet = KeySet(this) - override val values: MutableCollection = ValueCollection(this) + @Volatile + protected var end = 0 - /** - * @param key 查找的键 - * @return 键所在的下标 - * @return < 0 如果键不存在, -${return} - 1 为如果插入应插入的下标 - */ - fun search(key: K): Int { - if (end == 0) return -1 - return arr.binarySearch(key, 0, end) + override val size: Int get() = end + override val entries: MutableSet> = EntrySet(this) + override val keys: MutableSet = KeySet(this) + override val values: MutableCollection = ValueCollection(this) + + /** + * @param key 查找的键 + * @return 键所在的下标 + * @return < 0 如果键不存在, -${return} - 1 为如果插入应插入的下标 + */ + open fun search(key: K): Int { + if (end == 0) return -1 + for (index in 0 until end) { + if ((arr[index] ?: continue).key == key) { + return index + } + } + return -1 + } + + infix fun getFromIndex(index: Int): V? { + return if (index < 0) null + else arr[index]?.value + } + + override fun first(): V? = getFromIndex(0) + + override fun clear() { + end = 0 + } + + override operator fun set(key: K, value: V) { + setAndGet(key, value) + } + + override fun setAndGet(key: K, value: V): V? { + @Suppress("SENSELESS_COMPARISON") + if (key == null) return null + // 首先查找得到目标所在的下标 + val index = search(key) + var prev: V? = null + if (index < 0) { + // 下标小于零表示不存在,直接插入数据 + insert(key, value, -index - 1) + } else { + val node = arr[index] + if (node != null) { + prev = node.value + node.value = value + } else arr[index] = Node(key, value) + } + return prev + } + + override infix fun putAll(from: Map) { + from.forEach { (key, value) -> + val index = search(key) + if (index < 0) arr[end++] = Node(key, value) + else { + val node = arr[index] + if (node != null) node.value = value + else arr[index] = Node(key, value) + } + } + } + + override infix fun delete(key: K): V? { + val index = search(key) + return delete(index) + } + + override infix fun containsKey(key: K): Boolean = search(key) >= 0 + + override infix fun containsValue(value: V): Boolean { + arr.forEach { + if (it?.value == value) return true + } + return false + } + + override infix operator fun get(key: K): V? = getNode(key)?.value + override fun isEmpty(): Boolean = end == 0 + + fun copy(): ArrayMap { + val clone = ArrayMap(0) + clone.arr = Array(end) { arr[it]?.let { Node(it.key, it.value) } } + clone.end = end + return clone + } + + override fun toString(): String { + if (end == 0) return "{}" + val sb = StringBuilder("{${arr[0]}") + for (i in 1 until end) { + sb.append(", ") + sb.append(arr[i]) + } + sb.append("}") + return sb.toString() + } + + private fun getNode(key: K): Node? { + @Suppress("SENSELESS_COMPARISON") + if (key == null) return null + val index = search(key) + return if (index < 0) null + else arr[index] + } + + private fun resize(newSize: Int = if (arr.isNotEmpty()) arr.size * 2 else 1) { + arr = arr.copyOf(newSize) + } + + private fun insert(key: K, value: V, index: Int): V? { + if (end == arr.size) resize() + System.arraycopy(arr, index, arr, index + 1, end - index) + arr[index] = Node(key, value) + end++ + return value + } + + private infix fun delete(index: Int): V? { + if (index in 0 until end) { + val oldNode = arr[index] + System.arraycopy(arr, index + 1, arr, index, end - index - 1) + end-- + return oldNode?.value + } + return null + } + + class Node( + override val key: K, + @Volatile override var value: V + ) : MutableMap.MutableEntry, Comparable { + override fun toString(): String = "$key=$value" + override fun setValue(newValue: V): V = value.also { value = newValue } + override fun compareTo(other: K): Int { + return if (key is Comparable<*>) { + key.cast>().compareTo(other) + } else { + -1 + } + } + } + + + class EntrySet(private val map: ArrayMap) : MutableSet> { + override val size: Int get() = map.size + override fun contains(element: MutableMap.MutableEntry): Boolean = map[element.key] == element.value + override fun isEmpty(): Boolean = map.isEmpty() + override fun iterator(): MutableIterator> = MapIterator(map) + override fun clear() = map.clear() + + override fun containsAll(elements: Collection>): Boolean { + elements.forEach { + if (!contains(it)) return false + } + return true } - infix fun getFromIndex(index: Int): V? { - return if (index < 0) null - else arr[index]?.value + override fun add(element: MutableMap.MutableEntry): Boolean { + map[element.key] = element.value + return true } - override fun first(): V? = getFromIndex(0) - - override fun clear() { - end = 0 + override fun addAll(elements: Collection>): Boolean { + elements.forEach { add(it) } + return true } - override operator fun set(key: K, value: V) { - setAndGet(key, value) + override fun remove(element: MutableMap.MutableEntry): Boolean { + val index = map.search(element.key) + val value = map.getFromIndex(index) + return if (value == element.value) { + map.delete(index) + true + } else { + false + } } - override fun setAndGet(key: K, value: V): V? { - @Suppress("SENSELESS_COMPARISON") - if (key == null) return null - // 首先查找得到目标所在的下标 - val index = search(key) - var prev: V? = null - if (index < 0) { - // 下标小于零表示不存在,直接插入数据 - insert(key, value, -index - 1) - } else { - val node = arr[index] - if (node != null) { - prev = node.value - node.value = value - } else arr[index] = Node(key, value) - } - return prev + override fun removeAll(elements: Collection>): Boolean { + elements.forEach { remove(it) } + return true } - override infix fun putAll(from: Map) { - from.forEach { (key, value) -> - val index = search(key) - if (index < 0) arr[end++] = Node(key, value) - else { - val node = arr[index] - if (node != null) node.value = value - else arr[index] = Node(key, value) - } - } - arr.sort() + override fun retainAll(elements: Collection>): Boolean { + val contains = elements.filter { map[it.key] == it.value } + map.clear() + map.resize(contains.size) + addAll(contains) + return true + } + } + + class MapIterator(private val map: ArrayMap) : MutableIterator> { + private var index = 0 + + override fun hasNext(): Boolean { + @Suppress("ControlFlowWithEmptyBody") + while (++index < map.arr.size && index < map.end && map.arr[index] == null); + index-- + return index < map.end } - override infix fun delete(key: K): V? { - val index = search(key) - return delete(index) + override fun next(): MutableMap.MutableEntry = map.arr[index++]!! + + override fun remove() { + map.delete(index) + index-- + } + } + + class KeySet(private val map: ArrayMap) : MutableSet { + override val size: Int get() = map.size + override fun contains(element: K): Boolean = map.containsKey(element) + override fun isEmpty(): Boolean = size == 0 + override fun iterator(): MutableIterator = KeyIterator(map) + + override fun containsAll(elements: Collection): Boolean { + elements.forEach { + if (!map.containsKey(it)) return false + } + return true } - override infix fun containsKey(key: K): Boolean = search(key) >= 0 - - override infix fun containsValue(value: V): Boolean { - arr.forEach { - if (it?.value == value) return true - } - return false + override fun add(element: K): Boolean = false + override fun addAll(elements: Collection): Boolean = false + override fun clear() = map.clear() + override fun retainAll(elements: Collection): Boolean = map.entries.retainAll(elements.map { map.getNode(it) }.filterNotNull()) + override fun remove(element: K): Boolean = map.remove(element) != null + override fun removeAll(elements: Collection): Boolean { + elements.forEach { remove(it) } + return true } + } - override infix operator fun get(key: K): V? = getNode(key)?.value - override fun isEmpty(): Boolean = end == 0 + class KeyIterator(map: ArrayMap) : MutableIterator { + private val iterator = map.iterator() + override fun hasNext(): Boolean = iterator.hasNext() + override fun next(): K = iterator.next().key + override fun remove() = iterator.remove() + } - fun copy(): ArrayMap { - val clone = ArrayMap(0) - clone.arr = Array(end) { arr[it]?.let { Node(it.key, it.value) } } - clone.end = end - return clone + class ValueCollection(private val map: ArrayMap<*, V>) : MutableCollection { + override val size: Int get() = map.size + override fun contains(element: V): Boolean = map.containsValue(element) + override fun isEmpty(): Boolean = size == 0 + override fun iterator(): MutableIterator = ValueIterator(map) + override fun add(element: V): Boolean = false + override fun addAll(elements: Collection): Boolean = false + override fun clear() = map.clear() + override fun remove(element: V): Boolean = false + override fun removeAll(elements: Collection): Boolean = false + override fun retainAll(elements: Collection): Boolean = false + + override fun containsAll(elements: Collection): Boolean { + elements.forEach { + if (!map.containsValue(it)) return false + } + return true } + } - override fun toString(): String { - if (end == 0) return "{}" - val sb = StringBuilder("{${arr[0]}") - for (i in 1 until end) { - sb.append(", ") - sb.append(arr[i]) - } - sb.append("}") - return sb.toString() - } - - private fun getNode(key: K): Node? { - @Suppress("SENSELESS_COMPARISON") - if (key == null) return null - val index = search(key) - return if (index < 0) null - else arr[index] - } - - private fun resize(newSize: Int = if (arr.isNotEmpty()) arr.size * 2 else 1) { - arr = arr.copyOf(newSize) - } - - private fun insert(key: K, value: V, index: Int): V? { - if (end == arr.size) resize() - System.arraycopy(arr, index, arr, index + 1, end - index) - arr[index] = Node(key, value) - end++ - return value - } - - private infix fun delete(index: Int): V? { - if (index in 0 until end) { - val oldNode = arr[index] - System.arraycopy(arr, index + 1, arr, index, end - index - 1) - end-- - return oldNode?.value - } - return null - } - - class Node, V>( - override val key: K, - @Volatile override var value: V - ) : Comparable, MutableMap.MutableEntry { - override fun compareTo(other: K): Int = key.compareTo(other) - override fun toString(): String = "$key=$value" - override fun setValue(newValue: V): V = value.also { value = newValue } - } - - - class EntrySet, V>(private val map: ArrayMap) : MutableSet> { - override val size: Int get() = map.size - override fun contains(element: MutableMap.MutableEntry): Boolean = map[element.key] == element.value - override fun isEmpty(): Boolean = map.isEmpty() - override fun iterator(): MutableIterator> = MapIterator(map) - override fun clear() = map.clear() - - override fun containsAll(elements: Collection>): Boolean { - elements.forEach { - if (!contains(it)) return false - } - return true - } - - override fun add(element: MutableMap.MutableEntry): Boolean { - map[element.key] = element.value - return true - } - - override fun addAll(elements: Collection>): Boolean { - elements.forEach { add(it) } - return true - } - - override fun remove(element: MutableMap.MutableEntry): Boolean { - val index = map.search(element.key) - val value = map.getFromIndex(index) - return if (value == element.value) { - map.delete(index) - true - } else { - false - } - } - - override fun removeAll(elements: Collection>): Boolean { - elements.forEach { remove(it) } - return true - } - - override fun retainAll(elements: Collection>): Boolean { - val contains = elements.filter { map[it.key] == it.value } - map.clear() - map.resize(contains.size) - addAll(contains) - return true - } - } - - class MapIterator, V>(private val map: ArrayMap) : MutableIterator> { - private var index = 0 - - override fun hasNext(): Boolean { - @Suppress("ControlFlowWithEmptyBody") - while (++index < map.arr.size && index < map.end && map.arr[index] == null); - index-- - return index < map.end - } - - override fun next(): MutableMap.MutableEntry = map.arr[index++]!! - - override fun remove() { - map.delete(index) - index-- - } - } - - class KeySet>(private val map: ArrayMap) : MutableSet { - override val size: Int get() = map.size - override fun contains(element: K): Boolean = map.containsKey(element) - override fun isEmpty(): Boolean = size == 0 - override fun iterator(): MutableIterator = KeyIterator(map) - - override fun containsAll(elements: Collection): Boolean { - elements.forEach { - if (!map.containsKey(it)) return false - } - return true - } - - override fun add(element: K): Boolean = false - override fun addAll(elements: Collection): Boolean = false - override fun clear() = map.clear() - override fun retainAll(elements: Collection): Boolean = map.entries.retainAll(elements.map { map.getNode(it) }.filterNotNull()) - override fun remove(element: K): Boolean = map.remove(element) != null - override fun removeAll(elements: Collection): Boolean { - elements.forEach { remove(it) } - return true - } - } - - class KeyIterator>(map: ArrayMap) : MutableIterator { - private val iterator = map.iterator() - override fun hasNext(): Boolean = iterator.hasNext() - override fun next(): K = iterator.next().key - override fun remove() = iterator.remove() - } - - class ValueCollection(private val map: ArrayMap<*, V>) : MutableCollection { - override val size: Int get() = map.size - override fun contains(element: V): Boolean = map.containsValue(element) - override fun isEmpty(): Boolean = size == 0 - override fun iterator(): MutableIterator = ValueIterator(map) - override fun add(element: V): Boolean = false - override fun addAll(elements: Collection): Boolean = false - override fun clear() = map.clear() - override fun remove(element: V): Boolean = false - override fun removeAll(elements: Collection): Boolean = false - override fun retainAll(elements: Collection): Boolean = false - - override fun containsAll(elements: Collection): Boolean { - elements.forEach { - if (!map.containsValue(it)) return false - } - return true - } - } - - class ValueIterator(map: ArrayMap<*, V>) : MutableIterator { - private val iterator = map.iterator() - override fun hasNext(): Boolean = iterator.hasNext() - override fun next(): V = iterator.next().value - override fun remove() = iterator.remove() - } + class ValueIterator(map: ArrayMap<*, V>) : MutableIterator { + private val iterator = map.iterator() + override fun hasNext(): Boolean = iterator.hasNext() + override fun next(): V = iterator.next().value + override fun remove() = iterator.remove() + } } diff --git a/src/main/kotlin/cn/tursom/core/datastruct/ComparableArrayMap.kt b/src/main/kotlin/cn/tursom/core/datastruct/ComparableArrayMap.kt new file mode 100644 index 0000000..afcdaaf --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/datastruct/ComparableArrayMap.kt @@ -0,0 +1,27 @@ +package cn.tursom.core.datastruct + +@Suppress("MemberVisibilityCanBePrivate") +class ComparableArrayMap, V>(initialCapacity: Int = 16) : ArrayMap(initialCapacity) { + /** + * @param key 查找的键 + * @return 键所在的下标 + * @return < 0 如果键不存在, -${return} - 1 为如果插入应插入的下标 + */ + override fun search(key: K): Int { + if (end == 0) return -1 + return arr.binarySearch(key, 0, end) + } + + override infix fun putAll(from: Map) { + from.forEach { (key, value) -> + val index = search(key) + if (index < 0) arr[end++] = Node(key, value) + else { + val node = arr[index] + if (node != null) node.value = value + else arr[index] = Node(key, value) + } + } + arr.sort() + } +} diff --git a/src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt b/src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt new file mode 100644 index 0000000..029b6b3 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt @@ -0,0 +1,203 @@ +package cn.tursom.core.datastruct + +import cn.tursom.core.cast + +@Suppress("MemberVisibilityCanBePrivate") +open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { + @Volatile + protected var arr: Array = Array(initialCapacity) { null } + + @Volatile + protected var arrValue: Array = Array(initialCapacity) { null } + + @Volatile + protected var end = 0 + + override val size: Int get() = end + + @Suppress("LeakingThis") + override val entries: MutableSet> = EntrySet(this) + override val keys: MutableSet get() = arrValue.asList().subList(0, end).toMutableSet().cast() + override val values: MutableCollection get() = arrValue.asList().cast>().subList(0, end) + + /** + * @param key 查找的键 + * @return 键所在的下标 + * @return < 0 如果键不存在, -${return} - 1 为如果插入应插入的下标 + */ + open fun search(key: K): Int { + if (end == 0) return -1 + for (index in 0 until end) { + if ((arr[index] ?: continue) == key) { + return index + } + } + return -1 + } + + private fun resize(newSize: Int = if (arr.isNotEmpty()) arr.size * 2 else 1) { + arr = arr.copyOf(newSize) + arrValue = arrValue.copyOf(newSize) + } + + override fun setAndGet(key: K, value: V): V? { + var index = search(key) + if (index < 0 || index >= end) { + if (end == arr.size) { + resize() + } + index = end++ + arr[index] = key + } + return setByIndex(index, value) + } + + override fun set(key: K, value: V) { + setAndGet(key, value) + } + + fun setByIndex(index: Int, value: V): V? { + val oldValue = arrValue[end] + arrValue[index] = value + return oldValue.cast() + } + + override fun delete(key: K): V? { + return deleteByIndex(search(key)) + } + + fun deleteByIndex(index: Int): V? { + if (index < 0 || index >= end) return null + val oldValue = arrValue[index] + System.arraycopy(arr, index + 1, arr, index, end - index - 1) + System.arraycopy(arrValue, index + 1, arrValue, index, end - index - 1) + end-- + return oldValue.cast() + } + + override fun clear() { + end = 0 + } + + override fun first(): V? { + return if (end <= 0) { + null + } else { + arrValue[0].cast() + } + } + + override fun containsKey(key: K): Boolean { + return search(key) >= 0 + } + + override fun containsValue(value: V): Boolean { + if (end == 0) return false + for (index in 0 until end) { + if ((arrValue[index] ?: continue) == value) { + return true + } + } + return false + } + + override fun get(key: K): V? { + return getByIndex(search(key)) + } + + fun getKeyByIndex(index: Int): K? { + return if (index < 0 || index >= end) { + null + } else { + arr[index].cast() + } + } + + fun getByIndex(index: Int): V? { + return if (index < 0 || index >= end) { + null + } else { + arrValue[index].cast() + } + } + + override fun isEmpty(): Boolean = end <= 0 + + class EntrySet(private val map: ParallelArrayMap) : MutableSet> { + override val size: Int get() = map.size + override fun contains(element: MutableMap.MutableEntry): Boolean = map[element.key] == element.value + override fun isEmpty(): Boolean = map.isEmpty() + override fun iterator(): MutableIterator> = MapIterator(map) + override fun clear() = map.clear() + + override fun containsAll(elements: Collection>): Boolean { + elements.forEach { + if (!contains(it)) return false + } + return true + } + + override fun add(element: MutableMap.MutableEntry): Boolean { + map[element.key] = element.value + return true + } + + override fun addAll(elements: Collection>): Boolean { + elements.forEach { add(it) } + return true + } + + override fun remove(element: MutableMap.MutableEntry): Boolean { + val index = map.search(element.key) + val value = map.getByIndex(index) + return if (value == element.value) { + map.deleteByIndex(index) + true + } else { + false + } + } + + override fun removeAll(elements: Collection>): Boolean { + elements.forEach { remove(it) } + return true + } + + override fun retainAll(elements: Collection>): Boolean { + val contains = elements.filter { map[it.key] == it.value } + map.clear() + map.resize(contains.size) + addAll(contains) + return true + } + } + + class MapIterator(private val map: ParallelArrayMap) : MutableIterator> { + private var index = 0 + + override fun hasNext(): Boolean { + @Suppress("ControlFlowWithEmptyBody") + while (++index < map.arr.size && index < map.end && map.arr[index] == null); + index-- + return index < map.end + } + + override fun next(): MutableMap.MutableEntry = ParallelArrayMapEntry(map, index++) + + override fun remove() { + map.deleteByIndex(index) + index-- + } + } + + class ParallelArrayMapEntry( + val map: ParallelArrayMap, + val index: Int + ) : MutableMap.MutableEntry { + override val key: K get() = map.getKeyByIndex(index).cast() + override val value: V get() = map.getByIndex(index).cast() + override fun setValue(newValue: V): V { + return map.setByIndex(index, newValue).cast() + } + } +} diff --git a/src/main/kotlin/cn/tursom/core/stream/impl/JavaInputStreamProxy.kt b/src/main/kotlin/cn/tursom/core/stream/impl/JavaInputStreamProxy.kt new file mode 100644 index 0000000..5239b16 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/stream/impl/JavaInputStreamProxy.kt @@ -0,0 +1,15 @@ +package cn.tursom.core.stream.impl + +import java.io.InputStream + +class JavaInputStreamProxy( + val inputStream: cn.tursom.core.stream.InputStream +) : InputStream() { + override fun read(): Int = inputStream.read() + override fun read(b: ByteArray, off: Int, len: Int): Int = inputStream.read(b, off, len) + + override fun skip(n: Long): Long { + inputStream.skip(n) + return n + } +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt new file mode 100644 index 0000000..be9db7c --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt @@ -0,0 +1,39 @@ +package cn.tursom.utils.coroutine + +import cn.tursom.core.cast +import kotlinx.coroutines.Job +import java.util.concurrent.ConcurrentHashMap +import kotlin.coroutines.coroutineContext + +open class CoroutineLocal { + open suspend fun get(): T? { + var attach: MutableMap, Any?>? = coroutineContext[CoroutineLocalContext] + if (attach == null) { + val job = coroutineContext[Job] ?: return null + attach = attachMap[job] + } + return attach?.get(this)?.cast() + } + + open suspend infix fun set(value: T): Boolean { + var attach: MutableMap, Any?>? = coroutineContext[CoroutineLocalContext] + if (attach == null) { + val job = coroutineContext[Job] ?: return false + attach = attachMap[job] + if (attach == null) { + attach = HashMap() + attachMap[job] = attach + job.invokeOnCompletion { + attachMap.remove(job) + } + } + } + attach[this] = value + return true + } + + companion object { + private val attachMap = ConcurrentHashMap, Any?>>() + override fun toString(): String = attachMap.toString() + } +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt new file mode 100644 index 0000000..7f8cce1 --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt @@ -0,0 +1,43 @@ +package cn.tursom.utils.coroutine + +import kotlinx.coroutines.* +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +open class CoroutineLocalContext( + private val map: MutableMap, Any?> = HashMap(4) +) : CoroutineContext.Element, MutableMap, Any?> by map { + override val key: CoroutineContext.Key<*> get() = Companion + + override fun toString(): String { + return map.toString() + } + + companion object : CoroutineContext.Key +} + +fun CoroutineScope.launchWithCoroutineLocalContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> Unit +): Job { + return launch(context + CoroutineLocalContext(map), start, block) +} + +@Suppress("DeferredIsResult") +fun CoroutineScope.asyncWithCoroutineLocalContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): Deferred { + return async(context + CoroutineLocalContext(map), start, block) +} + +suspend fun withCoroutineLocalContext( + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): T { + return withContext(CoroutineLocalContext(map), block) +} \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt new file mode 100644 index 0000000..5aecdbf --- /dev/null +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -0,0 +1,32 @@ +package cn.tursom.utils.coroutine + +import cn.tursom.core.usingTime +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +val testCoroutineLocal = CoroutineLocal() +val testCoroutineLocalList = Array(100000) { + CoroutineLocal() +}.asList() + +suspend fun main() { + println(testCoroutineLocal.set(1)) + GlobalScope.launch { + coroutineScope { } + coroutineContext + testCoroutineLocal set 0 + testCoroutineLocal set 0 + testCoroutineLocalList.forEachIndexed { index, coroutineLocal -> + coroutineLocal set index + } + println(usingTime { + repeat(1000000000) { + testCoroutineLocal.get() + } + }) + }.join() + delay(1000) + println(CoroutineLocal) +} \ No newline at end of file From a9137789dbb7b013956a5db5c549405f770188f4 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 02:29:57 +0800 Subject: [PATCH 02/15] add CoroutineScopeContext --- .../utils/coroutine/CoroutineLocalContext.kt | 30 +------- .../utils/coroutine/CoroutineScopeContext.kt | 26 +++++++ .../kotlin/cn/tursom/utils/coroutine/utils.kt | 77 +++++++++++++++++++ .../utils/coroutine/CoroutineLocalTest.kt | 15 ++-- 4 files changed, 110 insertions(+), 38 deletions(-) create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt index 7f8cce1..849e807 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt @@ -1,8 +1,6 @@ package cn.tursom.utils.coroutine -import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext open class CoroutineLocalContext( private val map: MutableMap, Any?> = HashMap(4) @@ -10,34 +8,8 @@ open class CoroutineLocalContext( override val key: CoroutineContext.Key<*> get() = Companion override fun toString(): String { - return map.toString() + return "CoroutineLocalContext$map" } companion object : CoroutineContext.Key -} - -fun CoroutineScope.launchWithCoroutineLocalContext( - context: CoroutineContext = EmptyCoroutineContext, - start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), - block: suspend CoroutineScope.() -> Unit -): Job { - return launch(context + CoroutineLocalContext(map), start, block) -} - -@Suppress("DeferredIsResult") -fun CoroutineScope.asyncWithCoroutineLocalContext( - context: CoroutineContext = EmptyCoroutineContext, - start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), - block: suspend CoroutineScope.() -> T -): Deferred { - return async(context + CoroutineLocalContext(map), start, block) -} - -suspend fun withCoroutineLocalContext( - map: MutableMap, Any?> = HashMap(4), - block: suspend CoroutineScope.() -> T -): T { - return withContext(CoroutineLocalContext(map), block) } \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt new file mode 100644 index 0000000..4c535ef --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt @@ -0,0 +1,26 @@ +package cn.tursom.utils.coroutine + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.GlobalScope +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext + +class CoroutineScopeContext( + var coroutineScope: CoroutineScope +) : CoroutineContext.Element { + override val key: CoroutineContext.Key<*> get() = Companion + override fun toString(): String = "CoroutineScopeContext(coroutineScope=$coroutineScope)" + + companion object : CoroutineContext.Key, CoroutineLocal() { + override suspend fun get(): CoroutineScope = coroutineContext[this]?.coroutineScope ?: super.get() ?: GlobalScope + override suspend fun set(value: CoroutineScope): Boolean { + val coroutineScopeContext = coroutineContext[this] + return if (coroutineScopeContext != null) { + coroutineScopeContext.coroutineScope = value + true + } else { + super.set(value) + } + } + } +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt new file mode 100644 index 0000000..8d66ff3 --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -0,0 +1,77 @@ +package cn.tursom.utils.coroutine + +import kotlinx.coroutines.* +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + + +fun CoroutineScope.launchWithCoroutineLocalContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> Unit +): Job { + return launch(context + CoroutineLocalContext(map), start, block) +} + +@Suppress("DeferredIsResult") +fun CoroutineScope.asyncWithCoroutineLocalContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): Deferred { + return async(context + CoroutineLocalContext(map), start, block) +} + +suspend fun withCoroutineLocalContext( + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): T { + return withContext(CoroutineLocalContext(map), block) +} + +@Throws(InterruptedException::class) +fun runBlockingWithCoroutineLocalContext( + context: CoroutineContext = EmptyCoroutineContext, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): T { + return runBlocking(context + CoroutineLocalContext(map), block) +} + +fun CoroutineScope.launchWithCoroutineScopeContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +): Job { + return launch(context + CoroutineScopeContext(this), start, block) +} + +@Suppress("DeferredIsResult") +fun CoroutineScope.asyncWithCoroutineScopeContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> T +): Deferred { + return async(context + CoroutineScopeContext(this), start, block) +} + +fun CoroutineScope.launchWithCoroutineLocalAndCoroutineScopeContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> Unit +): Job { + return launch(context + CoroutineLocalContext(map) + CoroutineScopeContext(this), start, block) +} + +@Suppress("DeferredIsResult") +fun CoroutineScope.asyncWithCoroutineLocalAndCoroutineScopeContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): Deferred { + return async(context + CoroutineLocalContext(map) + CoroutineScopeContext(this), start, block) +} diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index 5aecdbf..1fbaf89 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,28 +1,25 @@ package cn.tursom.utils.coroutine import cn.tursom.core.usingTime -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.launch val testCoroutineLocal = CoroutineLocal() val testCoroutineLocalList = Array(100000) { CoroutineLocal() }.asList() -suspend fun main() { - println(testCoroutineLocal.set(1)) - GlobalScope.launch { - coroutineScope { } - coroutineContext +fun main() = runBlockingWithCoroutineLocalContext { + println(coroutineContext) + launchWithCoroutineLocalAndCoroutineScopeContext { + println(coroutineContext) + println(CoroutineScopeContext.get()) testCoroutineLocal set 0 testCoroutineLocal set 0 testCoroutineLocalList.forEachIndexed { index, coroutineLocal -> coroutineLocal set index } println(usingTime { - repeat(1000000000) { + repeat(10000000) { testCoroutineLocal.get() } }) From 9b80eabdf0aff89a859441d9c61a9ae58b3e3c23 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 02:44:51 +0800 Subject: [PATCH 03/15] add CoroutineScopeContext --- .../utils/coroutine/CoroutineScopeContext.kt | 2 +- .../kotlin/cn/tursom/utils/coroutine/utils.kt | 12 ++++++++++++ .../utils/coroutine/CoroutineLocalTest.kt | 19 +++++++------------ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt index 4c535ef..4c02a63 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt @@ -6,7 +6,7 @@ import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext class CoroutineScopeContext( - var coroutineScope: CoroutineScope + var coroutineScope: CoroutineScope = GlobalScope ) : CoroutineContext.Element { override val key: CoroutineContext.Key<*> get() = Companion override fun toString(): String = "CoroutineScopeContext(coroutineScope=$coroutineScope)" diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index 8d66ff3..f9397d5 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -75,3 +75,15 @@ fun CoroutineScope.asyncWithCoroutineLocalAndCoroutineScopeContext( ): Deferred { return async(context + CoroutineLocalContext(map) + CoroutineScopeContext(this), start, block) } + +@Throws(InterruptedException::class) +fun runBlockingWithCoroutineLocalAndCoroutineScopeContext( + context: CoroutineContext = EmptyCoroutineContext, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): T { + return runBlocking(context + CoroutineLocalContext(map) + CoroutineScopeContext()) { + CoroutineScopeContext set this + block() + } +} diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index 1fbaf89..108eec9 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,6 +1,5 @@ package cn.tursom.utils.coroutine -import cn.tursom.core.usingTime import kotlinx.coroutines.delay val testCoroutineLocal = CoroutineLocal() @@ -8,21 +7,17 @@ val testCoroutineLocalList = Array(100000) { CoroutineLocal() }.asList() -fun main() = runBlockingWithCoroutineLocalContext { +fun main() = runBlockingWithCoroutineLocalAndCoroutineScopeContext { println(coroutineContext) - launchWithCoroutineLocalAndCoroutineScopeContext { + CoroutineScopeContext.get().launchWithCoroutineLocalAndCoroutineScopeContext { println(coroutineContext) println(CoroutineScopeContext.get()) - testCoroutineLocal set 0 - testCoroutineLocal set 0 - testCoroutineLocalList.forEachIndexed { index, coroutineLocal -> - coroutineLocal set index + println(Thread.currentThread().name) + CoroutineScopeContext.get().launchWithCoroutineLocalAndCoroutineScopeContext { + println(coroutineContext) + println(CoroutineScopeContext.get()) + println(Thread.currentThread().name) } - println(usingTime { - repeat(10000000) { - testCoroutineLocal.get() - } - }) }.join() delay(1000) println(CoroutineLocal) From 78c06598e12fbb4ee9ed256a70f25999ef732317 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 15:45:45 +0800 Subject: [PATCH 04/15] add CurrentThreadCoroutineScope --- .../utils/coroutine/CoroutineContextScope.kt | 11 +++ .../tursom/utils/coroutine/CoroutineLocal.kt | 3 +- .../utils/coroutine/CoroutineScopeContext.kt | 15 +++- .../coroutine/CurrentThreadCoroutineScope.kt | 63 +++++++++++++++ .../kotlin/cn/tursom/utils/coroutine/utils.kt | 31 +++++++ .../utils/coroutine/CoroutineLocalTest.kt | 80 ++++++++++++++++--- 6 files changed, 187 insertions(+), 16 deletions(-) create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineContextScope.kt create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineContextScope.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineContextScope.kt new file mode 100644 index 0000000..a990d6b --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineContextScope.kt @@ -0,0 +1,11 @@ +package cn.tursom.utils.coroutine + +import kotlinx.coroutines.CoroutineScope +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext + +class CoroutineContextScope(override val coroutineContext: CoroutineContext) : CoroutineScope { + companion object { + suspend fun get() = CoroutineContextScope(coroutineContext) + } +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt index be9db7c..4c03611 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt @@ -3,6 +3,7 @@ package cn.tursom.utils.coroutine import cn.tursom.core.cast import kotlinx.coroutines.Job import java.util.concurrent.ConcurrentHashMap +import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext open class CoroutineLocal { @@ -33,7 +34,7 @@ open class CoroutineLocal { } companion object { - private val attachMap = ConcurrentHashMap, Any?>>() + private val attachMap = ConcurrentHashMap, Any?>>() override fun toString(): String = attachMap.toString() } } \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt index 4c02a63..c9199a7 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineScopeContext.kt @@ -1,7 +1,9 @@ package cn.tursom.utils.coroutine +import cn.tursom.core.cast import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext @@ -9,11 +11,20 @@ class CoroutineScopeContext( var coroutineScope: CoroutineScope = GlobalScope ) : CoroutineContext.Element { override val key: CoroutineContext.Key<*> get() = Companion + override fun toString(): String = "CoroutineScopeContext(coroutineScope=$coroutineScope)" companion object : CoroutineContext.Key, CoroutineLocal() { - override suspend fun get(): CoroutineScope = coroutineContext[this]?.coroutineScope ?: super.get() ?: GlobalScope - override suspend fun set(value: CoroutineScope): Boolean { + override suspend fun get(): CoroutineScope = coroutineContext[this]?.coroutineScope ?: super.get() + ?: coroutineContext[Job]?.let { + if (it is CoroutineScope) { + it.cast() + } else { + null + } + } ?: CoroutineContextScope(coroutineContext) + + override suspend infix fun set(value: CoroutineScope): Boolean { val coroutineScopeContext = coroutineContext[this] return if (coroutineScopeContext != null) { coroutineScopeContext.coroutineScope = value diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt new file mode 100644 index 0000000..29cd079 --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt @@ -0,0 +1,63 @@ +package cn.tursom.utils.coroutine + +import cn.tursom.core.cast +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext + +object CurrentThreadCoroutineScope { + private val currentThreadCoroutineScopeThreadLocal = ThreadLocal() + + private suspend fun getCoroutineScope(): CoroutineScope { + return currentThreadCoroutineScopeThreadLocal.get() ?: kotlin.run { + val eventLoop = newBlockingEventLoop() + val coroutineScope = newBlockingCoroutine(coroutineContext, Thread.currentThread(), eventLoop) + currentThreadCoroutineScopeThreadLocal.set(coroutineScope) + coroutineScope + } + } + + suspend fun launch( + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit + ) { + getCoroutineScope().start(start, block = block) + } + + private val BlockingEventLoop = Class.forName("kotlinx.coroutines.BlockingEventLoop") + private val BlockingEventLoopConstructor = BlockingEventLoop + .getConstructor(Thread::class.java) + .apply { isAccessible = true } + + private fun newBlockingEventLoop(thread: Thread = Thread.currentThread()): CoroutineDispatcher { + return BlockingEventLoopConstructor.newInstance(thread) as CoroutineDispatcher + } + + private val BlockingCoroutine = Class.forName("kotlinx.coroutines.BlockingCoroutine") + private val BlockingCoroutineConstructor = BlockingCoroutine.constructors[0].apply { isAccessible = true } + private val BlockingCoroutineStart = BlockingCoroutine.methods.first { it.name == "start" && it.parameters.size == 3 }.apply { isAccessible = true } + private val BlockingCoroutineJoinBlocking = BlockingCoroutine.methods.first { it.name == "joinBlocking" }.apply { isAccessible = true } + //private val BlockingCoroutineOnCompleted = BlockingCoroutine.methods.first { it.name == "onCompleted" }.apply { isAccessible = true } + + private fun newBlockingCoroutine( + coroutineContext: CoroutineContext, + thread: Thread = Thread.currentThread(), + eventLoop: CoroutineDispatcher + ): CoroutineScope { + return BlockingCoroutineConstructor.newInstance(coroutineContext, thread, eventLoop).cast() + } + + private fun CoroutineScope.start( + start: CoroutineStart = CoroutineStart.DEFAULT, + receiver: CoroutineScope = this, + block: suspend CoroutineScope.() -> T + ) { + BlockingCoroutineStart.invoke(this, start, receiver, block) + } + + private fun CoroutineScope.joinBlocking(): T { + return BlockingCoroutineJoinBlocking.invoke(this).cast() + } +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index f9397d5..9be05c6 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -87,3 +87,34 @@ fun runBlockingWithCoroutineLocalAndCoroutineScopeContext( block() } } + + +fun CoroutineScope.launchWithEnhanceContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> Unit +): Job { + return launch(context + CoroutineLocalContext(map), start, block) +} + +@Suppress("DeferredIsResult") +fun CoroutineScope.asyncWithEnhanceContext( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): Deferred { + return async(context + CoroutineLocalContext(map), start, block) +} + +@Throws(InterruptedException::class) +fun runBlockingWithEnhanceContext( + context: CoroutineContext = EmptyCoroutineContext, + map: MutableMap, Any?> = HashMap(4), + block: suspend CoroutineScope.() -> T +): T { + return runBlocking(context + CoroutineLocalContext(map)) { + block() + } +} \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index 108eec9..da94e78 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,24 +1,78 @@ package cn.tursom.utils.coroutine -import kotlinx.coroutines.delay +import cn.tursom.core.cast +import kotlinx.coroutines.* +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext val testCoroutineLocal = CoroutineLocal() val testCoroutineLocalList = Array(100000) { CoroutineLocal() }.asList() -fun main() = runBlockingWithCoroutineLocalAndCoroutineScopeContext { +suspend fun test() { println(coroutineContext) - CoroutineScopeContext.get().launchWithCoroutineLocalAndCoroutineScopeContext { - println(coroutineContext) - println(CoroutineScopeContext.get()) - println(Thread.currentThread().name) - CoroutineScopeContext.get().launchWithCoroutineLocalAndCoroutineScopeContext { - println(coroutineContext) - println(CoroutineScopeContext.get()) - println(Thread.currentThread().name) - } - }.join() + println(coroutineContext[Job] is CoroutineScope) + println(CoroutineScopeContext.get()) + println(Thread.currentThread().name) +} + +val EventLoop = Class.forName("kotlinx.coroutines.EventLoop") +val AbstractCoroutine = Class.forName("kotlinx.coroutines.AbstractCoroutine") + +val BlockingEventLoop = Class.forName("kotlinx.coroutines.BlockingEventLoop") +val BlockingEventLoopConstructor = BlockingEventLoop.getConstructor(Thread::class.java).apply { isAccessible = true } +fun newBlockingEventLoop(thread: Thread = Thread.currentThread()): CoroutineDispatcher { + return BlockingEventLoopConstructor.newInstance(thread) as CoroutineDispatcher +} + +val BlockingCoroutine = Class.forName("kotlinx.coroutines.BlockingCoroutine") +val BlockingCoroutineConstructor = BlockingCoroutine.constructors[0].apply { isAccessible = true } +val BlockingCoroutineStart = BlockingCoroutine.methods.first { it.name == "start" && it.parameters.size == 3 }.apply { isAccessible = true } +val BlockingCoroutineJoinBlocking = BlockingCoroutine.methods.first { it.name == "joinBlocking" }.apply { isAccessible = true } + +fun newBlockingCoroutine(coroutineContext: CoroutineContext, thread: Thread = Thread.currentThread(), eventLoop: CoroutineDispatcher): CoroutineScope { + return BlockingCoroutineConstructor.newInstance(coroutineContext, thread, eventLoop).cast() +} + +fun CoroutineScope.start(start: CoroutineStart = CoroutineStart.DEFAULT, receiver: CoroutineScope = this, block: suspend CoroutineScope.() -> Any?) { + return BlockingCoroutineStart.invoke(this, start, receiver, block).cast() +} + +fun CoroutineScope.joinBlocking() { + BlockingCoroutineJoinBlocking.invoke(this) +} + +suspend fun main() { + test() + CurrentThreadCoroutineScope.launch { + test() + } delay(1000) - println(CoroutineLocal) + //runBlockingWithEnhanceContext { + // println(coroutineContext) + // println(coroutineContext[Job] is CoroutineScope) + // println(CoroutineScopeContext.get()) + // println(Thread.currentThread().name) + // CoroutineContextScope(coroutineContext).launch { + // println(coroutineContext) + // println(coroutineContext[Job] is CoroutineScope) + // println(CoroutineScopeContext.get()) + // println(Thread.currentThread().name) + // }.join() + // //CoroutineScopeContext.get().launchWithEnhanceContext { + // // println(coroutineContext) + // // println(coroutineContext[Job] is CoroutineScope) + // // println(CoroutineScopeContext.get()) + // // println(Thread.currentThread().name) + // // CoroutineScopeContext.get().launchWithEnhanceContext { + // // println(coroutineContext) + // // println(coroutineContext[Job] is CoroutineScope) + // // println(CoroutineScopeContext.get()) + // // println(Thread.currentThread().name) + // // } + // //}.join() + // delay(1000) + // println(CoroutineLocal) + //} } \ No newline at end of file From f4ec9ca3c8993f39bc0d5141fb2e677f2906fff1 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 16:29:02 +0800 Subject: [PATCH 05/15] add CurrentThreadCoroutineScope --- .../coroutine/CurrentThreadCoroutineScope.kt | 36 ++++++++----- .../utils/coroutine/CoroutineLocalTest.kt | 50 ++++++++----------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt index 29cd079..826c864 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CurrentThreadCoroutineScope.kt @@ -1,31 +1,43 @@ package cn.tursom.utils.coroutine +import cn.tursom.core.SimpThreadLocal import cn.tursom.core.cast -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext object CurrentThreadCoroutineScope { - private val currentThreadCoroutineScopeThreadLocal = ThreadLocal() + private val eventLoopThreadLocal: SimpThreadLocal = SimpThreadLocal { + newBlockingEventLoop() + } private suspend fun getCoroutineScope(): CoroutineScope { - return currentThreadCoroutineScopeThreadLocal.get() ?: kotlin.run { - val eventLoop = newBlockingEventLoop() - val coroutineScope = newBlockingCoroutine(coroutineContext, Thread.currentThread(), eventLoop) - currentThreadCoroutineScopeThreadLocal.set(coroutineScope) - coroutineScope - } + val eventLoop = eventLoopThreadLocal.get() + val coroutineScopeContext = CoroutineScopeContext() + val newBlockingCoroutine = newBlockingCoroutine( + coroutineContext + coroutineScopeContext + Dispatchers.Unconfined, + Thread.currentThread(), + eventLoop + ) + coroutineScopeContext.coroutineScope = newBlockingCoroutine + return newBlockingCoroutine } suspend fun launch( start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit - ) { - getCoroutineScope().start(start, block = block) + ): Job { + val coroutineScope = getCoroutineScope() + //coroutineScope.launch(start = start, block = block) + coroutineScope.start(start, block = block) + return coroutineScope as Job } + private val EventLoop = Class.forName("kotlinx.coroutines.EventLoop") + private val EventLoopShouldBeProcessedFromContext = EventLoop.methods + .first { it.name == "shouldBeProcessedFromContext" } + .apply { isAccessible = true } + private val BlockingEventLoop = Class.forName("kotlinx.coroutines.BlockingEventLoop") private val BlockingEventLoopConstructor = BlockingEventLoop .getConstructor(Thread::class.java) diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index da94e78..71b9289 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,8 +1,6 @@ package cn.tursom.utils.coroutine -import cn.tursom.core.cast import kotlinx.coroutines.* -import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext val testCoroutineLocal = CoroutineLocal() @@ -17,37 +15,29 @@ suspend fun test() { println(Thread.currentThread().name) } -val EventLoop = Class.forName("kotlinx.coroutines.EventLoop") -val AbstractCoroutine = Class.forName("kotlinx.coroutines.AbstractCoroutine") - -val BlockingEventLoop = Class.forName("kotlinx.coroutines.BlockingEventLoop") -val BlockingEventLoopConstructor = BlockingEventLoop.getConstructor(Thread::class.java).apply { isAccessible = true } -fun newBlockingEventLoop(thread: Thread = Thread.currentThread()): CoroutineDispatcher { - return BlockingEventLoopConstructor.newInstance(thread) as CoroutineDispatcher -} - -val BlockingCoroutine = Class.forName("kotlinx.coroutines.BlockingCoroutine") -val BlockingCoroutineConstructor = BlockingCoroutine.constructors[0].apply { isAccessible = true } -val BlockingCoroutineStart = BlockingCoroutine.methods.first { it.name == "start" && it.parameters.size == 3 }.apply { isAccessible = true } -val BlockingCoroutineJoinBlocking = BlockingCoroutine.methods.first { it.name == "joinBlocking" }.apply { isAccessible = true } - -fun newBlockingCoroutine(coroutineContext: CoroutineContext, thread: Thread = Thread.currentThread(), eventLoop: CoroutineDispatcher): CoroutineScope { - return BlockingCoroutineConstructor.newInstance(coroutineContext, thread, eventLoop).cast() -} - -fun CoroutineScope.start(start: CoroutineStart = CoroutineStart.DEFAULT, receiver: CoroutineScope = this, block: suspend CoroutineScope.() -> Any?) { - return BlockingCoroutineStart.invoke(this, start, receiver, block).cast() -} - -fun CoroutineScope.joinBlocking() { - BlockingCoroutineJoinBlocking.invoke(this) -} - suspend fun main() { - test() CurrentThreadCoroutineScope.launch { - test() + println("Unconfined : I'm working in thread ${Thread.currentThread().name}") + delay(50) + println("Unconfined : After delay in thread ${Thread.currentThread().name}") + delay(50) + println("Unconfined : After delay in thread ${Thread.currentThread().name}") } + GlobalScope.launch(Dispatchers.Unconfined) { // 非受限的——将和主线程一起工作 + println("Unconfined : I'm working in thread ${Thread.currentThread().name}") + delay(50) + println("Unconfined : After delay in thread ${Thread.currentThread().name}") + delay(50) + println("Unconfined : After delay in thread ${Thread.currentThread().name}") + } + GlobalScope.launch { // 父协程的上下文,主 runBlocking 协程 + println("main runBlocking: I'm working in thread ${Thread.currentThread().name}") + delay(100) + println("main runBlocking: After delay in thread ${Thread.currentThread().name}") + delay(100) + println("main runBlocking: After delay in thread ${Thread.currentThread().name}") + } + println("end") delay(1000) //runBlockingWithEnhanceContext { // println(coroutineContext) From 3b8baada36c86e52f7ee718ce5d8a2dccee33605 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 17:36:16 +0800 Subject: [PATCH 06/15] add CoroutineLocalContinuation --- .../coroutine/CoroutineLocalContinuation.kt | 11 +++ .../kotlin/cn/tursom/utils/coroutine/utils.kt | 24 ++++++ .../utils/coroutine/CoroutineLocalTest.kt | 75 ++++++++++++------- 3 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt new file mode 100644 index 0000000..b21b202 --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt @@ -0,0 +1,11 @@ +package cn.tursom.utils.coroutine + +import cn.tursom.core.cast +import kotlin.coroutines.Continuation +import kotlin.coroutines.CoroutineContext + +class CoroutineLocalContinuation( + private val completion: Continuation<*> +) : Continuation by completion.cast() { + override val context: CoroutineContext = completion.context + CoroutineLocalContext() +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index 9be05c6..c8e7144 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -1,8 +1,11 @@ package cn.tursom.utils.coroutine +import cn.tursom.core.cast import kotlinx.coroutines.* +import kotlin.coroutines.Continuation import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.coroutines.coroutineContext fun CoroutineScope.launchWithCoroutineLocalContext( @@ -117,4 +120,25 @@ fun runBlockingWithEnhanceContext( return runBlocking(context + CoroutineLocalContext(map)) { block() } +} + +suspend inline fun runWithCoroutineLocalContext(block: () -> T): T { + return (block.cast<(Continuation<*>) -> T>()).invoke(CoroutineLocalContinuation(getContinuation())) +} + +suspend inline fun runWithCoroutineLocal(block: () -> T): T { + if (coroutineContext[CoroutineLocalContext] == null) { + return runWithCoroutineLocalContext(block) + } + return block() +} + +@Suppress("NOTHING_TO_INLINE") +inline fun getContinuation(continuation: Continuation<*>): Continuation<*> { + return continuation +} + +suspend inline fun getContinuation(): Continuation<*> { + val getContinuation: (continuation: Continuation<*>) -> Continuation<*> = ::getContinuation + return (getContinuation.cast Continuation<*>>()).invoke() } \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index 71b9289..55ff8ea 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,12 +1,12 @@ package cn.tursom.utils.coroutine -import kotlinx.coroutines.* +import cn.tursom.core.cast +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlin.coroutines.Continuation import kotlin.coroutines.coroutineContext val testCoroutineLocal = CoroutineLocal() -val testCoroutineLocalList = Array(100000) { - CoroutineLocal() -}.asList() suspend fun test() { println(coroutineContext) @@ -15,30 +15,51 @@ suspend fun test() { println(Thread.currentThread().name) } -suspend fun main() { - CurrentThreadCoroutineScope.launch { - println("Unconfined : I'm working in thread ${Thread.currentThread().name}") - delay(50) - println("Unconfined : After delay in thread ${Thread.currentThread().name}") - delay(50) - println("Unconfined : After delay in thread ${Thread.currentThread().name}") +@Suppress("NOTHING_TO_INLINE") +inline fun getContinuation(continuation: Continuation<*>): Continuation<*> { + return continuation +} + +suspend inline fun getContinuation(): Continuation<*> { + val getContinuation: (continuation: Continuation<*>) -> Continuation<*> = ::getContinuation + return (getContinuation.cast Continuation<*>>()).invoke() +} + +suspend fun testCustomContext(): Int? = runWithCoroutineLocal { + println(coroutineContext) + return testCoroutineLocal.get() +} + +suspend fun main(): Unit = runWithCoroutineLocal { + repeat(100) { + testCoroutineLocal.set(it) + println(testCustomContext()) } - GlobalScope.launch(Dispatchers.Unconfined) { // 非受限的——将和主线程一起工作 - println("Unconfined : I'm working in thread ${Thread.currentThread().name}") - delay(50) - println("Unconfined : After delay in thread ${Thread.currentThread().name}") - delay(50) - println("Unconfined : After delay in thread ${Thread.currentThread().name}") - } - GlobalScope.launch { // 父协程的上下文,主 runBlocking 协程 - println("main runBlocking: I'm working in thread ${Thread.currentThread().name}") - delay(100) - println("main runBlocking: After delay in thread ${Thread.currentThread().name}") - delay(100) - println("main runBlocking: After delay in thread ${Thread.currentThread().name}") - } - println("end") - delay(1000) + ////println(::main.javaMethod?.parameters?.get(0)) + //println(coroutineContext) + //CurrentThreadCoroutineScope.launch { + // println("Unconfined : I'm working in thread ${Thread.currentThread().name}") + // delay(50) + // println("Unconfined : After delay in thread ${Thread.currentThread().name}") + // delay(50) + // println("Unconfined : After delay in thread ${Thread.currentThread().name}") + //} + //GlobalScope.launch(Dispatchers.Unconfined) { // 非受限的——将和主线程一起工作 + // println("Unconfined : I'm working in thread ${Thread.currentThread().name}") + // delay(50) + // println("Unconfined : After delay in thread ${Thread.currentThread().name}") + // delay(50) + // println("Unconfined : After delay in thread ${Thread.currentThread().name}") + //} + //GlobalScope.launch { // 父协程的上下文,主 runBlocking 协程 + // println("main runBlocking: I'm working in thread ${Thread.currentThread().name}") + // delay(100) + // println("main runBlocking: After delay in thread ${Thread.currentThread().name}") + // delay(100) + // println("main runBlocking: After delay in thread ${Thread.currentThread().name}") + //} + //println("end") + //delay(1000) //runBlockingWithEnhanceContext { // println(coroutineContext) // println(coroutineContext[Job] is CoroutineScope) From e2f209ae56822a4ba7358074a76904186f27503e Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 22:05:23 +0800 Subject: [PATCH 07/15] =?UTF-8?q?=E6=9B=B4=E5=8A=A0=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E6=9C=AC=E5=9C=B0=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/cn/tursom/core/Tools.kt | 21 ++++ .../tursom/utils/coroutine/CoroutineLocal.kt | 26 ++-- .../coroutine/CoroutineLocalContinuation.kt | 7 +- .../kotlin/cn/tursom/utils/coroutine/utils.kt | 66 +++++++++- .../utils/coroutine/CoroutineLocalTest.kt | 114 +++++++----------- 5 files changed, 150 insertions(+), 84 deletions(-) diff --git a/src/main/kotlin/cn/tursom/core/Tools.kt b/src/main/kotlin/cn/tursom/core/Tools.kt index d29a003..f4ce86f 100644 --- a/src/main/kotlin/cn/tursom/core/Tools.kt +++ b/src/main/kotlin/cn/tursom/core/Tools.kt @@ -377,4 +377,25 @@ inline fun Any.assert(action: T.() -> Unit): Boolean { } else { false } +} + +val Class<*>.allFields: List + get() { + var clazz = this + val list = ArrayList() + while (clazz != Any::class.java) { + list.addAll(clazz.declaredFields) + clazz = clazz.superclass + } + list.addAll(clazz.declaredFields) + return list + } + +fun Class<*>.forAllFields(action: (Field) -> Unit) { + var clazz = this + while (clazz != Any::class.java) { + clazz.declaredFields.forEach(action) + clazz = clazz.superclass + } + clazz.declaredFields.forEach(action) } \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt index 4c03611..9a298ce 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt @@ -10,8 +10,13 @@ open class CoroutineLocal { open suspend fun get(): T? { var attach: MutableMap, Any?>? = coroutineContext[CoroutineLocalContext] if (attach == null) { - val job = coroutineContext[Job] ?: return null - attach = attachMap[job] + if (injectCoroutineLocalContext()) { + attach = coroutineContext[CoroutineLocalContext] + } + if (attach == null) { + val job = coroutineContext[Job] ?: return null + attach = attachMap[job] + } } return attach?.get(this)?.cast() } @@ -19,13 +24,18 @@ open class CoroutineLocal { open suspend infix fun set(value: T): Boolean { var attach: MutableMap, Any?>? = coroutineContext[CoroutineLocalContext] if (attach == null) { - val job = coroutineContext[Job] ?: return false - attach = attachMap[job] + if (injectCoroutineLocalContext()) { + attach = coroutineContext[CoroutineLocalContext] + } if (attach == null) { - attach = HashMap() - attachMap[job] = attach - job.invokeOnCompletion { - attachMap.remove(job) + val job = coroutineContext[Job] ?: return false + attach = attachMap[job] + if (attach == null) { + attach = HashMap() + attachMap[job] = attach + job.invokeOnCompletion { + attachMap.remove(job) + } } } } diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt index b21b202..9ba20bc 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContinuation.kt @@ -3,9 +3,14 @@ package cn.tursom.utils.coroutine import cn.tursom.core.cast import kotlin.coroutines.Continuation import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext class CoroutineLocalContinuation( private val completion: Continuation<*> ) : Continuation by completion.cast() { - override val context: CoroutineContext = completion.context + CoroutineLocalContext() + override val context: CoroutineContext = completion.context + if (completion.context[CoroutineLocalContext] == null) { + CoroutineLocalContext() + } else { + EmptyCoroutineContext + } } \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index c8e7144..c112554 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -1,6 +1,8 @@ package cn.tursom.utils.coroutine import cn.tursom.core.cast +import cn.tursom.core.forAllFields +import cn.tursom.core.isInheritanceFrom import kotlinx.coroutines.* import kotlin.coroutines.Continuation import kotlin.coroutines.CoroutineContext @@ -122,11 +124,21 @@ fun runBlockingWithEnhanceContext( } } -suspend inline fun runWithCoroutineLocalContext(block: () -> T): T { - return (block.cast<(Continuation<*>) -> T>()).invoke(CoroutineLocalContinuation(getContinuation())) +suspend fun runWithCoroutineLocalContext( + block: suspend () -> T +): T { + val continuation: Any? = getContinuation() + val coroutineLocalContinuation = if (continuation is Continuation<*>) { + CoroutineLocalContinuation(continuation.cast()) + } else { + return continuation.cast() + } + return (block.cast<(Any?) -> T>()).invoke(coroutineLocalContinuation) } -suspend inline fun runWithCoroutineLocal(block: () -> T): T { +suspend fun runWithCoroutineLocal( + block: suspend () -> T +): T { if (coroutineContext[CoroutineLocalContext] == null) { return runWithCoroutineLocalContext(block) } @@ -141,4 +153,52 @@ inline fun getContinuation(continuation: Continuation<*>): Continuation<*> { suspend inline fun getContinuation(): Continuation<*> { val getContinuation: (continuation: Continuation<*>) -> Continuation<*> = ::getContinuation return (getContinuation.cast Continuation<*>>()).invoke() +} + +suspend inline fun injectCoroutineLocalContext(coroutineLocalContext: CoroutineLocalContext = CoroutineLocalContext()): Boolean { + return if (coroutineContext[CoroutineLocalContext] == null) { + getContinuation().injectCoroutineLocalContext(coroutineLocalContext) + } else { + true + } +} + +private val BaseContinuationImpl = Class.forName("kotlin.coroutines.jvm.internal.BaseContinuationImpl") +private val BaseContinuationImplCompletion = BaseContinuationImpl.getDeclaredField("completion").apply { isAccessible = true } + +fun Continuation<*>.injectCoroutineLocalContext(coroutineLocalContext: CoroutineLocalContext = CoroutineLocalContext()): Boolean { + return if (context[CoroutineLocalContext] == null) { + if (BaseContinuationImpl.isInstance(this)) { + BaseContinuationImplCompletion.get(this).cast>().injectCoroutineLocalContext(coroutineLocalContext) + } + combinedContext(context) + if (context[CoroutineLocalContext] == null) { + javaClass.forAllFields { + if (!it.type.isInheritanceFrom(CoroutineContext::class.java)) { + return@forAllFields + } + it.isAccessible = true + val coroutineContext = it.get(this).cast() + it.set(this, coroutineContext + coroutineLocalContext) + } + context[CoroutineLocalContext] != null + } else { + true + } + } else { + true + } +} + +private val combinedContextClass = Class.forName("kotlin.coroutines.CombinedContext") +private val left = combinedContextClass.getDeclaredField("left").apply { isAccessible = true } + +fun combinedContext(coroutineContext: CoroutineContext): Boolean { + return if (coroutineContext.javaClass == combinedContextClass && coroutineContext[CoroutineLocalContext] == null) { + val leftObj = left.get(coroutineContext).cast() + left.set(coroutineContext, leftObj + CoroutineLocalContext()) + true + } else { + false + } } \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index 55ff8ea..a7eca6f 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,89 +1,59 @@ package cn.tursom.utils.coroutine +import cn.tursom.core.allFields import cn.tursom.core.cast -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job import kotlin.coroutines.Continuation import kotlin.coroutines.coroutineContext val testCoroutineLocal = CoroutineLocal() -suspend fun test() { - println(coroutineContext) - println(coroutineContext[Job] is CoroutineScope) - println(CoroutineScopeContext.get()) - println(Thread.currentThread().name) +suspend fun testCustomContext() { + testCoroutineLocal.set(1) + testInlineCustomContext() } -@Suppress("NOTHING_TO_INLINE") -inline fun getContinuation(continuation: Continuation<*>): Continuation<*> { +fun Any.printMsg() { + javaClass.allFields.forEach { + it.isAccessible = true + println("${it.type} ${it.name} = ${it.get(this)}") + val value: Any? = it.get(this) + println("${value?.javaClass} $value") + println(it.get(this) == this) + if (it.name == "completion") { + println((value as Continuation<*>).context) + } + println() + } +} + +val BaseContinuationImpl = Class.forName("kotlin.coroutines.jvm.internal.BaseContinuationImpl") +val BaseContinuationImplCompletion = BaseContinuationImpl.getDeclaredField("completion").apply { isAccessible = true } + +fun Continuation<*>.rootCompletion(): Continuation<*> { + var completion = this.javaClass.allFields.firstOrNull { it.name == "completion" } + val coroutineLocalContext = CoroutineLocalContext() + @Suppress("NAME_SHADOWING") var continuation = this + while (completion != null) { + continuation.injectCoroutineLocalContext(coroutineLocalContext) + completion.isAccessible = true + val newContinuation = completion.get(continuation)?.cast>() ?: return continuation + if (newContinuation == continuation) { + return continuation + } + completion = newContinuation.javaClass.allFields.firstOrNull { it.name == "completion" } + continuation = newContinuation + } + continuation.injectCoroutineLocalContext(coroutineLocalContext) return continuation } -suspend inline fun getContinuation(): Continuation<*> { - val getContinuation: (continuation: Continuation<*>) -> Continuation<*> = ::getContinuation - return (getContinuation.cast Continuation<*>>()).invoke() -} - -suspend fun testCustomContext(): Int? = runWithCoroutineLocal { +suspend inline fun testInlineCustomContext() { println(coroutineContext) - return testCoroutineLocal.get() + println("===================") } -suspend fun main(): Unit = runWithCoroutineLocal { - repeat(100) { - testCoroutineLocal.set(it) - println(testCustomContext()) - } - ////println(::main.javaMethod?.parameters?.get(0)) - //println(coroutineContext) - //CurrentThreadCoroutineScope.launch { - // println("Unconfined : I'm working in thread ${Thread.currentThread().name}") - // delay(50) - // println("Unconfined : After delay in thread ${Thread.currentThread().name}") - // delay(50) - // println("Unconfined : After delay in thread ${Thread.currentThread().name}") - //} - //GlobalScope.launch(Dispatchers.Unconfined) { // 非受限的——将和主线程一起工作 - // println("Unconfined : I'm working in thread ${Thread.currentThread().name}") - // delay(50) - // println("Unconfined : After delay in thread ${Thread.currentThread().name}") - // delay(50) - // println("Unconfined : After delay in thread ${Thread.currentThread().name}") - //} - //GlobalScope.launch { // 父协程的上下文,主 runBlocking 协程 - // println("main runBlocking: I'm working in thread ${Thread.currentThread().name}") - // delay(100) - // println("main runBlocking: After delay in thread ${Thread.currentThread().name}") - // delay(100) - // println("main runBlocking: After delay in thread ${Thread.currentThread().name}") - //} - //println("end") - //delay(1000) - //runBlockingWithEnhanceContext { - // println(coroutineContext) - // println(coroutineContext[Job] is CoroutineScope) - // println(CoroutineScopeContext.get()) - // println(Thread.currentThread().name) - // CoroutineContextScope(coroutineContext).launch { - // println(coroutineContext) - // println(coroutineContext[Job] is CoroutineScope) - // println(CoroutineScopeContext.get()) - // println(Thread.currentThread().name) - // }.join() - // //CoroutineScopeContext.get().launchWithEnhanceContext { - // // println(coroutineContext) - // // println(coroutineContext[Job] is CoroutineScope) - // // println(CoroutineScopeContext.get()) - // // println(Thread.currentThread().name) - // // CoroutineScopeContext.get().launchWithEnhanceContext { - // // println(coroutineContext) - // // println(coroutineContext[Job] is CoroutineScope) - // // println(CoroutineScopeContext.get()) - // // println(Thread.currentThread().name) - // // } - // //}.join() - // delay(1000) - // println(CoroutineLocal) - //} +suspend fun main() { + testCustomContext() + println(testCoroutineLocal.get()) + testInlineCustomContext() } \ No newline at end of file From 46cf3932f3f6c6c7298245bf4615fdbb4121d864 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 22:25:22 +0800 Subject: [PATCH 08/15] =?UTF-8?q?=E6=9B=B4=E5=8A=A0=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E6=9C=AC=E5=9C=B0=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/cn/tursom/utils/coroutine/utils.kt | 43 ++++++++--------- .../utils/coroutine/CoroutineLocalTest.kt | 47 ++++--------------- 2 files changed, 27 insertions(+), 63 deletions(-) diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index c112554..ab791e2 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -1,3 +1,5 @@ +@file:Suppress("unused") + package cn.tursom.utils.coroutine import cn.tursom.core.cast @@ -167,38 +169,31 @@ private val BaseContinuationImpl = Class.forName("kotlin.coroutines.jvm.internal private val BaseContinuationImplCompletion = BaseContinuationImpl.getDeclaredField("completion").apply { isAccessible = true } fun Continuation<*>.injectCoroutineLocalContext(coroutineLocalContext: CoroutineLocalContext = CoroutineLocalContext()): Boolean { - return if (context[CoroutineLocalContext] == null) { - if (BaseContinuationImpl.isInstance(this)) { - BaseContinuationImplCompletion.get(this).cast>().injectCoroutineLocalContext(coroutineLocalContext) - } - combinedContext(context) - if (context[CoroutineLocalContext] == null) { - javaClass.forAllFields { - if (!it.type.isInheritanceFrom(CoroutineContext::class.java)) { - return@forAllFields - } - it.isAccessible = true - val coroutineContext = it.get(this).cast() - it.set(this, coroutineContext + coroutineLocalContext) - } - context[CoroutineLocalContext] != null - } else { - true - } - } else { - true + if (context[CoroutineLocalContext] != null) return true + if (BaseContinuationImpl.isInstance(this)) { + BaseContinuationImplCompletion.get(this).cast>().injectCoroutineLocalContext(coroutineLocalContext) } + combinedContext(context) + if (context[CoroutineLocalContext] != null) return true + javaClass.forAllFields { + if (!it.type.isInheritanceFrom(CoroutineContext::class.java)) { + return@forAllFields + } + it.isAccessible = true + val coroutineContext = it.get(this).cast() + it.set(this, coroutineContext + coroutineLocalContext) + } + return context[CoroutineLocalContext] != null } private val combinedContextClass = Class.forName("kotlin.coroutines.CombinedContext") private val left = combinedContextClass.getDeclaredField("left").apply { isAccessible = true } fun combinedContext(coroutineContext: CoroutineContext): Boolean { - return if (coroutineContext.javaClass == combinedContextClass && coroutineContext[CoroutineLocalContext] == null) { + if (!combinedContextClass.isInstance(coroutineContext)) return false + if (coroutineContext[CoroutineLocalContext] == null) { val leftObj = left.get(coroutineContext).cast() left.set(coroutineContext, leftObj + CoroutineLocalContext()) - true - } else { - false } + return true } \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index a7eca6f..c80f630 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,9 +1,8 @@ package cn.tursom.utils.coroutine -import cn.tursom.core.allFields -import cn.tursom.core.cast -import kotlin.coroutines.Continuation import kotlin.coroutines.coroutineContext +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine val testCoroutineLocal = CoroutineLocal() @@ -12,47 +11,17 @@ suspend fun testCustomContext() { testInlineCustomContext() } -fun Any.printMsg() { - javaClass.allFields.forEach { - it.isAccessible = true - println("${it.type} ${it.name} = ${it.get(this)}") - val value: Any? = it.get(this) - println("${value?.javaClass} $value") - println(it.get(this) == this) - if (it.name == "completion") { - println((value as Continuation<*>).context) - } - println() - } -} - -val BaseContinuationImpl = Class.forName("kotlin.coroutines.jvm.internal.BaseContinuationImpl") -val BaseContinuationImplCompletion = BaseContinuationImpl.getDeclaredField("completion").apply { isAccessible = true } - -fun Continuation<*>.rootCompletion(): Continuation<*> { - var completion = this.javaClass.allFields.firstOrNull { it.name == "completion" } - val coroutineLocalContext = CoroutineLocalContext() - @Suppress("NAME_SHADOWING") var continuation = this - while (completion != null) { - continuation.injectCoroutineLocalContext(coroutineLocalContext) - completion.isAccessible = true - val newContinuation = completion.get(continuation)?.cast>() ?: return continuation - if (newContinuation == continuation) { - return continuation - } - completion = newContinuation.javaClass.allFields.firstOrNull { it.name == "completion" } - continuation = newContinuation - } - continuation.injectCoroutineLocalContext(coroutineLocalContext) - return continuation -} - -suspend inline fun testInlineCustomContext() { +suspend fun testInlineCustomContext() { println(coroutineContext) println("===================") } suspend fun main() { + println(getContinuation()) + suspendCoroutine { cont -> + println(cont) + cont.resume(0) + } testCustomContext() println(testCoroutineLocal.get()) testInlineCustomContext() From e5c7633f726e4ab49753bf452b452963ca6c1ab5 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 22:50:12 +0800 Subject: [PATCH 09/15] =?UTF-8?q?=E6=9B=B4=E5=8A=A0=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E6=9C=AC=E5=9C=B0=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/cn/tursom/utils/coroutine/utils.kt | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index ab791e2..3eeb81a 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -157,7 +157,18 @@ suspend inline fun getContinuation(): Continuation<*> { return (getContinuation.cast Continuation<*>>()).invoke() } -suspend inline fun injectCoroutineLocalContext(coroutineLocalContext: CoroutineLocalContext = CoroutineLocalContext()): Boolean { +suspend fun injectCoroutineContext( + coroutineContext: CoroutineContext, + key: CoroutineContext.Key? = null +): Boolean { + return if (key == null || coroutineContext[key] == null) { + getContinuation().injectCoroutineContext(coroutineContext, key) + } else { + true + } +} + +suspend fun injectCoroutineLocalContext(coroutineLocalContext: CoroutineLocalContext? = null): Boolean { return if (coroutineContext[CoroutineLocalContext] == null) { getContinuation().injectCoroutineLocalContext(coroutineLocalContext) } else { @@ -165,13 +176,25 @@ suspend inline fun injectCoroutineLocalContext(coroutineLocalContext: CoroutineL } } +fun Continuation<*>.injectCoroutineLocalContext( + coroutineLocalContext: CoroutineLocalContext? = null +): Boolean { + return if (context[CoroutineLocalContext] == null) { + injectCoroutineContext(coroutineLocalContext ?: CoroutineLocalContext(), CoroutineLocalContext) + } else { + true + } +} + private val BaseContinuationImpl = Class.forName("kotlin.coroutines.jvm.internal.BaseContinuationImpl") private val BaseContinuationImplCompletion = BaseContinuationImpl.getDeclaredField("completion").apply { isAccessible = true } - -fun Continuation<*>.injectCoroutineLocalContext(coroutineLocalContext: CoroutineLocalContext = CoroutineLocalContext()): Boolean { - if (context[CoroutineLocalContext] != null) return true +fun Continuation<*>.injectCoroutineContext( + coroutineContext: CoroutineContext, + key: CoroutineContext.Key? = null +): Boolean { + if (key != null && context[key] != null) return true if (BaseContinuationImpl.isInstance(this)) { - BaseContinuationImplCompletion.get(this).cast>().injectCoroutineLocalContext(coroutineLocalContext) + BaseContinuationImplCompletion.get(this).cast>().injectCoroutineContext(coroutineContext, key) } combinedContext(context) if (context[CoroutineLocalContext] != null) return true @@ -180,8 +203,7 @@ fun Continuation<*>.injectCoroutineLocalContext(coroutineLocalContext: Coroutine return@forAllFields } it.isAccessible = true - val coroutineContext = it.get(this).cast() - it.set(this, coroutineContext + coroutineLocalContext) + it.set(this, it.get(this).cast() + coroutineContext) } return context[CoroutineLocalContext] != null } From 71ce18234b7eb30523463868b1b9cb8451d3eb73 Mon Sep 17 00:00:00 2001 From: tursom Date: Sat, 18 Jul 2020 23:23:15 +0800 Subject: [PATCH 10/15] add MainDispatcher to replace Dispatchers.Main --- .../tursom/utils/coroutine/MainDispatcher.kt | 36 +++++++++++++++++++ .../utils/coroutine/CoroutineLocalTest.kt | 21 +++++------ 2 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt new file mode 100644 index 0000000..d8119a9 --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt @@ -0,0 +1,36 @@ +package cn.tursom.utils.coroutine + +import kotlinx.coroutines.MainCoroutineDispatcher +import kotlinx.coroutines.asCoroutineDispatcher +import java.io.Closeable +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import java.util.concurrent.Executors +import kotlin.concurrent.thread +import kotlin.coroutines.CoroutineContext + +object MainDispatcher : MainCoroutineDispatcher(), Closeable { + private val dispatcher = Executors.newSingleThreadExecutor { + thread(start = false, block = it::run, name = "mainDispatcher", isDaemon = false) + }.asCoroutineDispatcher() + + fun init() { + val mainDispatcherLoader = Class.forName("kotlinx.coroutines.internal.MainDispatcherLoader") + val dispatcher = mainDispatcherLoader.getDeclaredField("dispatcher") + dispatcher.isAccessible = true + val mf: Field = Field::class.java.getDeclaredField("modifiers") + mf.isAccessible = true + mf.setInt(dispatcher, dispatcher.modifiers and Modifier.FINAL.inv()) + dispatcher.set(null, this) + } + + override val immediate: MainCoroutineDispatcher get() = this + + override fun dispatch(context: CoroutineContext, block: Runnable) { + dispatcher.dispatch(context, block) + } + + override fun close() { + dispatcher.close() + } +} \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index c80f630..fe319b3 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,8 +1,11 @@ package cn.tursom.utils.coroutine +import cn.tursom.core.cast +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import java.io.Closeable import kotlin.coroutines.coroutineContext -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine val testCoroutineLocal = CoroutineLocal() @@ -16,13 +19,11 @@ suspend fun testInlineCustomContext() { println("===================") } -suspend fun main() { - println(getContinuation()) - suspendCoroutine { cont -> - println(cont) - cont.resume(0) +fun main() { + MainDispatcher.init() + GlobalScope.launch(Dispatchers.Main) { + println(Thread.currentThread().name) + }.invokeOnCompletion { + Dispatchers.Main.cast().close() } - testCustomContext() - println(testCoroutineLocal.get()) - testInlineCustomContext() } \ No newline at end of file From 604f87ff66a508e1ee45381d51cc6ce993bfb48b Mon Sep 17 00:00:00 2001 From: tursom Date: Sun, 19 Jul 2020 02:47:10 +0800 Subject: [PATCH 11/15] add ConverterFactory --- utils/async-http/build.gradle | 18 +- .../cn/tursom/http/HtmlConverterFactory.kt | 53 ++++++ .../cn/tursom/http/StringConverterFactory.kt | 47 +++++ .../cn/tursom/http/XmlConverterFactory.kt | 51 +++++ .../cn/tursom/utils/AsyncHttpRequest.kt | 10 +- .../src/test/kotlin/cn/tursom/http/test.kt | 180 ++++++++++++++++++ .../utils/coroutine/CoroutineLocalTest.kt | 38 ++-- utils/xml/build.gradle | 4 +- 8 files changed, 380 insertions(+), 21 deletions(-) create mode 100644 utils/async-http/src/main/kotlin/cn/tursom/http/HtmlConverterFactory.kt create mode 100644 utils/async-http/src/main/kotlin/cn/tursom/http/StringConverterFactory.kt create mode 100644 utils/async-http/src/main/kotlin/cn/tursom/http/XmlConverterFactory.kt create mode 100644 utils/async-http/src/test/kotlin/cn/tursom/http/test.kt diff --git a/utils/async-http/build.gradle b/utils/async-http/build.gradle index a5707ae..532426e 100644 --- a/utils/async-http/build.gradle +++ b/utils/async-http/build.gradle @@ -1,6 +1,20 @@ dependencies { + implementation project(":") + implementation project(":utils") + api project(":utils:xml") + // kotlin 协程 - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' + //implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' + // kotlin 反射 + //implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" // OkHttp - implementation("com.squareup.okhttp3:okhttp:3.14.1") + //implementation("com.squareup.okhttp3:okhttp:3.14.1") + //implementation group: 'cglib', name: 'cglib', version: '3.3.0' + // https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson + implementation group: 'com.squareup.retrofit2', name: 'converter-gson', version: '2.9.0' + // https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit + implementation group: 'com.squareup.retrofit2', name: 'retrofit', version: '2.9.0' + + // https://mvnrepository.com/artifact/org.jsoup/jsoup + api group: 'org.jsoup', name: 'jsoup', version: '1.13.1' } diff --git a/utils/async-http/src/main/kotlin/cn/tursom/http/HtmlConverterFactory.kt b/utils/async-http/src/main/kotlin/cn/tursom/http/HtmlConverterFactory.kt new file mode 100644 index 0000000..4711920 --- /dev/null +++ b/utils/async-http/src/main/kotlin/cn/tursom/http/HtmlConverterFactory.kt @@ -0,0 +1,53 @@ +package cn.tursom.http + +import cn.tursom.core.isInheritanceFrom +import okhttp3.MediaType +import okhttp3.RequestBody +import okhttp3.ResponseBody +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.nodes.Node +import retrofit2.Converter +import retrofit2.Retrofit +import java.lang.reflect.Type + +object HtmlConverterFactory : Converter.Factory() { + override fun responseBodyConverter( + type: Type, + annotations: Array, + retrofit: Retrofit + ): Converter? { + return if (type is Class<*> && Document::class.java.isInheritanceFrom(type)) { + DocumentResponseBodyConverter(retrofit.baseUrl().uri().toString()) + } else { + null + } + } + + override fun requestBodyConverter( + type: Type, + parameterAnnotations: Array, + methodAnnotations: Array, + retrofit: Retrofit + ): Converter? { + return if (type is Class<*> && type::class.java.isInheritanceFrom(Node::class.java)) { + NodeRequestBodyConverter + } else { + null + } + } + + class DocumentResponseBodyConverter( + private val baseUri: String + ) : Converter { + override fun convert(value: ResponseBody): Document { + return Jsoup.parse(value.string(), baseUri) + } + } + + object NodeRequestBodyConverter : Converter { + override fun convert(value: Node): RequestBody { + return RequestBody.create(MediaType.parse("text/html; charset=utf-8"), value.outerHtml()) + } + } +} \ No newline at end of file diff --git a/utils/async-http/src/main/kotlin/cn/tursom/http/StringConverterFactory.kt b/utils/async-http/src/main/kotlin/cn/tursom/http/StringConverterFactory.kt new file mode 100644 index 0000000..2baa5a4 --- /dev/null +++ b/utils/async-http/src/main/kotlin/cn/tursom/http/StringConverterFactory.kt @@ -0,0 +1,47 @@ +package cn.tursom.http + +import okhttp3.MediaType +import okhttp3.RequestBody +import okhttp3.ResponseBody +import retrofit2.Converter +import retrofit2.Retrofit +import java.lang.reflect.Type + +object StringConverterFactory : Converter.Factory() { + override fun responseBodyConverter( + type: Type, + annotations: Array, + retrofit: Retrofit + ): Converter? { + return if (type == String::class.java) { + StringResponseBodyConverter + } else { + null + } + } + + override fun requestBodyConverter( + type: Type, + parameterAnnotations: Array, + methodAnnotations: Array, + retrofit: Retrofit + ): Converter<*, RequestBody>? { + return if (type == String::class.java) { + StringRequestBodyConverter + } else { + null + } + } + + object StringResponseBodyConverter : Converter { + override fun convert(value: ResponseBody): String? { + return value.string() + } + } + + object StringRequestBodyConverter : Converter { + override fun convert(value: String): RequestBody { + return RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), value) + } + } +} \ No newline at end of file diff --git a/utils/async-http/src/main/kotlin/cn/tursom/http/XmlConverterFactory.kt b/utils/async-http/src/main/kotlin/cn/tursom/http/XmlConverterFactory.kt new file mode 100644 index 0000000..c1f2ae4 --- /dev/null +++ b/utils/async-http/src/main/kotlin/cn/tursom/http/XmlConverterFactory.kt @@ -0,0 +1,51 @@ +package cn.tursom.http + +import cn.tursom.core.isInheritanceFrom +import cn.tursom.utils.xml.Xml +import okhttp3.MediaType +import okhttp3.RequestBody +import okhttp3.ResponseBody +import org.dom4j.Document +import org.dom4j.Node +import retrofit2.Converter +import retrofit2.Retrofit +import java.lang.reflect.Type + +object XmlConverterFactory : Converter.Factory() { + override fun responseBodyConverter( + type: Type, + annotations: Array, + retrofit: Retrofit + ): Converter? { + return if (type is Class<*> && Document::class.java.isInheritanceFrom(type)) { + DocumentResponseBodyConverter + } else { + null + } + } + + override fun requestBodyConverter( + type: Type, + parameterAnnotations: Array, + methodAnnotations: Array, + retrofit: Retrofit + ): Converter? { + return if (type is Class<*> && type.isInheritanceFrom(Node::class.java)) { + NodeRequestBodyConverter + } else { + null + } + } + + object DocumentResponseBodyConverter : Converter { + override fun convert(value: ResponseBody): Document { + return Xml.saxReader.read(value.string().reader()) + } + } + + object NodeRequestBodyConverter : Converter { + override fun convert(value: Node): RequestBody { + return RequestBody.create(MediaType.parse("text/xml; charset=utf-8"), value.asXML()) + } + } +} \ No newline at end of file diff --git a/utils/async-http/src/main/kotlin/cn/tursom/utils/AsyncHttpRequest.kt b/utils/async-http/src/main/kotlin/cn/tursom/utils/AsyncHttpRequest.kt index 5d229bb..ecbb6ef 100644 --- a/utils/async-http/src/main/kotlin/cn/tursom/utils/AsyncHttpRequest.kt +++ b/utils/async-http/src/main/kotlin/cn/tursom/utils/AsyncHttpRequest.kt @@ -14,15 +14,13 @@ import kotlin.coroutines.suspendCoroutine @Suppress("unused", "MemberVisibilityCanBePrivate") object AsyncHttpRequest { - val defaultClient: OkHttpClient = OkHttpClient().newBuilder() .retryOnConnectionFailure(true) .build() val socketClient: OkHttpClient = proxyClient() val httpProxyClient: OkHttpClient = proxyClient(port = 8080, type = Proxy.Type.HTTP) - - + fun proxyClient( host: String = "127.0.0.1", port: Int = 1080, @@ -31,13 +29,13 @@ object AsyncHttpRequest { .proxy(Proxy(type, InetSocketAddress(host, port) as SocketAddress)) .retryOnConnectionFailure(true) .build() - - private suspend fun sendRequest(call: Call): Response = suspendCoroutine { + + suspend fun sendRequest(call: Call): Response = suspendCoroutine { call.enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { it.resumeWithException(e) } - + override fun onResponse(call: Call, response: Response) { it.resume(response) } diff --git a/utils/async-http/src/test/kotlin/cn/tursom/http/test.kt b/utils/async-http/src/test/kotlin/cn/tursom/http/test.kt new file mode 100644 index 0000000..9b2573b --- /dev/null +++ b/utils/async-http/src/test/kotlin/cn/tursom/http/test.kt @@ -0,0 +1,180 @@ +package cn.tursom.http + +import cn.tursom.utils.gson +import org.jsoup.nodes.Document +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.create +import retrofit2.http.GET +import retrofit2.http.Path + +interface CoroutineLocalTest { + @GET("/") + suspend fun test(): Document + + @GET("/status") + suspend fun status(): List + + @GET("status/{db}") + suspend fun status(@Path("db") db: String): RoomStatus +} + +suspend fun main() { + val retrofit = Retrofit.Builder() + //.baseUrl("http://tursom.cn:15015") + .baseUrl("https://www.baidu.com") + .addConverterFactory(StringConverterFactory) + .addConverterFactory(HtmlConverterFactory) + .addConverterFactory(XmlConverterFactory) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() + val coroutineLocalTest: CoroutineLocalTest = retrofit.create() + println(coroutineLocalTest.test()) + //println(coroutineLocalTest.status()) + //println(coroutineLocalTest.status("wula")) +} + +data class RoomStatus( + val connected: Boolean, + val db: String, + val liveUser: LiveUser, + val living: Boolean, + val recvCount: Int, + val roomId: Int, + val roomInfo: RoomInfo, + val totalCount: Int +) + +data class LiveUser( + val info: Info, + val level: Level, + val san: Int +) + +data class RoomInfo( + val allow_change_area_time: Int, + val allow_upload_cover_time: Int, + val area_id: Int, + val area_name: String, + val area_pendants: String, + val attention: Int, + val background: String, + val battle_id: Int, + val description: String, + val hot_words: List, + val hot_words_status: Int, + val is_anchor: Int, + val is_portrait: Boolean, + val is_strict_room: Boolean, + val keyframe: String, + val live_status: Int, + val live_time: String, + val new_pendants: NewPendants, + val old_area_id: Int, + val online: Int, + val parent_area_id: Int, + val parent_area_name: String, + val pendants: String, + val pk_id: Int, + val pk_status: Int, + val room_id: Int, + val room_silent_level: Int, + val room_silent_second: Int, + val room_silent_type: String, + val short_id: Int, + val studio_info: StudioInfo, + val tags: String, + val title: String, + val uid: Int, + val up_session: String, + val user_cover: String, + val verify: String +) + +data class Info( + val face: String, + val gender: Int, + val identification: Int, + val mobile_verify: Int, + val official_verify: OfficialVerify, + val platform_user_level: Int, + val rank: String, + val uid: Int, + val uname: String, + val vip_type: Int +) + +data class Level( + val anchor_score: Int, + val color: Int, + val cost: Int, + val master_level: MasterLevel, + val rcost: Long, + val svip: Int, + val svip_time: String, + val uid: Int, + val update_time: String, + val user_level: Int, + val user_score: String, + val vip: Int, + val vip_time: String +) + +data class OfficialVerify( + val desc: String, + val role: Int, + val type: Int +) + +data class MasterLevel( + val anchor_score: Int, + val color: Int, + val current: List, + val level: Int, + val master_level_color: Int, + val next: List, + val sort: String, + val upgrade_score: Int +) + +data class NewPendants( + val badge: Badge, + val frame: Frame, + val mobile_frame: MobileFrame +) + +data class StudioInfo( + val master_list: List, + val status: Int +) + +data class Badge( + val desc: String, + val name: String, + val position: Double, + val value: String +) + +data class Frame( + val area: Int, + val area_old: Int, + val bg_color: String, + val bg_pic: String, + val desc: String, + val name: String, + val position: Int, + val use_old_area: Boolean, + val value: String +) + +data class MobileFrame( + val area: Int, + val area_old: Int, + val bg_color: String, + val bg_pic: String, + val desc: String, + val name: String, + val position: Int, + val use_old_area: Boolean, + val value: String +) \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index fe319b3..a91cd80 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,10 +1,6 @@ package cn.tursom.utils.coroutine -import cn.tursom.core.cast -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import java.io.Closeable +import kotlinx.coroutines.* import kotlin.coroutines.coroutineContext val testCoroutineLocal = CoroutineLocal() @@ -19,11 +15,31 @@ suspend fun testInlineCustomContext() { println("===================") } -fun main() { - MainDispatcher.init() - GlobalScope.launch(Dispatchers.Main) { - println(Thread.currentThread().name) - }.invokeOnCompletion { - Dispatchers.Main.cast().close() +annotation class Request(val url: String, val method: String = "GET") + +interface CoroutineLocalTest { + @Request("http://tursom.cn:15015/living") + suspend fun test(): String +} + +class Test : CoroutineScope by MainScope() { + suspend fun test(): Job { + println(this) + println(coroutineContext) + return coroutineScope { + println(this) + println(coroutineContext) + println(Thread.currentThread().name) + delay(1) + return@coroutineScope launch { + println(Thread.currentThread().name) + } + } } +} + +suspend fun main() { + MainDispatcher.init() + Test().test().join() + MainDispatcher.close() } \ No newline at end of file diff --git a/utils/xml/build.gradle b/utils/xml/build.gradle index 9c0fc7f..1734507 100644 --- a/utils/xml/build.gradle +++ b/utils/xml/build.gradle @@ -1,4 +1,4 @@ dependencies { - // 解析XML - implementation group: 'dom4j', name: 'dom4j', version: '1.6.1' + // 解析XML https://mvnrepository.com/artifact/org.dom4j/dom4j + compile group: 'org.dom4j', name: 'dom4j', version: '2.1.3' } From 43e9e7ed3ee7c17942fcde334d239970a37e97c3 Mon Sep 17 00:00:00 2001 From: tursom Date: Sun, 19 Jul 2020 04:24:36 +0800 Subject: [PATCH 12/15] add BlockingCallAdapterFactory --- src/main/kotlin/cn/tursom/core/Tools.kt | 7 +++++ utils/async-http/build.gradle | 4 +-- .../tursom/http/BlockingCallAdapterFactory.kt | 20 ++++++++++++++ .../src/test/kotlin/cn/tursom/http/test.kt | 26 +++++++++++++++---- 4 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt diff --git a/src/main/kotlin/cn/tursom/core/Tools.kt b/src/main/kotlin/cn/tursom/core/Tools.kt index f4ce86f..0837148 100644 --- a/src/main/kotlin/cn/tursom/core/Tools.kt +++ b/src/main/kotlin/cn/tursom/core/Tools.kt @@ -398,4 +398,11 @@ fun Class<*>.forAllFields(action: (Field) -> Unit) { clazz = clazz.superclass } clazz.declaredFields.forEach(action) +} + +fun Iterable.firstNotNull(selector: (T) -> R): R? { + forEach { + return selector(it) ?: return@forEach + } + return null } \ No newline at end of file diff --git a/utils/async-http/build.gradle b/utils/async-http/build.gradle index 532426e..5758c26 100644 --- a/utils/async-http/build.gradle +++ b/utils/async-http/build.gradle @@ -11,9 +11,9 @@ dependencies { //implementation("com.squareup.okhttp3:okhttp:3.14.1") //implementation group: 'cglib', name: 'cglib', version: '3.3.0' // https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson - implementation group: 'com.squareup.retrofit2', name: 'converter-gson', version: '2.9.0' + api group: 'com.squareup.retrofit2', name: 'converter-gson', version: '2.9.0' // https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit - implementation group: 'com.squareup.retrofit2', name: 'retrofit', version: '2.9.0' + compile group: 'com.squareup.retrofit2', name: 'retrofit', version: '2.9.0' // https://mvnrepository.com/artifact/org.jsoup/jsoup api group: 'org.jsoup', name: 'jsoup', version: '1.13.1' diff --git a/utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt b/utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt new file mode 100644 index 0000000..f087536 --- /dev/null +++ b/utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt @@ -0,0 +1,20 @@ +package cn.tursom.http + +import retrofit2.Call +import retrofit2.CallAdapter +import retrofit2.Retrofit +import java.lang.reflect.Type + +object BlockingCallAdapterFactory : CallAdapter.Factory() { + override fun get( + returnType: Type, + annotations: Array, + retrofit: Retrofit + ): CallAdapter<*, *>? { + if (annotations.any { it is retrofit2.SkipCallbackExecutor }) return null + return object : CallAdapter { + override fun responseType(): Type = returnType + override fun adapt(call: Call): Any? = call.execute().body() + } + } +} \ No newline at end of file diff --git a/utils/async-http/src/test/kotlin/cn/tursom/http/test.kt b/utils/async-http/src/test/kotlin/cn/tursom/http/test.kt index 9b2573b..67e5945 100644 --- a/utils/async-http/src/test/kotlin/cn/tursom/http/test.kt +++ b/utils/async-http/src/test/kotlin/cn/tursom/http/test.kt @@ -1,12 +1,18 @@ package cn.tursom.http +import cn.tursom.utils.coroutine.MainDispatcher import cn.tursom.utils.gson +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import org.jsoup.nodes.Document import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.create import retrofit2.http.GET import retrofit2.http.Path +import retrofit2.http.Query + interface CoroutineLocalTest { @GET("/") @@ -17,21 +23,31 @@ interface CoroutineLocalTest { @GET("status/{db}") suspend fun status(@Path("db") db: String): RoomStatus + + @GET("room/v1/Room/get_info") + suspend fun getRoomInfo(@Query("id") roomId: Int, @Query("from") from: String = "room"): String } suspend fun main() { + MainDispatcher.init() val retrofit = Retrofit.Builder() //.baseUrl("http://tursom.cn:15015") - .baseUrl("https://www.baidu.com") + //.baseUrl("https://www.baidu.com") + .baseUrl("https://api.live.bilibili.com") + .addCallAdapterFactory(BlockingCallAdapterFactory) .addConverterFactory(StringConverterFactory) .addConverterFactory(HtmlConverterFactory) .addConverterFactory(XmlConverterFactory) .addConverterFactory(GsonConverterFactory.create(gson)) .build() - val coroutineLocalTest: CoroutineLocalTest = retrofit.create() - println(coroutineLocalTest.test()) - //println(coroutineLocalTest.status()) - //println(coroutineLocalTest.status("wula")) + GlobalScope.launch(Dispatchers.Main) { + launch { + println(Thread.currentThread().name) + } + val coroutineLocalTest: CoroutineLocalTest = retrofit.create() + //println(coroutineLocalTest.status()) + println(coroutineLocalTest.getRoomInfo(801580)) + }.join() } data class RoomStatus( From a1046bca98f9c4f1abeb3709de044d8c0752616b Mon Sep 17 00:00:00 2001 From: tursom Date: Sun, 19 Jul 2020 12:11:24 +0800 Subject: [PATCH 13/15] update BlockingCallAdapterFactory --- .../kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt b/utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt index f087536..c2ad949 100644 --- a/utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt +++ b/utils/async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt @@ -4,17 +4,20 @@ import retrofit2.Call import retrofit2.CallAdapter import retrofit2.Retrofit import java.lang.reflect.Type +import java.util.concurrent.CompletableFuture object BlockingCallAdapterFactory : CallAdapter.Factory() { override fun get( returnType: Type, annotations: Array, retrofit: Retrofit - ): CallAdapter<*, *>? { + ): CallAdapter? { + if (getRawType(returnType) == Call::class.java) return null + if (getRawType(returnType) == CompletableFuture::class.java) return null if (annotations.any { it is retrofit2.SkipCallbackExecutor }) return null - return object : CallAdapter { + return object : CallAdapter { override fun responseType(): Type = returnType - override fun adapt(call: Call): Any? = call.execute().body() + override fun adapt(call: Call): Any? = call.execute().body() } } } \ No newline at end of file From b26f89ff5ef850aebfc9605b954cfa5eacb43d2a Mon Sep 17 00:00:00 2001 From: tursom Date: Sun, 19 Jul 2020 13:11:44 +0800 Subject: [PATCH 14/15] add MainCoroutineDispatcherFactory --- .../tursom/utils/coroutine/CoroutineLocal.kt | 3 ++ .../MainCoroutineDispatcherFactory.kt | 13 +++++++++ .../tursom/utils/coroutine/MainDispatcher.kt | 29 ++++++++++++++----- .../kotlin/cn/tursom/utils/coroutine/utils.kt | 8 +++++ ....coroutines.internal.MainDispatcherFactory | 1 + .../utils/coroutine/CoroutineLocalTest.kt | 26 ++++++++++------- 6 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 utils/src/main/kotlin/cn/tursom/utils/coroutine/MainCoroutineDispatcherFactory.kt create mode 100644 utils/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt index 9a298ce..e3e3bb9 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocal.kt @@ -1,6 +1,7 @@ package cn.tursom.utils.coroutine import cn.tursom.core.cast +import cn.tursom.core.toHexString import kotlinx.coroutines.Job import java.util.concurrent.ConcurrentHashMap import kotlin.coroutines.CoroutineContext @@ -43,6 +44,8 @@ open class CoroutineLocal { return true } + override fun toString(): String = "CoroutineLocal@${hashCode().toHexString(false)}" + companion object { private val attachMap = ConcurrentHashMap, Any?>>() override fun toString(): String = attachMap.toString() diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainCoroutineDispatcherFactory.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainCoroutineDispatcherFactory.kt new file mode 100644 index 0000000..3ad4214 --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainCoroutineDispatcherFactory.kt @@ -0,0 +1,13 @@ +package cn.tursom.utils.coroutine + +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.MainCoroutineDispatcher +import kotlinx.coroutines.internal.MainDispatcherFactory + +@Suppress("unused") +@InternalCoroutinesApi +class MainCoroutineDispatcherFactory : MainDispatcherFactory { + override val loadPriority: Int = 1 + + override fun createDispatcher(allFactories: List): MainCoroutineDispatcher = MainDispatcher +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt index d8119a9..8847e1a 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/MainDispatcher.kt @@ -1,27 +1,42 @@ package cn.tursom.utils.coroutine +import cn.tursom.core.cast +import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.MainCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher import java.io.Closeable import java.lang.reflect.Field import java.lang.reflect.Modifier import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicBoolean import kotlin.concurrent.thread import kotlin.coroutines.CoroutineContext object MainDispatcher : MainCoroutineDispatcher(), Closeable { - private val dispatcher = Executors.newSingleThreadExecutor { - thread(start = false, block = it::run, name = "mainDispatcher", isDaemon = false) + private val loaded = AtomicBoolean(false) + private val dispatcher: ExecutorCoroutineDispatcher = Executors.newSingleThreadExecutor { + thread(start = false, block = it::run, name = "MainDispatcher", isDaemon = false) }.asCoroutineDispatcher() - - fun init() { - val mainDispatcherLoader = Class.forName("kotlinx.coroutines.internal.MainDispatcherLoader") - val dispatcher = mainDispatcherLoader.getDeclaredField("dispatcher") + private var oldDispatcher: MainCoroutineDispatcher? = null + private val mainDispatcherLoader: Class<*> = Class.forName("kotlinx.coroutines.internal.MainDispatcherLoader") + private val dispatcherField: Field = mainDispatcherLoader.getDeclaredField("dispatcher").also { dispatcher -> dispatcher.isAccessible = true val mf: Field = Field::class.java.getDeclaredField("modifiers") mf.isAccessible = true mf.setInt(dispatcher, dispatcher.modifiers and Modifier.FINAL.inv()) - dispatcher.set(null, this) + } + + fun init() { + if (loaded.compareAndSet(false, true)) { + oldDispatcher = dispatcherField.get(null).cast() + dispatcherField.set(null, this) + } + } + + fun resume() { + if (loaded.compareAndSet(true, false) && oldDispatcher != null) { + dispatcherField.set(null, oldDispatcher) + } } override val immediate: MainCoroutineDispatcher get() = this diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index 3eeb81a..faee0a1 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -218,4 +218,12 @@ fun combinedContext(coroutineContext: CoroutineContext): Boolean { left.set(coroutineContext, leftObj + CoroutineLocalContext()) } return true +} + +//fun CoroutineScope.runOnUiThread(action: suspend CoroutineScope.() -> Unit): Job { +// return launch(Dispatchers.Main, block = action) +//} + +suspend fun runOnUiThread(action: suspend CoroutineScope.() -> T): T { + return withContext(Dispatchers.Main, action) } \ No newline at end of file diff --git a/utils/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory b/utils/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory new file mode 100644 index 0000000..e4f9d18 --- /dev/null +++ b/utils/src/main/resources/META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory @@ -0,0 +1 @@ +cn.tursom.utils.coroutine.MainDispatcherFactory \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index a91cd80..13bdd73 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -23,23 +23,29 @@ interface CoroutineLocalTest { } class Test : CoroutineScope by MainScope() { - suspend fun test(): Job { - println(this) + suspend fun test() { + testCoroutineLocal.set(1) println(coroutineContext) - return coroutineScope { - println(this) + coroutineScope { println(coroutineContext) - println(Thread.currentThread().name) delay(1) - return@coroutineScope launch { - println(Thread.currentThread().name) - } } } } suspend fun main() { - MainDispatcher.init() - Test().test().join() + //MainDispatcher.init() + runOnUiThread { + Test().test() + println(testCoroutineLocal.get()) + println(coroutineContext) + GlobalScope.launch(Dispatchers.Main) { + println(coroutineContext) + } + //runOnUiThread { + // println(coroutineContext) + // println(testCoroutineLocal.get()) + //} + } MainDispatcher.close() } \ No newline at end of file From d2e5b72391627bc1cc37b62341deb7cb7435b0ce Mon Sep 17 00:00:00 2001 From: tursom Date: Sun, 19 Jul 2020 21:43:29 +0800 Subject: [PATCH 15/15] update CoroutineLocalContext --- .../utils/coroutine/CoroutineLocalContext.kt | 38 ++++++++++++++-- .../kotlin/cn/tursom/utils/coroutine/utils.kt | 44 +++++++++---------- .../utils/coroutine/CoroutineLocalTest.kt | 23 +++++++--- 3 files changed, 73 insertions(+), 32 deletions(-) diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt index 849e807..1ac3742 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/CoroutineLocalContext.kt @@ -1,15 +1,45 @@ package cn.tursom.utils.coroutine +import cn.tursom.core.cast +import kotlinx.coroutines.ThreadContextElement import kotlin.coroutines.CoroutineContext open class CoroutineLocalContext( - private val map: MutableMap, Any?> = HashMap(4) -) : CoroutineContext.Element, MutableMap, Any?> by map { - override val key: CoroutineContext.Key<*> get() = Companion + private val mapBuilder: () -> MutableMap, Any?> = { HashMap(4) } +) : CoroutineContext.Element, ThreadContextElement, Any?>>, MutableMap, Any?> { + override val key: CoroutineContext.Key<*> get() = Key + private var map: MutableMap, Any?> = mapBuilder() override fun toString(): String { return "CoroutineLocalContext$map" } - companion object : CoroutineContext.Key + companion object Key : CoroutineContext.Key + + override fun restoreThreadContext(context: CoroutineContext, oldState: MutableMap, Any?>) { + map = oldState + } + + override fun updateThreadContext(context: CoroutineContext): MutableMap, Any?> { + val oldState = map + map = mapBuilder() + return oldState + } + + @JvmName("getTyped") + operator fun get(key: CoroutineLocal): T? = map[key].cast() + operator fun set(key: CoroutineLocal, value: T?): T? = map.put(key, value).cast() + + override val size: Int get() = map.size + override fun containsKey(key: CoroutineLocal<*>): Boolean = map.containsKey(key) + override fun containsValue(value: Any?): Boolean = map.containsValue(value) + override fun get(key: CoroutineLocal<*>): Any? = map[key] + override fun isEmpty(): Boolean = map.isEmpty() + override val entries: MutableSet, Any?>> get() = map.entries + override val keys: MutableSet> get() = map.keys + override val values: MutableCollection get() = map.values + override fun clear() = map.clear() + override fun put(key: CoroutineLocal<*>, value: Any?): Any? = map.put(key, value) + override fun putAll(from: Map, Any?>) = map.putAll(from) + override fun remove(key: CoroutineLocal<*>): Any? = map.remove(key) } \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt index faee0a1..a64500b 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/coroutine/utils.kt @@ -15,36 +15,36 @@ import kotlin.coroutines.coroutineContext fun CoroutineScope.launchWithCoroutineLocalContext( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> Unit ): Job { - return launch(context + CoroutineLocalContext(map), start, block) + return launch(context + CoroutineLocalContext(mapBuilder), start, block) } @Suppress("DeferredIsResult") fun CoroutineScope.asyncWithCoroutineLocalContext( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> T ): Deferred { - return async(context + CoroutineLocalContext(map), start, block) + return async(context + CoroutineLocalContext(mapBuilder), start, block) } suspend fun withCoroutineLocalContext( - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> T ): T { - return withContext(CoroutineLocalContext(map), block) + return withContext(CoroutineLocalContext(mapBuilder), block) } @Throws(InterruptedException::class) fun runBlockingWithCoroutineLocalContext( context: CoroutineContext = EmptyCoroutineContext, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> T ): T { - return runBlocking(context + CoroutineLocalContext(map), block) + return runBlocking(context + CoroutineLocalContext(mapBuilder), block) } fun CoroutineScope.launchWithCoroutineScopeContext( @@ -67,29 +67,29 @@ fun CoroutineScope.asyncWithCoroutineScopeContext( fun CoroutineScope.launchWithCoroutineLocalAndCoroutineScopeContext( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> Unit ): Job { - return launch(context + CoroutineLocalContext(map) + CoroutineScopeContext(this), start, block) + return launch(context + CoroutineLocalContext(mapBuilder) + CoroutineScopeContext(this), start, block) } @Suppress("DeferredIsResult") fun CoroutineScope.asyncWithCoroutineLocalAndCoroutineScopeContext( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> T ): Deferred { - return async(context + CoroutineLocalContext(map) + CoroutineScopeContext(this), start, block) + return async(context + CoroutineLocalContext(mapBuilder) + CoroutineScopeContext(this), start, block) } @Throws(InterruptedException::class) fun runBlockingWithCoroutineLocalAndCoroutineScopeContext( context: CoroutineContext = EmptyCoroutineContext, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> T ): T { - return runBlocking(context + CoroutineLocalContext(map) + CoroutineScopeContext()) { + return runBlocking(context + CoroutineLocalContext(mapBuilder) + CoroutineScopeContext()) { CoroutineScopeContext set this block() } @@ -99,29 +99,29 @@ fun runBlockingWithCoroutineLocalAndCoroutineScopeContext( fun CoroutineScope.launchWithEnhanceContext( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> Unit ): Job { - return launch(context + CoroutineLocalContext(map), start, block) + return launch(context + CoroutineLocalContext(mapBuilder), start, block) } @Suppress("DeferredIsResult") fun CoroutineScope.asyncWithEnhanceContext( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> T ): Deferred { - return async(context + CoroutineLocalContext(map), start, block) + return async(context + CoroutineLocalContext(mapBuilder), start, block) } @Throws(InterruptedException::class) fun runBlockingWithEnhanceContext( context: CoroutineContext = EmptyCoroutineContext, - map: MutableMap, Any?> = HashMap(4), + mapBuilder: () -> MutableMap, Any?> = { HashMap(4) }, block: suspend CoroutineScope.() -> T ): T { - return runBlocking(context + CoroutineLocalContext(map)) { + return runBlocking(context + CoroutineLocalContext(mapBuilder)) { block() } } @@ -224,6 +224,6 @@ fun combinedContext(coroutineContext: CoroutineContext): Boolean { // return launch(Dispatchers.Main, block = action) //} -suspend fun runOnUiThread(action: suspend CoroutineScope.() -> T): T { - return withContext(Dispatchers.Main, action) +suspend fun runOnUiThread(coroutineContext: CoroutineContext = EmptyCoroutineContext, action: suspend CoroutineScope.() -> T): T { + return withContext(coroutineContext + Dispatchers.Main, action) } \ No newline at end of file diff --git a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt index 13bdd73..7d14eb8 100644 --- a/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt +++ b/utils/src/test/kotlin/cn/tursom/utils/coroutine/CoroutineLocalTest.kt @@ -1,6 +1,7 @@ package cn.tursom.utils.coroutine import kotlinx.coroutines.* +import kotlin.coroutines.ContinuationInterceptor import kotlin.coroutines.coroutineContext val testCoroutineLocal = CoroutineLocal() @@ -33,13 +34,23 @@ class Test : CoroutineScope by MainScope() { } } -suspend fun main() { - //MainDispatcher.init() - runOnUiThread { - Test().test() - println(testCoroutineLocal.get()) +object R : () -> R { + override fun invoke(): R = this +} + +val threadLocal = ThreadLocal() + +fun main() = runBlocking { + println(coroutineContext[ContinuationInterceptor]) + MainDispatcher.init() + runOnUiThread(threadLocal.asContextElement()) { + threadLocal.set("hello") + //Test().test() + //println(testCoroutineLocal.get()) + println(threadLocal.get()) println(coroutineContext) - GlobalScope.launch(Dispatchers.Main) { + launch(Dispatchers.Main) { + println(threadLocal.get()) println(coroutineContext) } //runOnUiThread {