Некоторые варианты, о которых я знаю:
Во-первых, вы можете вручную конвертировать
void *thread_fn(void *) {
// init
while (true) {
// do work
}
}
в функции инициализации и работы (и, возможно, в третью функцию склеивания для исходного случая), затем сериализуйте выполнение вручную (как вы написали): вызовите одну «рабочую функцию», затем другую, затем сначала снова и т. Д.
Другой вариант, полуавтоматический способ, который может быть успешным: с GNU Pth вы можете эмулировать многопоточность внутри одного потока ОС. Как я могу себе представить, это делается именно с сопрограммами. Если вам удастся портировать GNU Pth на сопрограммы Emscripten, то вы автоматически переключите эмулированные потоки на некоторые системные вызовы (?). Я все еще не уверен в этом подходе, но если вы перенесете эту библиотеку на Emscripten, вы, вероятно, получите достаточно информации о том, как отделить основной высокоприоритетный поток.
Лично я использовал первый подход при портировании QEMU на Emscripten (и он работал, но с некоторыми сбоями). Второй подход был использован мной для однопоточности нативного кода, но я никогда не использовал его с Emscripten, поэтому вы были предупреждены :) - он довольно многообещающий, но требует некоторой работы, которая может закончиться просто потраченное время ... Во всяком случае, похоже, что сериализация многопоточного кода - это не простая задача, которая определенно будет успешной, это зависит ...
Между тем, в браузерах уже была поддержка многопоточности в общей памяти. AFAIK, он был снова отключен после Meltdown & Spectre. Я не знаю, было ли оно уже повторно включено (если возможно вообще).