Pull to refresh

Comments 8

Выглядит очень заманчиво, но хотелось бы больше деталей о реализации - пока что-то непонятно. Зачем классы-заглушки? Сколько их генериться? Когда? Можно же, наверное, создать руками любой стектрейс, не?

Зачем классы-заглушки?

Классы-заглушки нужны для эмуляции стека вызова. Например, рассмотрим 3 функции, которые по очереди друг друга вызывают:

suspend fun fun1() {
    fun2()
    delay(10)
}

suspend fun fun2() {
    fun3()
    delay(10)
}

suspend fun fun3() {
    delay(10)
    throw Exception()
}

Мы вызваем fun1, после этого вызываются fun2 и fun3 и корутина "засыпает".


В обычном случае после "пробуждения" будет вызвана только функция fun3 и когда сгенерируется исключение, в его трейсе будет содержаться только этот метод.

C моей библиотекой происходит по-другому: генерируется класс-заглушка, содержащий методы fun1 и fun2, fun1 вызывает fun2, а fun2 "будит" корутину. В результате стек вызова содержит все 3 метода. И стектрейс исключения содержит их все.

Сколько их генериться? 

Столько же, сколько содержится классов с suspend-функциями.

Когда?

Для класса A генерируется заглушка во время первого "пробуждения" корутины, содержащей в стеке методы класса A. После этого заглушка кешируется и повторные "пробуждения" не приводят к генерации заглушки.

Можно же, наверное, создать руками любой стектрейс, не?

Создать-то можно. Нельзя переписать стектрейс исключения, сгенерированного во время выполнения корутины.

А как вы понимаете, какая функцию какую вызывает в классе А (ведь все уже скомпилировано)? Ведь там может быть сколь угодно жуткая логика, не?

Как это будет работать вот для такого случая (для многократных вызовов fun1). Не будет ли иногда в стектрейсе лишнего fun2?

suspend fun fun1() {
    if ((0..1).random() == 0) {
      fun2("throw fun2")
    } else {
      fun3("directly fun3")
    }
    delay(10)
}

suspend fun fun2(x: String) {
    fun3(x)
    delay(10)
}

suspend fun fun3(x: String) {
    delay(10)
    throw Exception(x)
}

Для генерации заглушек мне не нужно знать, в каком порядке они будут вызываться - они могут вызывать себя как угодно, в каком угодно порядке.

Это обеспечивается за счет использования MethodHandle API. Просто, грубо говоря, при вызове метода заглушки я ему передаю массив MethodHandle[] которые нужно вызвать.

Конкретный порядок я определяю именно в момент "пробуждения" корутины. В вашем примере все будет работать

upd:
Если вопрос в том, как я определяю стек вызова во время "пробуждения" - в Kotlin stdlib есть функция для этого.

Я попробую вашу либу, которая для JVM. У меня есть бек полностью на котлине + спринг, правда он сделан на блокирующих библиотеках, но я его потихоньку мигрирую на реактивные с использованием корутин. Подключу и буду юзать, спасибо за хорошую идею

? Баги/вопросы/предложения/замечания можете оставлять в Github issue, Gitter или в личном сообщении мне)

Спасибо за статью. Очень интересная библиотека. Не рассматривали вариант с плагином для компилятора вместо генерации классов в рантайме?

Вариант с плагином не рассматривал. Но предположу, что с ним будут две проблемы:

  • API для плагинов компилятора пока не стабилизорован

  • и как быть с внешними зависимостями, которые уже скомпилированы без плагина.

Sign up to leave a comment.

Articles