- BrainTools - https://www.braintools.ru -
Приветствую вас, читатели.
Подозреваю, что вы из мира программирования. Приглашаю вас присоединиться к увлекательному рассказу о том, как один энтузиаст решил доработать свой любимый язык.
Я собираюсь рассказать об увлекательном опыте [1] внесения существенных изменений в очень сложную архитектуру кода.
Будет несколько статей, это первая.
Для тех, кто хочет весь список сразу, держите:
Позвольте представиться: я Антон, программист-даос и любитель китайского чая.
В то время как многие коллеги превращают кофе в строчки кода, я нахожу вдохновение в хорошем китайском чае.
Десять лет я совмещаю два этих искусства — завариваю чай и пишу код. Чай бывает разный, и код тоже.
Начинал с классического Java Backend, затем добавил Groovy и Kotlin, а потом пришло время моего первого фреймворка (о нём — позже).
После JVM-мира я погрузился в Эльбрус [5] и увлекательное портирование ПО на разных языках.
Но сегодня хочу рассказать о самом интересном (хотя и не самом свежем) опыте — работе с Dart [6].
Сейчас я Platform Engineer с уклоном в Go, но Dart приключения заслуживают отдельной истории.
В технических статьях редко встретишь объяснение, зачем они вообще написаны. Мне же хочется быть честным с читателем с первых строк.
Дело в том, что за 10 лет программирования у меня не было периода, когда бы я не писал код после работы или учёбы. Накопилось немало интересного материала — того, что действительно достойно публикации.
Но каждый раз, задаваясь вопросом «Зачем?», я не находил убедительного ответа.
Сейчас он появился: это попытка достучаться.
Достучаться до тех, кто верит, что OpenSource автоматически означает массовую доработку кода. Я в это не верю.
Достучаться до тех, кто не верит, что может изменить что-то важное — даже сам язык. А вот в это, в возможность изменить все, я верю крепко.
Достучаться до уставших. Любой бы устал: текущее состояние гибкости и поддерживаемости кода во Вселенной оставляет желать лучшего.
Достучаться до Dart-сообщества: ребята, пора вытаскивать Dart из фронтенд-ниши. Взгляните, на что он способен! Небольшое усилие — и он завоюет бэкенд.
Достучаться до команды Dart (вдруг кто-то прочитает):

Обожаю эту картинку — регулярно напоминаю себе её смысл.
И ещё — повод. Один мой друг сказал: «Статья — это тоже коммуникация».
Сейчас я сознательно создаю себе коммуникационный голод, чтобы подтолкнуть себя к более масштабному общению, чем переписка в чатах.
Итак, начинается цикл из трех статей о моем эксперименте с Dart Language и Dart VM.
Предыстория такова: обычный разработчик захотел добавить в язык сложную фичу.
Давайте представим: вам нравится язык, но чего-то в нем не хватает. Не магическую кнопку “сделай за меня”, а что-то технически значимое — будь то новая библиотека, синтаксический сахар, ключевое слово или даже механизм исполнения кода.
В моем случае это были корутины в Dart. И мне удалось реализовать [7] их — с кучей ограничений, но все же.
Наверняка подумали: “Ну вот, еще одна история успеха про самодельный велосипед”. Но суть не в этом. Я хочу рассказать не столько о том, что и как сделано, а о том, что я чувствовал на разных этапах — как разработчик и просто человек.
Будет много технических деталей, конечно, но фокус — на состояниях, через которые проходил.
Прежде чем начать, спросите себя: “А стоит ли мне это читать?” Помогу с ответом.
Кому может быть интересно:
Тем, кто хочет добавить фичу в язык
Тем, кто видит Dart не только как Flutter-рантайм
Любителям разбираться в компиляторах и внутренностях языков
Выгорающим разработчикам (может, отвлечетесь?)
Студентам в поисках темы для диплома
Кому, возможно, не зайдет:
Тем, кто не интересуется устройством языков
Flutter-разработчикам, которым не нужны глубины Dart (про Flutter — ни слова)
Теперь, когда все формальности соблюдены, налью пуэр и, если позволите, начну.
И имя ему — ART [8]. Так я окрестил свой первый фреймворк для Java/Kotlin.
Что умел этот зверь:
Элегантная обработка HTTP/RSocket трафика
Работа с Tarantool
Интеграция (ранее была) с реляционными БД
Взаимодействие (было) с Kafka
Динамическое применение изменений конфигураций на лету
А еще — компиляция в нативный бинарник через GraalVM [9].
Я задумал его как инструмент для чистой и гибкой разработки сервисов на Java/Kotlin. Без рефлексии, без модификаций байт-кода, без лишней инверсии контроля, без аннотаций.
Простота без потери мощности — чтобы разработчик мог легко создавать production-решения. Да, очередной велосипед, ничего сверхъестественного.
Но потом я понял: сложности есть не только в бэкенде. Фронтенд и инфраструктура тоже страдают.
Так появились art-platform [10] и art-react [11] — ещё два велосипеда в коллекцию.
Идея была проста: единый инструментарий для всего цикла разработки — от написания кода до деплоя.
Параллельно с ночными экспериментами приходилось заниматься и реальными рабочими проектами, где я столкнулся с любопытными технологиями.
Среди них были Tarantool, Golang, Koishi [12], node-fibers и ucontext — все они так или иначе связаны с элегантной абстракцией под названием корутины.
Признаюсь честно: когда я наконец разобрался, как они работают, это было озарение [13]. С тех пор их красота и эффективность не выходят у меня из головы.
Если хотите погрузиться в тему, вот хорошие отправные точки:
Общее объяснение [14]
Корутины в Kotlin [15]
Это одна из тех концепций, которые меняют взгляд на асинхронный код.
На определённом этапе разработки ART я чётко сформулировал технические требования к языку для реализации своих идей — и Java, увы, перестала им соответствовать.
Именно тогда я случайно открыл для себя Dart — и обнаружил, что он идеально ложится в мои критерии. Так закончилась моя Java-эпоха и начался Dart-путь.
Почему именно Dart? Мне нужен был язык, который:
Работает везде: нативные десктопы (Linux/Windows/macOS), мобильные платформы (iOS/Android), веб и бэкенд
Обладает выразительным синтаксисом для создания простых, но эффективных языковых конструкций
Одновременно компилируется в компактный бинарник без зависимостей и позволяет горячую перезагрузку кода при разработке
Даёт низкоуровневый доступ к нативным технологиям (C/C++/Rust) для преодоления ограничений рантайма
Обеспечивает автоматическое управление памятью [17] (в любой форме)
Рассматривал альтернативы: Go, Rust, Kotlin, Nim, Crystal, V, C/C++. Но выбор пал на Dart.
История мучительного выбора с таблицами сравнения и метаниями достойна отдельного поста — но здесь просто примем как факт: Вселенная подкинула мне Dart, и я не стал спорить.
Прежде чем погружаться в очередной фреймворк, я решил расширить экосистему Dart, создав несколько ключевых модулей:
IOUring модуль [18] — даёт доступ к мощному Linux-механизму IOUring [19]
Tarantool модуль [20] — позволяет писать на Dart вместо Lua для Tarantool [21]
RSocket модуль [22] — минимально достаточная реализация RSocket [23] протокола
После этого относительно успешного опыта велосипедостроения я подошёл к моменту, где началась настоящая магия — история “романтических” отношений между Dart и корутинами.
Мне отчаянно понадобились корутины. И я решил их реализовать.
Важно: я хочу говорить не об ассемблерных инструкциях, а о тех вызовах, с которыми сталкивается каждый разработчик в процессе создания чего-то нового.
О том, что остаётся за кадром большинства технических статей.
Осознав творческий потенциал Dart, я испытывал настоящий восторг. Этот язык открывал потрясающие возможности:
Кроссплатформенный UI через Flutter
Нативная совместимость через FFI
Векторизация вычислений через SIMD
Графика через шейдеры
Метапрограммирование через кодогенерацию
Я был полон амбиций и готовности погрузиться в совершенно новую для меня область — реализацию корутин.
Состояние было противоречивым: с одной стороны — вдохновение от безграничных возможностей, с другой — страх [24] перед внедрением новой модели исполнения кода. Но творческий азарт перевесил все сомнения.
Решимость на максимуме, пуэр заварен, IDE запущена — пора разбираться в основах.
Но сначала вопрос: что не так с родными async/await [25] и Future в Dart? Ведь они и так дают асинхронность.
Присмотримся к их работе — интуиция [26] подсказывает, что реализация корутин будет строиться на схожих принципах, но с критическими отличиями.
Создают цепочки Future — каждый await это точка разрыва потока исполнения
Требуют явной пометки async — заранее “заражают” всю цепочку вызовов
Работают через механизм событийной очереди — что иногда даёт неочевидные накладные расходы
Не имеют состояния между вызовами — каждый await это полноценный вход/выход из контекста
Именно эти ограничения и заставляют задуматься о более гибкой абстракции — корутинах, которые:
Сохраняют состояние между приостановками
Позволяют тоньше управлять переключениями контекста
Могут работать без привязки к событийному циклу
Но чтобы понять, как это сделать в Dart — сначала нужно разобрать, как устроены существующие механизмы асинхронности.
Итак, с чем мы работаем ?
Thread
Isolate
Future
Coroutine & Fiber
Я постараюсь дать мои собственные определения этим понятиям с учетом работы с Dart.
Потоки, или нити — одна из фундаментальных концепций, с которой сталкивается практически каждый разработчик. Она реализована в большинстве языков, на которых мне доводилось писать код.
Однако в Dart всё иначе. Вернее, сама возможность работы с потоками есть, но скрыта от разработчика. Под капотом Dart VM отлично справляется с многопоточностью.
Классическое понимание потока — механизм для параллельного выполнения кода.
Если копнуть глубже, поток можно представить как структуру данных, которая включает:
стек вызовов
хранилище
атрибуты
состояние
Конкретная реализация может добавлять и другие компоненты.
Управление этой структурой ложится на runtime — будь то виртуальная машина языка или непосредственно операционная система.
Главная задача потока — организовать исполнение инструкций так, чтобы соответствовать заданным требованиям.
Например, если нужно задействовать все доступные ядра процессора, потоки позволяют явно указать планировщику runtime: “этот код должен выполняться параллельно”.
Довольно свежая концепция, с которой я впервые столкнулся в Dart.
Если по-простому, изолят — это:
контейнер с исполняемым кодом
выделенная область памяти (куча)
набор атрибутов
Всё это “упаковывается” и отправляется в поток для выполнения.
Зачем это нужно?
У традиционных потоков есть известная проблема — состояние гонки. Когда несколько потоков работают с общими данными, возможны конфликты. Изоляты решают это радикально: у каждого изолята своя память, и они не могут напрямую влиять друг на друга.
Дополнительный бонус:
Такой подход упрощает сборку мусора. Память сегментирована по изолятам, поэтому сборщику мусора не нужно анализировать всю память сразу — только отдельные изолированные участки.
Связь с потоками:
Когда Dart выполняет код, он:
Берет код в изоляте
Назначает для его выполнения поток
Отличное объяснение этой архитектуры можно найти у @mraleph [27]:
перевод на Хабре [29]
Кстати, если ты это читаешь, загляни сюда [30] :)
В Dart функции можно разделить на два основных типа (хотя технически их четыре, но остановимся на ключевых) — синхронные и асинхронные.
Работают предсказуемо и линейно: вызвали — получили результат. Никакой магии:
void main() {
print("before start");
final result = child(0);
print("child result: $result");
print("after start");
}
int child(int argument) {
print("child: entry($argument)");
return 0;
}
Вывод будет строго последовательным:
before start
child: entry(0)
child result: 0
after start
Здесь начинается интересное. Возьмем аналогичный пример, но с async/await:
Future<void> main() async {
print("before start");
final result = child(0);
print("child result: $result");
final resultWhenReady = await result;
print("child result when ready: $resultWhenReady");
print("after start");
}
Future<int> child(int argument) async {
await otherChild();
print("child: entry($argument)");
return 0;
}
Future<void> otherChild() async {
print("other child");
}
Вывод выглядит неожиданно:
before start
other child
child result: Instance of 'Future<int>'
child: entry(0)
child result when ready: 0
after start
main вызывает child(), но не ждет его завершения
Внутри child() есть await otherChild() — выполнение приостанавливается
Управление возвращается в main, который продолжает работу
Только после завершения otherChild() выполняется оставшаяся часть child()
Ключевые моменты:
Асинхронные функции не блокируют выполнение кода
Результат возвращается немедленно в виде объекта Future — обещания будущего значения
Фактическое значение можно получить через await
Важное ограничение Dart:
В рамках одного изолята нет настоящего параллелизма — в каждый момент времени выполняется только одна функция (синхронная или асинхронная)
Асинхронность ≠ многопоточность, это кооперативная многозадачность [31] в рамках одного потока выполнения
Давайте разберём этот магический трюк по косточкам. Перед нами — живая демонстрация работы корутин в Dart с использованием Fiber.
Исходное состояние:
var commonState = ""; // Наша разделяемая "память"
void main() {
print("before start");
Fiber.launch(mainEntry);
print("after start");
}
Последовательность выполнения:
Запускаем main() → выводится “before start”
Fiber.launch(mainEntry) — создаём корутину
Выводится “after start” (главный поток продолжает работу)
Внутри корутины:
void mainEntry() {
print("main: entry"); // Шаг 1 корутины
commonState += "main -> "; // Изменяем состояние
Fiber.spawn(childEntry); // Создаём ДРУГУЮ корутину
commonState += "main -> "; // Продолжаем работу
print("main: after child transfer");
Fiber.reschedule(); // Волшебная точка переключения!
print(commonState); // Финальный вывод
}
Вторая корутина:
void childEntry() {
print("child: entry"); // Шаг 1 второй корутины
commonState += "child -> "; // Меняем то же состояние
Fiber.reschedule(); // Возвращаем управление
commonState += "child"; // Продолжаем после возврата
print("child: after main transfer");
}
Вывод
before start
main: entry
child: entry
main: after child transfer
child: after main transfer
main -> child -> main -> child
after start
Магия переключений:
Первый Fiber.reschedule() в mainEntry:
Сохраняет текущее состояние выполнения (включая позицию в коде!)
Передаёт управление childEntry
Fiber.reschedule() в childEntry:
Возвращает выполнение ТОЧНО на место предыдущей остановки
В mainEntry это место после первого reschedule()
Почему это круто:
Мы явно управляем переключениями между задачами
Каждая корутина сохраняет свой контекст выполнения (стек, переменные)
Нет привязки к потокам ОС — это “зелёные потоки” (green threads)
Можно реализовать кооперативную многозадачность без настоящих потоков
Fiber vs корутины:
Корутина — низкоуровневая концепция сохранения состояния выполнения
Fiber — удобная обёртка, которая даёт:
API для запуска (launch, spawn)
Контроль переключений (reschedule)
Управление стеком и контекстом
Это и есть тот самый “контроль над выполнением”, который делает асинхронный код более предсказуемым.
На текущий момент реализация async в Dart (как мне видится) — в основном заслуга одного человека — Александра Маркова [32].
Александр, если читаешь — добавь корутины, ну позязя :)
С чего начать разбор…
Функционал достаточно обширный, поэтому сосредоточимся на конкретном примере:
Future<void> main async {
final myFuture = myFunction();
await myFuture;
}
Future<String> myFunction() async { return "Hello, World!"; }
Как выполняется этот код с учетом async-await:
Вызов main()
Вызов myFunction()
Неявное создание объекта Future
Завершение myFunction()
Ожидание myFuture и получение значения
Первое, что привлекает внимание [33] — класс Future. С него и начнём.
Future — это удобный публичный класс, ориентированный на разработчика.
Методы хорошо документированы, их назначение понятно из названий.
Основная идея — предоставить инструменты для работы с асинхронными операциями:
цепочки преобразований через then
колбэки завершения через whenComplete
обработка ошибок через catchError
Но сегодня нас больше интересует не сам класс, а конструкция await myFuture.
Когда-нибудь задумывались, что скрывается за await?
Покажу:
@pragma("vm:entry-point", "call")
@pragma("vm:invisible")
Object? _await(Object? object) {
if (_thenCallback == null) {
_createAsyncCallbacks();
}
if (object is _Future) {
if (object._isComplete) {
_awaitCompletedFuture(object);
} else {
object._thenAwait<dynamic>(
unsafeCast<dynamic Function(dynamic)>(_thenCallback),
unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
}
} else if (object is! Future) {
_awaitNotFuture(object);
} else {
_awaitUserDefinedFuture(object);
}
return _functionData;
}
Эта функция из класса _SuspendState — основа реализации await. Правда, между ключевым словом и вызовом функции происходит магия, о которой поговорим позже.
Сейчас разберёмся с _SuspendState и его связью с Future. Нас интересуют два фрагмента:
object._thenAwait<dynamic>(
unsafeCast<dynamic Function(dynamic)>(_thenCallback),
unsafeCast<dynamic Function(Object, StackTrace)>(_errorCallback));
и
_awaitCompletedFuture(object);
_thenAwait — метод _Future, добавляющий новый _FutureListener. Каждый такой listener — это callback, вызываемый при завершении Future.
В случае _SuspendState вызовется _thenCallback, который запустит:
thenCallback(value) {
suspendState._resume(value, null, null);
}
_awaitCompletedFuture создаёт микротаску для вызова thenCallback.
О микротасках кратко: когда код Dart не выполняется, управление переходит к VM (C++ часть).
В Dart VM есть несколько очередей событий. Микротаски попадают в начало очереди и выполняются при первой возможности.
Переходим к самой интересной части — реализации async-await в Dart VM.
Лучшее описание — в технической документации [34].
Я выделю ключевые моменты для общего понимания.
Заранее упомянем важный механизм Dart VM — заглушки (stubs).
Это ассемблерный код, генерируемый C++ частью VM. Простейший пример:
void StubCodeCompiler::GenerateInitStaticFieldStub() {
__ EnterStubFrame();
__ PushObject(NullObject()); // Make room for result.
__ PushRegister(InitStaticFieldABI::kFieldReg);
__ CallRuntime(kInitStaticFieldRuntimeEntry, /*argument_count=*/1);
__ Drop(1);
__ PopRegister(InitStaticFieldABI::kResultReg);
__ LeaveStubFrame();
__ Ret();
}
Разберём по шагам:
EnterStubFrame() — сохраняет текущий фрейм (RBP) и перемещает указатель стека (RSP) в RBP
PushObject(NullObject()) — подготовка регистра для результата
PushRegister(InitStaticFieldABI::kFieldReg) — передача регистра с полем для инициализации
CallRuntime(kInitStaticFieldRuntimeEntry, 1) — вызов C++ функции из Dart VM
Drop(1) — корректировка указателя стека
PopRegister(InitStaticFieldABI::kResultReg) — получение результата
LeaveStubFrame() — восстановление фрейма
Ret() — возврат из функции
Это простейшая заглушка. Сложные выглядят куда интереснее.
В реализации async-await используются три основные заглушки:
GenerateAwaitStub (она же SuspendStub)
GenerateResumeStub
GenerateReturnAsyncStub
AwaitStub генерируется при вызове await и отвечает за:
Создание _SuspendState (при необходимости)
Сохранение текущего фрейма
Вызов асинхронной функции
Удаление текущего фрейма
Возврат в вызывающий код (в нашем случае — в C++ часть VM, вызвавшую main())
ResumeStub реализует _SuspendState._resume:
Загрузка фрейма приостановленной функции
Восстановление стека
Переход к точке после await
ReturnAsyncStub в нашем примере просто вызывает _SuspendState._returnAsync.
Итоговый путь выполнения:
Создание _SuspendState
Сохранение фрейма
Вызов функции
Завершение Future через _returnAsync
Выполнение микротаски с thenCallback и _resume
Возврат с восстановлением контекста
Пока оставлю в тайне детали реализации своих корутин – вместо этого разберём их на примере отличной базы данных Tarantool [21]. Мои корутины заслуживают отдельного глубокого разбора, который я планирую дать в будущих статьях.
В Tarantool ключевой концепцией является Fiber – это абстракция над корутиной. С Fiber можно работать как с обычными корутинами: приостанавливать, возобновлять, делать yield для переключения на другой готовый к выполнению Fiber. За управление Fiber отвечает специальный планировщик – Fiber Scheduler.
Давайте сосредоточимся на двух фундаментальных аспектах реализации корутин в Tarantool:
Как происходит переключение контекста?
Что происходит, когда ни один Fiber не готов к выполнению?
Вот как выглядит реализация переключения контекста для Linux x64 в Tarantool:
void coro_transfer (coro_context *prev, coro_context *next) {
pushq %rbp
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %rsp, (%rdi)
movq (%rsi), %rsp
popq %r15
popq %r14
popq %r13
popq %r12
popq %rbx
popq %rbp
popq %rcx
jmpq *%rcx
}
Кратко о происходящем:
На вход получаем контексты текущей и следующей корутин
Сохраняем callee-saved [35] регистры на стек
Сохраняем текущий стек в контекст старой корутины
Загружаем стек новой корутины
Восстанавливаем регистры из стека
Переходим по адресу следующей инструкции (через регистр %rcx)
Функция coro_transfer – это наш “переключатель” между корутинами. Все операции с Fiber в Tarantool, требующие смены контекста, используют именно её.
Второй вопрос решается через комбинацию coro_transfer и Event Loop. Подробнее о реализации event loop можно почитать здесь [36], а в Tarantool используется библиотека libev [37].
Разберём алгоритм функции sleep [38]:
Регистрируем таймер через libev
Привязываем таймер к функции пробуждения Fiber
Делаем yield (переключаем контекст на вызывающий Fiber)
Если планировщику нечего выполнять, он обращается к libev
При срабатывании таймера libev уведомляет планировщик о готовности Fiber
Почти все операции с Fiber в Tarantool построены на связке libev и libcoro [39].
Ещё один интересный пример – кроссплатформенная реализация корутин с поддержкой Эльбруса. Она доступна здесь [12].
Именно на эти примеры я и ориентировался в своей работе. По крайней мере, так было задумано.
Хотя на практике получилось не совсем так :)
Серьёзно, в чём смысл? Реализация async в Dart выглядит вполне солидно.
Хотя я не проводил глубокого тестирования производительности, те немногие бенчмарки, что делал, показывали приемлемые результаты.
Но дело не в скорости. Главное — гибкость и функциональность.
Вот какие требования у меня сформировались к системе асинхронного управления:
Не хочу плодить лишние сущности вроде Completer [40] просто для фиксации точек suspend/resume
Нужна прозрачная интеграция с FFI [41]: чтобы события из нативного кода мгновенно пробуждали корутины в Dart без лишних накладных расходов
Меня не устраивает объём вспомогательного кода внутри Future — кажется, можно сделать лаконичнее
Разделение на sync и async функции выглядит искусственным, а переходы между ними — неудобными (см. эту проблему [42])
Ну и… шило в одном месте — просто захотелось реализовать это самому :)
Прежде чем изобретать велосипед, я решил проверить — может, решение уже существует? И действительно, кое-что нашлось.
Знакомьтесь: Dartino [43] — заброшенная реализация Dart VM для embedded-устройств (официальное закрытие проекта [44]).
Но самое интересное — вот что я в ней обнаружил:
void InterpreterGeneratorX64::DoCoroutineChange() {
LoadLiteralNull(RAX);
LoadLocal(RBX, 0); // Load argument.
LoadLocal(RSI, 1); // Load coroutine.
StoreLocal(RAX, 0);
StoreLocal(RAX, 1);
Label resume;
SaveState(&resume);
LoadProcess(RDI);
// RSI already loaded with coroutine.
__ call("HandleCoroutineChange");
RestoreState();
__ Bind(&resume);
__ Bind("", "InterpreterCoroutineEntry");
StoreLocal(RBX, 1);
Drop(1);
Dispatch(kCoroutineChangeLength);
}
Да, это настоящие корутины в Dart.
Позже я подробнее расскажу о своих впечатлениях от этого этапа, но когда я увидел, что нужная мне функциональность уже была реализована, но оказалась заброшена — это вызвало двойственное чувство.
Дело даже не в том, что я не смогу использовать Dartino как основу для своей работы (ведь он несовместим с текущей Dart VM).
Главная грусть в том, что 10 лет назад уже существовала компактная, эффективная реализация Dart — и её просто выбросили.
Обидно осознавать, что столько труда осталось невостребованным, без шанса на развитие.
Как и обещал, фиксирую свое состояние на этом этапе разработки.
Я был расстроен из-за Dartino, но моя уверенность и мой настрой не потерялись.
Я нашел много красивых реализаций корутин, которые я могу использовать для разработки.
Кстати, команде Tarantool огромное спасибо за OpenSource! Это правда нужно миру. Пожалуйста, развивайте его.
После моего небольшого анализа и поисков примеров для реализации, мой уровень решимости – 90%, потому что Dartino правда расстроил.
Нет, серьезно, скачайте себе репозиторий, посмотрите на код, почитайте, может быть среди вас найдется тот, кто сможет возродить этот проект.
Следующая статья будет максимально технической. Я собираюсь рассказать о том, что из себя представляет реализация корутин на Dart.
Какие аспекты Dart VM пришлось затронуть. Какие особенности Dart мне мешали, а какие помогали на моем пути.
Разумеется, будет фиксация моего состояния к моменту создания первого рабочего прототипа.
Приглашаю посмотреть на жесткие техно-извращения с Dart VM. Тыкайте сюда [3].
Спасибо за внимание !
Автор: antonbashirov
Источник [45]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/15644
URLs in this post:
[1] опыте: http://www.braintools.ru/article/6952
[2] Часть 1: О времени, сложности и мотивации: знакомство: https://habr.com/ru/articles/913726/
[3] Часть 2: О времени, сложности и мотивации: история поражения и победы: https://habr.com/ru/articles/913728/
[4] Часть 3: О времени, сложности и мотивации: финал: https://habr.com/ru/articles/913730/
[5] Эльбрус: https://habr.com/ru/companies/rostelecom/articles/562858/
[6] Dart: https://dart.dev/
[7] реализовать: https://github.com/antonbashir/dart-fibers
[8] ART: https://github.com/art-community/art-java
[9] GraalVM: https://www.graalvm.org/
[10] art-platform: https://github.com/art-community/art-platform
[11] art-react: https://github.com/art-community/art-react
[12] Koishi: https://github.com/taisei-project/koishi
[13] озарение: http://www.braintools.ru/article/7570
[14] Общее объяснение: https://habr.com/ru/articles/850970/
[15] Корутины в Kotlin: https://kotlinlang.org/docs/coroutines-overview.html
[16] Реализация в Tarantool: https://www.tarantool.io/en/dev/core/fiber/
[17] памятью: http://www.braintools.ru/article/4140
[18] IOUring модуль: https://github.com/antonbashir/dart-iouring-transport
[19] IOUring: https://unixism.net/loti/what_is_io_uring.html
[20] Tarantool модуль: https://github.com/antonbashir/dart-tarantool-storage
[21] Tarantool: https://www.tarantool.io/en/
[22] RSocket модуль: https://github.com/antonbashir/dart-reactive-transport
[23] RSocket: https://rsocket.io/
[24] страх: http://www.braintools.ru/article/6134
[25] async/await: https://dart.dev/libraries/async/async-await
[26] интуиция: http://www.braintools.ru/article/6929
[27] @mraleph: https://www.braintools.ru/users/mraleph
[28] оригинал на английском: https://mrale.ph/dartvm/
[29] перевод на Хабре: https://habr.com/ru/articles/848166/
[30] сюда: https://github.com/antonbashir/dart
[31] многозадачность: http://www.braintools.ru/article/3673
[32] Александра Маркова: https://github.com/alexmarkov
[33] внимание: http://www.braintools.ru/article/7595
[34] технической документации: https://github.com/dart-lang/sdk/blob/main/runtime/docs/async.md
[35] callee-saved: https://www.uclibc.org/docs/psABI-x86_64.pdf
[36] здесь: https://habr.com/ru/articles/319350/#event-loop
[37] libev: https://github.com/enki/libev
[38] sleep: https://www.tarantool.io/en/doc/latest/reference/reference_lua/fiber/#lua-function.fiber.sleep
[39] libcoro: https://github.com/semistrict/libcoro
[40] Completer: https://api.flutter.dev/flutter/dart-async/Completer-class.html
[41] FFI: https://dart.dev/interop/c-interop
[42] эту проблему: https://github.com/dart-lang/sdk/issues/39390
[43] Dartino: https://github.com/dart-archive/sdk
[44] закрытие проекта: https://groups.google.com/g/dartino-discuss/c/U3fzZjoOdKg/m/0CB8ON6RCAAJ
[45] Источник: https://habr.com/ru/articles/913726/?utm_source=habrahabr&utm_medium=rss&utm_campaign=913726
Нажмите здесь для печати.