Files
msime-windows/server/src/ipc/candidate_render_sync.h
T
Jia Suiandfanlusky 9beab473f4 fix(windows,server): 提交键慢响应改为分片让出等待,不再丢字或冻结宿主 (#982)
* fix(windows,server): 提交键慢响应改为分片让出等待,不再丢字或冻结宿主

* fix(windows): 提交回复轮询不阻塞宿主,补齐标点顶候选路径并保持结算顺序

- 标点顶高亮候选(WM_AsyncPunctuationCommit 无预取文本)也在处理函数里分片取回复,
  edit session 不再在宿主 UI 线程上阻塞 300 ms
- 轮询改为不阻塞:先查缓存、PeekNamedPipe,有帧才以零超时读,不再每片阻塞 16 ms
- 定时器只重派已开始等待的请求,按请求 id 升序结算,较新的提交排在仍在等的旧提交之后
- 处理函数取到 TransportUnavailable 时直接按歧义丢键并重置,不再交给 edit session
- 已在等回复的提交键在同一焦点下被组合纪元取代时按 DeliveryAmbiguous 处理,避免两端分叉
- 回复缓存淘汰不再淘汰刚写入的条目;CachePendingReply 与提交回复缓存共用一份逻辑
- 停用时显式 KillTimer(TIMER_COMMIT_REPLY_POLL);windows/AGENTS.md 同步新的提交预算

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-16 02:48:58 +08:00

49 lines
2.4 KiB
C++

#pragma once
#include <cstdint>
namespace FanyImeIpc
{
// A digit/space selection settles against the live page_words, but the user is looking at the
// asynchronously painted CandidatePageSnapshot. Pin-frequency reorders a page after every commit, so
// a selection that runs ahead of the painted frame commits a candidate the user never saw.
//
// The wait bound is load-bearing: the TSF side waits for a committing reply within
// FANY_IME_COMMIT_REPLY_TIMEOUT_MS (1500ms, windows/src/IPC/Ipc.h) and at the end of that window
// tears the pipe down and makes the user choose again. The wait is woken by the render echo, so a
// condition variable timeout (rounded up to one timer tick, ~16ms) is the only slack on top of
// this bound; together with candidate resolution it must stay well inside that budget. The
// frequency write runs after the reply and no longer competes for it. On a slow machine (battery,
// WebView2 paint) a paint regularly takes longer than 30ms, and settling against the unpainted
// page is exactly the "committed something I did not see" bug, so the bound leaves room for a slow
// paint. On timeout the caller continues with the current data and the diagnostic log records the
// miss. Do not raise this without re-deriving the TSF budget.
constexpr int kCandidateSelectionRenderWaitMaxMs = 80;
// Header-only pure policy so tests can pin it without linking the Windows/server stack.
// Generation 0 means "never published/rendered". A host-drawn (UI-less) or invisible candidate
// window has no on-screen list to match either, so none of those cases waits.
constexpr bool ShouldWaitForCandidateRender(std::uint64_t rendered, std::uint64_t current, bool uiless,
bool visible) noexcept
{
if (uiless || !visible)
{
return false;
}
if (current == 0 || rendered == 0)
{
return false;
}
return rendered < current;
}
// The render echo only moves forward. WebView2 echoes from async DOM callbacks, so a content-only
// update's callback can land after a dedup of the same content already echoed the newer generation;
// storing the older one left rendered one behind published and every selection waited out the full
// bound.
constexpr std::uint64_t AdvanceRenderedCandidateGeneration(std::uint64_t rendered, std::uint64_t echoed) noexcept
{
return echoed > rendered ? echoed : rendered;
}
} // namespace FanyImeIpc