权重调整(AdjustCandidateRanking)和输入过的英文词(LearnEnteredEnglishWord) 原本和按键排在同一个 worker 队列里:每次选非首位候选都要在下一个键之前付一次 用户日志的提交和 fsync,触发阈值时还有 msime.db 的一次提交。磁盘忙或杀软扫描 时,这就是肉眼可见的卡顿。 - server/src/utils/serial_task_runner:单线程、按提交顺序执行、低优先级,Stop() 先执行完队列再退出,选词不会在退出时丢失。附单元测试。 - 两类写入改为投递给这个执行器。它们只调用 user_dictionary,不碰输入会话;唯一 的会话副作用——权重变化后清引擎候选缓存——经 EnqueueResetInputSessionCacheTask 回到 worker。删除对应的 TaskType 和 Task 字段。main 在 worker join 之后调用 ShutdownDictionaryWriter()。 - user_dictionary:默认日志连接改为每线程一条。FULLMUTEX 只保证单次调用安全, 保证不了事务:连接只有一个事务状态,写线程的 BEGIN IMMEDIATE..COMMIT 会把 worker 的读卷进来,worker 侧 UserDatabase 析构时还会把它回滚。 close_default_user_database() 递增代号,各线程下次访问时关闭旧连接。 - WorkerThread 取任务改为 move,不再每次复制整个 Task。 行为变化:选词后如果写入还没完成就立刻再打同一串,会短暂看到旧顺序;写完后 worker 清缓存,下一次查询即是新顺序。此前的“下一个键之前一定写完”换成了不阻塞 按键。
71 lines
1.9 KiB
C++
71 lines
1.9 KiB
C++
#include "utils/serial_task_runner.h"
|
|
#include "tests/includes/test_framework.h"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <stdexcept>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
TEST_CASE(serial_task_runner_runs_jobs_in_posting_order)
|
|
{
|
|
std::vector<int> order;
|
|
{
|
|
SerialTaskRunner runner;
|
|
for (int i = 0; i < 100; ++i)
|
|
{
|
|
// Only the runner thread touches `order`, so no lock is needed.
|
|
runner.Post([&order, i] { order.push_back(i); });
|
|
}
|
|
}
|
|
REQUIRE(order.size() == 100);
|
|
for (int i = 0; i < 100; ++i)
|
|
{
|
|
REQUIRE(order[i] == i);
|
|
}
|
|
}
|
|
|
|
TEST_CASE(serial_task_runner_stop_drains_queued_jobs)
|
|
{
|
|
std::atomic<int> done{0};
|
|
SerialTaskRunner runner(/*below_normal_priority=*/true);
|
|
// Hold the thread busy so the rest are still queued when Stop() arrives.
|
|
runner.Post([&done] {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
++done;
|
|
});
|
|
for (int i = 0; i < 10; ++i)
|
|
{
|
|
runner.Post([&done] { ++done; });
|
|
}
|
|
runner.Stop();
|
|
REQUIRE(done.load() == 11);
|
|
// Posting after Stop() is ignored rather than run or crashing.
|
|
runner.Post([&done] { ++done; });
|
|
runner.Stop();
|
|
REQUIRE(done.load() == 11);
|
|
}
|
|
|
|
TEST_CASE(serial_task_runner_survives_a_throwing_job)
|
|
{
|
|
std::atomic<int> done{0};
|
|
{
|
|
SerialTaskRunner runner;
|
|
runner.Post([] { throw std::runtime_error("write failed"); });
|
|
runner.Post([&done] { ++done; });
|
|
}
|
|
REQUIRE(done.load() == 1);
|
|
}
|
|
|
|
TEST_CASE(serial_task_runner_does_not_run_jobs_on_the_posting_thread)
|
|
{
|
|
const std::thread::id poster = std::this_thread::get_id();
|
|
std::thread::id runner_thread;
|
|
{
|
|
SerialTaskRunner runner;
|
|
runner.Post([&runner_thread] { runner_thread = std::this_thread::get_id(); });
|
|
}
|
|
REQUIRE(runner_thread != std::thread::id());
|
|
REQUIRE(runner_thread != poster);
|
|
}
|