Conversation
|
目前需要 ltask 实现 wasm 版的 semaphore https://github.com/cloudwu/ltask/blob/master/src/semaphore.h 对于非 web 场景,它完全复用了 cond ,不实现 sem_acquire() 的 try 语义,即 inf 永远认为是 1 。永远等待,不会失败。 ltask 的 https://github.com/cloudwu/soluna/blob/frameinit/src/lualib/main.lua#L87-L95 这里利用这个新特性,会 yield(false) 所以,启动函数可能被多次调用 https://github.com/cloudwu/soluna/blob/frameinit/src/lualib/main.lua#L87-L95 而在 entry.c 中,https://github.com/cloudwu/soluna/blob/frameinit/src/entry.c#L746 检测到 init 流程返回 布尔 false 就维持原状态,待下一次 frame callback 继续调用。 如果想在非 web 环境测试这个新机制,可以在 https://github.com/cloudwu/soluna/blob/frameinit/src/lualib/main.lua#L87 之前插入 -- 推迟 100 帧再初始化
for i = 1, 100 do
coroutine.yield(false)
end |
|
刚修改了一下 sem_* api 的名字,从 sem_acquire/sem_release 改为 sem_wait/sem_post 。我觉得语义和 https://man7.org/linux/man-pages/man3/sem_post.3.html 更一致一些。 即:sem_wait 等待 sem_post 发出的信号后返回。没有定义额外的 sem_trywait ,而使用 sem_wait 的参数区分。 |
|
我合并了你的分支,启动阶段顺利度过,ltask 开始调度了,但是还存在 service 死掉的情况,应该是我哪里没处理到。还有几个 worker 直接 Quit 了。看了下也没有进行到初始化 render 的阶段,发送给 start service 的消息没被处理。Root 直接 quit 了。 奇怪的是,我在 quit_all_workers 函数里加个打印,就不会有任何服务 dead 了. 但是也只是在忙调度,打印日志, 日志Details |
|
看 log ,我发现了类似 "Service 7 quit“ 。这说明服务在 resume 时出错或结束了。但 ltask 并不会让服务代码结束,所以多半是出错。 我猜原本 https://github.com/cloudwu/ltask/blob/master/src/service.c#L426-L432 这里应该由 writestringerror 输出 error log ,但没能成功。我觉得可以把 writestringerror 这个宏修改一下,让它可以正确输出 log 。
|
|
我修改了 writestringerror 的实现,但是没有打印出日志(或者说没有走到这里)。 另外偶尔会触发 quit_all_worker 函数, 看样子应该是 root 服务出错了。 |
以上是节选的 service 7 退出阶段的 log (按时间戳排序)
所以,一定是 https://github.com/cloudwu/ltask/blob/master/src/service.c#L405 这个函数有五个分支 return 1 ,最后两个会触发 writestringerror 。 前三个分别是 : S == NULL ,L == NULL ,以及 lua_resume 返回 LUA_OK (即运行的 lua vm 的 mainthread 正常结束)。 如果是第三种,那么只能是 https://github.com/cloudwu/ltask/blob/master/lualib/service.lua#L935 这里的正常退出。 |
|
会不会是 ltask 的启动阶段出了错,然后 root 服务去了退出流程? root 服务现在是当启动完毕后,没有任何匿名服务,就把 ltask 整个退出 https://github.com/cloudwu/ltask/blob/master/service/root.lua#L284 |
|
我现在已经定位到是 start.lua 在调用 file.load 时报错了。 走到这个分支,所以没有错误信息。 不过多试几次,也不一定走到这里,有可能还没走到挂了。 难道 wasm worker 不能使用文件 api ? 所以读取不到文件? 联想到 io.write 在 wasm worker 中无法使用,需要我重写成 emscripten_outf, 感觉很有可能 拿到的错误信息就是 No such file or directory 我怀疑跨线程不共享 memfs |
|
我之前在做 Ant 的时候,倒是把所有的 IO 都集中到一个独立服务中,统一管理。 memfs 听起来是 js 侧的模块,不知道如何保证多个 worker 共享。从 https://emscripten.webassembly.net.cn/docs/api_reference/wasm_workers.html 这里看
听起来 pthread 版本是用消息队列完成的。 如果有必要,其实可以把所有 file.load 请求放在一个服务中,并在这个服务里使用 mainthread 切到主线程运行。另外, 亦或用单一 zip 包的话,其实或许连 memfs 都可以不用(只在启动时打开文件加载)。 |
|
emscripten_async_run_in_main_runtime_thread 现在 soluna 就有在使用。这是一个 pthread 限定的函数,将函数发送到主线程执行。例如 openurl 和 ime 通过这个函数实现的。但是 wasm worker 无法使用,没有实现。 Line 30 in 12d613b 当然, wasm worker 有其他通信方式,可以通过 emscripten_wasm_worker_post_function_ 发送消息。 PS: https://github.com/emscripten-core/emscripten/tree/main/test/wasm_worker 列出了大部分 wasm 函数的用法和用途 |
|
我在 https://cocalc.com/github/emscripten-core/emscripten/blob/main/system/include/emscripten/wasm_worker.h 看完了 wasm worker api ,尤其是 emscripten_wasm_worker_post_function_ 的注解。 感觉这个工作可以暂搞一段落,soluna 应继续使用 pthread 。如果使用 wasm worker ,还需要解决把某些 api 扔回 parent 即 mainthread 的 event loop 运行的问题。单独只是用 post function 还不足以取回返回值,得用 semaphore 同步返回值。 目前 ltask 的 mainthread api 尚不支持多个 service 以队列形式向 wasm mainthread 编排任务,只供 render 服务使用。若独立一个 io 服务出来还需要再改进。不如让 wasm pthread 在 C api 层次解决,减少 ltask/soluna 的维护成本。 不过,这次了解了 wasm worker 的原理。我认为如果能想到好的解决方案,利用 wasm worker 访问 memfs 应该能提高 io 性能:因为从 pthread 的 api 层次看,io 操作更细碎,有很大的同步成本。而如果在更高层次解决,可以合并这些操作,把整个 io 任务,例如 file.load ,一次同步。另外,也可以顺便把 openurl/ime 也合并到同一方案中。 ps. 这个分支我就不删了,备以后参考。 |
|
补充:通过这次尝试,可以了解到,在 ltask 里,最好不要在不同服务同时进行 io 操作。wasm pthread 只是做了一些封装,但根源还是会把 io proxy 回主线程。猜测它会 block 住 ltask 的 worker ,等待 web mainthread 。这对 ltask 的调度器是未知的,如果多个 ltask worker 同时进行 io ,就会被 wasm pthread 串行到 web mainthread ,猜测如果 ltask worker 数量不够,非常大概率导致 ltask 调度器死锁。 另外,log 服务是否应该做一些修改,使用 emscripten_outf ,或许能更好一些。 |
#51 关闭后,我把分支删了。结果重新推的同名分支被 github 识别为新的。所以重新开一个 pr 。