hchunhui / librime-lua

Extending RIME with Lua scripts
BSD 3-Clause "New" or "Revised" License
357 stars 43 forks source link

【Bug】Memory 对象会导致 Rime 无法同步 #335

Closed mirtlecn closed 6 months ago

mirtlecn commented 6 months ago

如 #330 所示

Memory 无法用于已经启用,且用户词典打开(默认情况)的方案,会导致大部分前端同步时抛出词典锁定的错误。

而如果想要使用 Memory 对象,所用方案很可能为启用用户词典状态(默认),lua 并未阻止此危险操作。所以此设计似有问题。请确认。

复现

测试方案(内含报错信息):

test_lua.zip

有关问题的前端:ibus-rime,Squirrelfcitx5-rime(不会显示错误,但无法继续同步)

预期情况

Memory 可以正常工作,不导致用户词典锁定。

或者以下情况均可:

问题 lua

-- filter:
--   - lua_fiter@*test

local F = {}

function F.init( env )
    -- F.foo = Memory( env.engine, env.engine.schema) -- error
    -- local foo = Memory( env.engine, env.engine.schema)  -- no error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error
end

function F.func( input, env )
    -- F.foo = Memory( env.engine, env.engine.schema) -- error
    local foo = Memory( env.engine, env.engine.schema) -- error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error

    for cand in input:iter() do
        cand.comment = '*' -- make sure this lua being called
        yield( cand )
    end
end

return F

Originally posted by @mirtlecn in https://github.com/hchunhui/librime-lua/issues/333#issuecomment-2094575070

shewer commented 6 months ago

檢查 整個方案中有無 leveldb 實例,且已 loaded ( 可以使用 :close() 關閉, 但可能再也開不起來了,因爲 user_dict 調用 db 是常開狀態 )

如果有和 方案中是否有衝突 上次討論中有使用 lua 環境製造 dbpool 的腳本 ,要取消 或是 用 :close() 全關閉.

確保 lua 環境中 所有 leveldb 實例中 與 user_dict 衝突

還是一個原則 儘量不要用 leveldb 開啓 user_dict 的db

mirtlecn commented 6 months ago

fcitx5-rime 似乎不会将错误写入 log,可能需要一些额外的操作。因此我选了 ibus-rime 测试

不知道你能否复现?

rime/home 有一个 issue 看 log 似乎也与此相关:

我觉得它是 bug 的原因是:

shewer commented 6 months ago

你可以先將 更新的 expent_translator.lua 重新下載 試試 commits --> commit 變數名 錯誤 這個錯誤 只會造成 callback 呼叫 失敗 沒有無法開啓userdict 的問題

mirtlecn commented 6 months ago

我没有用那个 lua,整个方案只用了一个测试 lua,就是上面写的那个。

mirtlecn commented 6 months ago

test_lua.zip

@shewer

这个错误在 rime_api_console 看不出来,其他前端均能复现。

Edit:我将部分补充更新到了主 issue 说明,请确认。

mirtlecn commented 6 months ago

如若诸位维护者有空,请确认此 issue 是否存在,或者给予指导。

个人已经在两台虚拟机的 fcitx5-rime 和 ibus-rime 稳定复现,且鼠须管、fcitx5 android 也有相关报错。rime/home 的一则 issue 据我判断也可能与此有关。

简易的测试方案和 lau 已经在主贴中提供。

@hchunhui @TsinamLeung (为当初引入该对象的贡献者)

感激不尽。

原因是此问题会影响多个前端,导致前端无法继续同步,且报错不指向 lua 错误而是指向用户词典被锁定。

TsinamLeung commented 6 months ago

如若诸位维护者有空,请确认此 issue 是否存在,或者给予指导。

个人已经在两台虚拟机的 fcitx5-rime 和 ibus-rime 稳定复现,且鼠须管、fcitx5 android 也有相关报错。rime/home 的一则 issue 据我判断也可能与此有关。

简易的测试方案和 lau 已经在主贴中提供。

@hchunhui @TsinamLeung (为当初引入该对象的贡献者)

感激不尽。

原因是此问题会影响多个前端,导致前端无法继续同步,且报错不指向 lua 错误而是指向用户词典被锁定。

我目前工作電腦還沒有測試還境,因此我見到你的Code我猜想是: 如果Memory沒有被回收則會佔用leveldb。 透過觀察前端的implementation,可以見到是rime_api的sync_user_data作全部同步操作的

RIME_API Bool RimeSyncUserData() {
  RimeCleanupAllSessions();
  Deployer& deployer(Service::instance().deployer());
  deployer.ScheduleTask("installation_update");
  deployer.ScheduleTask("backup_config_files");
  deployer.ScheduleTask("user_dict_sync");
  return Bool(deployer.StartMaintenance());
}

RIME_API void RimeCleanupAllSessions() {
  Service::instance().CleanupAllSessions();
}
void Service::CleanupAllSessions() {
  sessions_.clear();
}
// SessionMap session_
//  using SessionMap = map<SessionId, an<Session>>;

此步似乎是notify to release session.(這個操作應把Memory全部清空) 我認為短期解決方案是: 在每次F.func 中新建Memory實例,確保在function結束後能被Release(可能會導致效能下降)

之後可能需要lua component能夠捕獲到session deconstructing時的訊息,並傳遞給各Lua部件,調起sessionDestory的handler(需要用戶自行編寫) 這樣做的目的是因為,luaComponent是隨着librime加載而initialize的,僅當librime 關閉時,lua 的 context才會清空。因此需要自行管理自librime取得的組件

mirtlecn commented 6 months ago

在每次F.func 中新建Memory實例,確保在function結束後能被Release

感谢指导,我会测试并给予反馈

我已经测试并更新到主 issue,在 func 函数中使用 local foo = Memory 新建一个对象也会报错。

唯一不报错的,是在 init 函数中,用 local foo = Memory 新建一个对象。

shewer commented 6 months ago

在關閉 section 時 lua component 也會解構 LuaObj env 也會移除 可以試試 在 LuaMemory LuaTranslator .... 有 Lua member 解構時 加上lua->gc();

所有 關於 librime 物件 都要掛在 env 中 ,不能掛到 upvalue or global

--module file
local F={}
local m_mem
g_mem=nil
function F.init(env)
   env.mem = Memory(.....)
    F.mem = env.mem  -- NG
    m_mem = env.mem  -- NG
    g_mem = env.mem -- NG
end
function F.func(inp, env)
   local mem = env.mem   -- ok
end
mirtlecn commented 6 months ago

在關閉 section 時 lua component 也會解構 LuaObj env 也會移除 可以試試 在 LuaMemory LuaTranslator .... 有 Lua member 解構時 加上lua->gc();

所有 關於 librime 物件 都要掛在 env 中 ,不能掛到 upvalue or global

--module file
local F={}
local m_mem
g_mem=nil
function F.init(env)
   env.mem = Memory(.....)
    F.mem = env.mem  -- NG
    m_mem = env.mem  -- NG
    g_mem = env.mem -- NG
end
function F.func(inp, env)
   local mem = env.mem   -- ok
end
-- filter:
--   - lua_fiter@*test

local F = {}

function F.init( env )
    -- local foo = Memory( env.engine, env.engine.schema)  -- no error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error
end

function F.func( input, env )
    local foo = Memory( env.engine, env.engine.schema) -- error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error

    for cand in input:iter() do
        cand.comment = '*' -- make sure this lua being called
        yield( cand )
    end
end

return F
shewer commented 6 months ago

加上個 試試,在同步時 蜀發 engine 解構 lua_component 完成 fini 後 gc() Memory Component.Translator Processor ..... 要掛在 env
git apply patch

diff --git a/src/lua_gears.cc b/src/lua_gears.cc
index 9e1f704..0f8a01e 100644
--- a/src/lua_gears.cc
+++ b/src/lua_gears.cc
@@ -159,6 +159,7 @@ LuaFilter::~LuaFilter() {
       LOG(ERROR) << "LuaFilter::~LuaFilter of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 //--- LuaTranslator
@@ -186,6 +187,7 @@ LuaTranslator::~LuaTranslator() {
       LOG(ERROR) << "LuaTranslator::~LuaTranslator of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 //--- LuaSegmentor
@@ -213,6 +215,7 @@ LuaSegmentor::~LuaSegmentor() {
       LOG(ERROR) << "LuaSegmentor::~LuaSegmentor of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 //--- LuaProcessor
@@ -244,6 +247,7 @@ LuaProcessor::~LuaProcessor() {
       LOG(ERROR) << "LuaProcessor::~LuaProcessor of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 }  // namespace rime
shewer commented 6 months ago

在關閉 section 時 lua component 也會解構 LuaObj env 也會移除 可以試試 在 LuaMemory LuaTranslator .... 有 Lua member 解構時 加上lua->gc(); 所有 關於 librime 物件 都要掛在 env 中 ,不能掛到 upvalue or global

--module file
local F={}
local m_mem
g_mem=nil
function F.init(env)
   env.mem = Memory(.....)
    F.mem = env.mem  -- NG
    m_mem = env.mem  -- NG
    g_mem = env.mem -- NG
end
function F.func(inp, env)
   local mem = env.mem   -- ok
end
-- filter:
--   - lua_fiter@*test

local F = {}

function F.init( env )
    -- local foo = Memory( env.engine, env.engine.schema)  -- no error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error
end

function F.func( input, env )
    local foo = Memory( env.engine, env.engine.schema) -- error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error

    for cand in input:iter() do
        cand.comment = '*' -- make sure this lua being called
        yield( cand )
    end
end

return F

Memory 中有 OnCommit(ctx) 掛在 translation 中 怪怪的

function F.fini(env)
   env.mem=nil
   collectgarbage('collect')
end
mirtlecn commented 6 months ago

不好意思,改源码然后编译测试,我暂时没这个条件,虚拟机里面编译 boost 和 librime 很慢,会卡死。

主机是 Weasel,倒是能编译,但它不报这个错误。

function F.fini(env)
   env.mem=nil
   collectgarbage('collect')
end

这个我试试,之后反馈给你。不过我认为没有帮助,因为你看

在 func 函数内用 local foo = memory 的方式仍然会引起 leveldb 被锁定,不像是因为没有回收资源造成的错误

shewer commented 6 months ago

lifetime env 底下的 一定大於 func 下的 local var

在 func 中 local mem 參考 env.mem 執行完 回收 mem
func(inp,env) local mem = env.mem end -- distory mem [point to env.mem]

mirtlecn commented 6 months ago

lifetime env 底下的 一定大於 func 下的 local var

在 func 中 local mem 參考 env.mem 執行完 回收 mem func(inp,env) local mem = env.mem end -- distory mem [point to env.mem]

赋值 nil 并 collectgarbage('collect') 不会导致同步错误!

当前测试了 ibus-rime 是成功解决了问题。其他的还没测试,

@shewer

local F = {}

function F.init( env )
    env.mem = Memory( env.engine, env.engine.schema)
end

function F.func( input, env )
    for cand in input:iter() do
        cand.comment = '-' -- test if this lua has been called
        yield( cand )
    end
end

function F.fini(env)
   env.mem=nil
   collectgarbage('collect')
end

return F
mirtlecn commented 6 months ago

fcitx5-rime 测试也没有问题。

这样做了后,直接 update_user_dict 也不会有问题了。

想问下,我想保持对 librime 之前版本的兼容,希望直接在 fini 函数里面,手动回收 Memory,如之前的代码所示,应当没有负面效果吧?此操作不会在用户输入的情况下因回收资源而造成卡顿吧?

shewer commented 6 months ago

影響不大 , 只發生在 session close () 還有更頻繁的 LuaTranslation

hchunhui commented 6 months ago

依赖gc释放内存以外的资源并不是很合理,因为gc的时机本来就是不确定的。最好是能给 Memory 加一个 close()release() 之类的方法,在 fini() 里面手动确保释放掉它打开的词典。

shewer commented 6 months ago

根據上述狀況 ComponentReg 可能也有問題 , table_translator script_translator 也是屬於 Memory 類

Memory 成員有 字典(unique_ptr) 和 notifier 要如何解構 自己? env.mem:close() ?

原來的想法是 env (LuaObj) 在消滅時 env 下面的 userdata 就可以被回收了,所以在此時觸發一次 gc()

TsinamLeung commented 6 months ago

But never rely on GC if so it must be our mechanism

Get Outlook for Androidhttps://aka.ms/AAb9ysg


From: Shewer Lu @.> Sent: Sunday, May 12, 2024 11:11:19 AM To: hchunhui/librime-lua @.> Cc: Chiram @.>; Mention @.> Subject: Re: [hchunhui/librime-lua] 【Bug】Memory 对象会导致 Rime 无法同步 (Issue #335)

根“鲜�›r ComponentReg 可能也有†–} , table_translator script_translator 也是Œ凫 Memory 

Memory 成†T有 字典(unique_ptr) 和 notifier 要如何解˜‹ 自己? env.mem:close() ?

原淼南敕ㄊ env (LuaObj) 在消œ•r env 下面的 userdata 就可以被回收了,所以在此•r|l一次 gc()

― Reply to this email directly, view it on GitHubhttps://github.com/hchunhui/librime-lua/issues/335#issuecomment-2106101629, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AKU7ZT55IGWGCDCFH2IEPP3ZB3MVPAVCNFSM6AAAAABHHMEWIGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMBWGEYDCNRSHE. You are receiving this because you were mentioned.Message ID: @.***>