iT邦幫忙

2025 iThome 鐵人賽

DAY 22
0

前一篇在開發 Conjure Piglet Client 時,提到了第三方函式庫 WebSocket 。我一開始還一度天真地以為,我只要找一些 Neovim plugin 或是 Lua library 裝一裝就可以完成了,真的開始動手之後,才發現這比我最初想象的困難多了。

問題概述

在嘗試了幾個選項之後,最接近我的需求的選項是 lua-websockets 這個 library ,其它的選項有一些可能只有實作 websocket client 又或是並非純 Lua 的實作,由於希望將來這個 conjure client 不會太難安裝,我決定使用 Luarocks 安裝的 library 就是極限了。

為了測試方便,我還安裝了一個 websocat ,這樣子我可以先專注在測試 websocket ,而不需要 websocket 與 CBOR 一起測。

不久,我成功地用 lua-websockets 造出了一個 websocket echo server 。然後,第一個挑戰就出現了:「儘管用 websocat 做的 client 端,可以順利連上在 Neovim 裡執行的 websocket server ,但是當 client 切斷連線之後,Neovim 卻會整個凍結。」

解題架構

這是怎麼回事呢?問題出在 Neovim 使用 lua-websockets 本來就不太合理。lua-websockets 函式庫提供了兩種非同步的機制:lua-ev 或是 copas (coroutine) ,然而,無論是哪一個非同步機制,它的底層都不是 Neovim 的 vim.uv 。換言之,前述的作法等於是使用了兩個事件迴圈 (一個來自 Neovim,另一個來自 lua-websockets) ,自然很容易造成上述的凍結。

了解凍結的原因之後,合理的解法也很明顯了,應該要讓事件迴圈只有一個。於是,我決定修改 lua-websockets 的底層,讓它依賴於 Neovim 的 vim.uv 。簡單地來說,我決定要移植 lua-websockets 到 vim.uv 上。

在閱讀了 lua-websockets 的原始碼之後,我發現移植 (porting) 的重點,應該放在 src/websocket/server_ev.lua 即可。

這部分的移植,我用 LLM 來做。前後試了兩個 prompts,總算完成了可用的 websocket server.

Prompt 1

src/websocket/server_ev.lua 重寫,使其能夠在 Neovim 中執行。 注意:原始實作依賴四個函式庫:evloopwebsocket.ev_commonsocket。請移除對這些函式庫的依賴,改為使用 vim.uv。 …

Prompt 1 產生的程式碼大致可用,但是其中一個函數 message_io 算是嚴重的幻覺,而這個的幻覺滿合理的,因為我沒有注意到 websocket.ev_common 其實也是 server_ev.lua 的主要部分,而非外部依賴。

於是,我用了第二個 prompt 來修正這個問題。

Prompt 2

考慮 src/websocket/ev_common.lua 裡的 message_io,它依賴於 lua evloop 。請重構 message_io,並將該 message_io 裡的 sock 改成 vim.uv 裡的 uv.new_tcp() 的傳回值,也就是說,這個 sock 它有 read_start, write 等函式可以呼叫。 ...

在兩個 Prompt 之後,我再做一些簡單的人工除錯,WebSocket 連線就打通了。

小結

某種程度來講,這回解的問題也還是跟之前 day20, day21 一樣是移植問題。大的移植問題裡,又有小的移植問題。

這個移植問題,由於我已經確定了它的依賴項目了,所以 LLM 的 prompt 就可以順利處理。另一方面,如果沒有用人工來將依賴關系加釐清,LLM 成功的機率就非常低了。


上一篇
專案研討—Conjure Piglet Client
系列文
在 Neovim 中探索 Fennel 與函數式編程22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言