На многие устройства портировали NetBSD и программы для неё. На цифровом телефоне GrandStream GXV-3140 попадался портированный Pidgin. На ТВ-приставках с Venus Linux и роутерах с OpenWrt портировали много программ. WebAssembly похож на встраиваемые устройства, и всё же он остался непокорённый.
Корни JavaScript
Исходный текст для обычных устройств отличается от того, что требуется для WebAssembly. Когда WebAssembly работает в браузере, он подвержен всё тем же ограничениям, что и программы для браузера. Необходимо постоянно возвращать управление наружу.
Проблема двухцветности
В мире JavaScript происходила эволюция: CPS, deferred, promise, async/await. И пользоваться этим по-прежнему неудобно. async/await порождает проблему двухцветности: нужно отслеживать, какие функции могут быть асинхронные, и либо асинхронность разрастается, перекрашивает всё в свой цвет, либо асинхронность упирается в барьер синхронного интерфейса. Тяжело обращаться с двумя цветами. Также с точки зрения разных цветов есть отличия даже в стандартных классах. Например, у массива Array есть метод forEach, но он сам синхронный, и он ожидает на вход синхронную функцию. Стандартного асинхронного аналога нет. Под аналогом следует понимать метод массива, который вызовет обработку каждого элемента, дожидаясь обработки прошлого.
Странный параллельный запуск
Можно пойти другим путём. Можно запустить обработку элементов параллельно, а потом дожидаться результатов. Такого готового асинхронного метода тоже нет, а наивная попытка написать вскрывает ещё странные особенности асинхронных функций. Вызовы асинхронных функций интуитивно воспринимаются как запуск в другом потоке, но на самом деле они могут выполнить множество операций перед тем, как заснуть и вернуть обещание.
Специфика WebAssembly
В-принципе, можно пытаться повторять тот же путь, что и в JavaScript, и дойти до async/await и всех проблем с ними. Но посмотрим, что пробовали ещё.
Интерпретатор
Очень популярное решение в подобных ситуациях: заменять асинхронный участок кода интерпретируемым. В EmScripten это называлось Emterpreter. Недостаток: замедление в 10 раз и большие проблемы с двухцветностью. Сейчас эта возможность устаревшая.
WARNING: The Emterpreter was removed in emscripten 1.39.17. For details, see the changelog.
Asyncify
Другой попыткой в EmScripten был режим asyncify. Во всех возможных точках останова делались дубликаты функций, в которые можно и продолжить с этой точки. Это, по идее, должно быть максимально быстро, но это приводило к чрезмерному разрастанию кода, который можно продолжить со всех возможных точек останова. Эту возможность объявили устаревшей ещё раньше, чем Emterpreter.
ASYNCIFY has a bad worst-case of large code size: If it needs to modify many methods, it can grow code size very significantly (even 10x more was seen).
Типизированные продолжения
В 2020м было добавлено предложение добавить в WebAssembly типизированные продолжения. Пока не принято, то есть, в браузерах не доступно и, может быть, не будет доступно.
JavaScript Promise Integration (JSPI)
Другой попыткой было сохранение стека WebAssembly за кулисами при попытке взаимодействовать с асинхронным кодом JavaScript. Это было реализовано в одном из браузеров, но требует включения экспериментальных возможностей. Возможно, это так и не будет принято. Также при использовании этой возможности наложены довольно жёсткие ограничения на размер стека, который может быть сохранён на будущее.
Ещё одна попытка
Последнее слово ещё не сказано. Автор видит перспективным идти не по пути CPS и его вариаций вплоть до async/await, а по пути батутизации (trampolined style). Разница: в CPS вызов преобразуется в вызов, и возврат тоже преобразуется в вызов. При батутизации вызов превращается в возврат, и возврат тоже воплощён через возврат. Для исполнения батутизированного кода нужен рантайм, нужен батут, но вот эта комбинация из батута и батут-преобразованного кода и является, по мнению автора, лучшим решением. Автор ставит цель написать транслятор языка Си в WebAssembly с батут-преобразованием. В отличие от Asyncify, оно 1:1, и размер кода не раздувается. Батут похож на интерпретатор, но не замедлен в 10 раз. Ожидаемое замедление в 3 раза, но в обмен на это свобода писать в удобном синхронном стиле и не решать проблемы двухцветности. Такой транслятор должен быть наиболее похож на трансляторы для обычных платформ. Из одних исходников должно быть возможно компилировать для обычных платформ обычными трансляторами, и этим новым транслятором для WebAssembly, по возможности, без необходимости подстраиваться под особенности браузера. |