如何看 emacs-ng 项目?

我感觉多线程的支持可以逐步迁移。先实现一套新的emacs操作api,保证跟老的api优好合作,并保证没有并发问题。然后新的插件就可以使用新的api写了,老的插件也可以慢慢迁移。更进一步可以添加一个关键字可以把老的emacs api编译成新的api(例如python 里面的async await?/). 之后迁移的动作就比较小了。

不需要这么复杂,可以直接暴力添加并行处理,不能接受这个的现有 lisp 代码不从非主要处理单位调用就行了。主要是 C 端需要做到安全,而已有的协作性线程已经解决大部分问题 (比如说 specpdl 等),只留下 GC 和 wait_reading_process_output 这几个难题。

1 个赞
(let ((case-fold-search nil))
  (search-forward "abc"))

就这代码怎么把里面的 search-forward 搬到其它线程去? case-fold-search 的作用域怎么处理?

2 个赞

比如说:

(let ((team (make-team 1)))
  (make-thread (lambda ()
		 (%with-held-buffer-lock
		  (let ((case-fold-search nil))
		    (search-forward "abc"))))
	       nil team))

现有elisp运行环境的全局资源太不安全了

没关系,要求是 C 端不会崩溃就行了

剩下的可以由 Lisp 端的 mutex 保护

而现在我遇到的难题是在 GC 过程中如何停下其他线程。我正在尝试对其他线程发送 SIGUSR1,在 SIGUSR1 的 handler 中运行 __builtin_unwind_init 和等待 _gc_cvar condition variable,不知道这样可不可行

这么来说也挺容易, 可以在现有emacs基础上, 用lsp类似的rpc通信模式来实现, 外部启动一个node.js进程, 两边定义一套rpc协议, 然后就可以在这个基础上搞很多应用了, emacs负责派发(繁重的)任务给node.js, node.js负责执行并产生结果, 然后emacs接收结果并展示.

类似lsp, 也部分类似EAF.

这样不单可以外挂node.js, 还可以外挂其他任何运行环境, 比如python, lua等等, 甚至可以跟neovim打通.

跟界面有关的, 比如锁住一个buffer, 有可能卡住界面. 另外, 全局变量太多了, 几十上百个全局变量, 怎么锁? 读写都要锁啊

这个应用有点高级了, 还想象不出来使用场景, 有点像浏览器

EAF 其实就是一个IPC设计,Elisp可以调用Python、NodeJS和C/C++联合辅助Emacs分担计算压力。

Elisp自己多线程的问题是:

  1. 全局状态、Hook太多,一旦产生多线程写冲突,出错太多就没人想写多线程代码
  2. 不是靠通讯而是靠锁的机制,锁多了,各种相互卡,死锁就会导致没人用

多线程最佳模型就是不要用锁,独立线程间用队列通讯,同时图形代码和非图形代码一定要分开调用,Elisp这么多API, 根本无法完全区分开来。

现在Emacs理论上有线程这些东西,但是多线程编程模型完全没法用,不是技术问题,是设计问题。

2 个赞

现在就是emacs的展示能力有点差, 所以EAF是自己展示了

沙箱也不行,因为Emacs本身就是一个解释器,状态在发生变化(比如已加载符号和文件等),比如要写一个查询所有命令的插件,fork一个干净的沙箱就没有这么多已加载的列表,所以沙箱没有状态也不行。

现在最合理的方式就是,Emacs这么多年大杂烩超动态hook集合的模式,就让他这样,没有多线程只要小心维护文本编辑性能足够的,其他图形和耗时计算用外部进程或者EAF类似技术分离出去,用正规军的打法去实现,不要到处再 hacking way 了。

沙箱里面功能是受限的, 你说的命令列表是emacs主线程的吧? 沙箱里面不能访问这种外部资源. 每个沙箱环境是独立的, 类似于一个独立emacs进程的环境. 沙箱就是省掉了创建emacs进程的开销, 把它放线程里了.

Emacs的展示可以说是简陋,撇开多线程基础设施,一个好的图形库最少要支持几个基本组成:

  1. Box控件体系,实现侧边栏不用靠 dedicated window 来 hack
  2. Z轴图层透明图层叠加控制,实现 window number tooltip、波浪线、indent line、语法建议提示等,不用通过 overlay 来实现, overlay 实现太依赖字体宽度和文字逻辑了
  3. SVG合成库,虽然支持 cairo, 但是每个操作系统的库还是不统一,cairo库本身的API能力也没有暴露出来
  4. 再牛逼一点, Web这种控件库跨平台支持,很多场景就不适合纯文本

现在Emacs的策略是不依赖任何现有图形库,自己对接不同操作系统的图形库来实现一些功能feature, 而不是整体顶层设计,规划图形能力,大多数Emacs开发者都对图形编程没有经验,这样对接不同操作系统的贴瓷砖的活要干多少年?

是啊, 现有的emacs太复杂了, 保持现状, 让它做轻量的工作, 以及结果展示等简单工作, 重工作用rpc实现, 确实是比较合理的方式.

我的问题是,现在耗时计算分为Emacs状态计算和非Emacs状态计算,Emacs状态耗时计算可以靠Elisp性能提升或者沙箱来实现,如果不关心内存,可以采用 Snapshot 机制,随时拷贝两份Emacs来做,一个做界面控制,一个专门用于做后台计算,常驻开起来。

因为实时Shapshot, 所以状态不会差多少。

有个大胆的设想, 让emacs不依赖操作系统的任何图形库, 直接类似于谷歌的flutter, 全部自己绘图, 包括控件. 谷歌flutter好像是基于skia. 它好像不用每个系统的ui库, 好像直接跟显卡打交道.

是,这也是我多年后设计EAF的初衷,虽然说Emacs的现在代码是一大盘意大利面条,到处hacking way, 但是数万插件生态也是Emacs最大价值所在。

与其从底层大改不如在外部进程做多线程和图形计算,一来不破坏Emacs生态,二来分离开来Emacs本身专注 text world 性能也够,也纯粹。

什么东西都往Emacs里面塞,就会产生经济学里面常说的 ‘好心办坏事’。

2 个赞

Flutter的思想是,底层有一个类似 Cairo 的矢量图形库来对接操作系统底层绘制API, 因为这个矢量库就只是绘制SVG和位图, 所以工作量非常容易收敛和稳定。

Flutter再在这个 跨平台图形矢量库 上开发统一的控件。

但是这个矢量图形库还是要对接和磨平操作系统底层差异的。

这个观点不成立的是,一、Emacs不光是图形跨平台,还需要和底层非图形API访问(比如环境变量、进程管理等); 二、重新基于另外一个图形库来改造Emacs, 撇开巨大工作量没人付费后持续开发的问题,光FSF的free software争论都会把这个想法快速枪毙了。

我的观点就是,顺势而为,不要抗争已有的东西,生态之所以为生态,就是要做到尊重传统想办法变通,举个例子, Visual Studio 这么牛逼,VSCode 在Web导向的目标下,不也寻找到 LSP 这样变通的方法了吗? 还挺好用。

3 个赞