【问题标题】:why the coroutine exception handler double the original exception?为什么协程异常处理程序是原始异常的两倍?
【发布时间】:2017-06-16 14:51:26
【问题描述】:

我实现了自己的async,我无法以正确的方式处理异常。为什么?

val expected = IllegalStateException();
val it = async<Any> {
    throw expected;
};

assert.that({ it.get() }, throws(equalTo(expected)));
//              ^--- but it throws a IllegalStateException(cause = expected)

源代码

interface Response<in T> {
    suspend fun yield(value: T);
}

interface Request<out T> {
    fun get(): T;
    fun <R> then(mapping: (T) -> R): Request<R>;
}

private val executor: ExecutorService = ForkJoinPool(20);
fun <T> async(block: suspend Response<T>.() -> Unit): Request<T> {
    return object : Request<T>, Response<T> {
        @Volatile var value: T? = null;

        var request: Continuation<Unit>? = block.createCoroutine(this, delegate {}).let {
            var task: Future<*>? = executor.submit { it.resume(Unit); };
            return@let delegate {
                try {
                    val current = task!!;
                    task = null;
                    current.get();
                } catch(e: ExecutionException) {
                    throw e.cause ?: e;
                }
            };
        };

        override fun <R> then(mapping: (T) -> R): Request<R> = async<R> {
            yield(mapping(get()));
        };

        override fun get(): T {
            return value ?: wait();
        }

        private fun wait(): T {
            val it = request!!;
            request = null;
            it.resume(Unit);
            return value!!;
        }

        suspend override fun yield(value: T) {
            this.value = value;
        }

    };
}

inline fun <T> delegate(noinline exceptional: (Throwable) -> Unit = { throw it; }, crossinline resume: (T) -> Unit): Continuation<T> {
    return object : Continuation<T> {
        override val context: CoroutineContext = EmptyCoroutineContext;


        override fun resumeWithException(exception: Throwable) {
            exceptional(exception);
        }

        override fun resume(value: T) {
            resume(value);
        }
    }
}

【问题讨论】:

    标签: asynchronous kotlin coroutine


    【解决方案1】:

    奇怪的行为来自java。 ForkJoinTask#getThrowableException 将为给定任务重新抛出异常:

    返回给定任务的可重新抛出异常,如果 可用的。提供准确堆栈跟踪,如果异常 不是由当前线程抛出的,我们尝试创建一个新的 异常与抛出的异常类型相同,但具有 记录异常作为其原因。如果没有这样的 构造函数,我们尝试使用 no-arg 构造函数, 然后是initCause,效果相同。如果这些都没有 申请,或因其他异常而失败,我们将返回 记录的异常,这仍然是正确的,尽管它可能 包含误导性堆栈跟踪。

    这意味着如果您不想为给定任务重新抛出异常,您可以非公开地创建异常构造函数,例如:

    val exception = object: IllegalStateException(){/**/};
    //                      ^--- its constructor only available in its scope 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-24
      • 2019-10-17
      • 2020-02-24
      • 2015-10-26
      • 2022-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多