为什么协程异常处理程序加倍原始异常?

时间:2017-06-16 14:51:26

标签: asynchronous kotlin coroutine

我实现了自己的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);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

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

  

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

这意味着如果您不想为请求任务重新抛出异常,则可以非公开地设置异常构造函数,例如:

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