Files
msime-windows/server/tests/src/test_serial_task_runner.cpp
T
fanlusky 8f2c81dd0f perf(server,engine): 选词学习的写库挪出按键线程
权重调整(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 清缓存,下一次查询即是新顺序。此前的“下一个键之前一定写完”换成了不阻塞
按键。
2026-09-27 16:09:51 +08:00

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);
}