Skip to content

Move dictionary lookup into a resident service process - #12

Closed
ArnauvGilotra wants to merge 4 commits into
mainfrom
perf/dictionary-service
Closed

ArnauvGilotra wants to merge 4 commits into
mainfrom
perf/dictionary-service

Conversation

@ArnauvGilotra

Copy link
Copy Markdown
Collaborator

What

The dictionary was parsed on every switch into the IME. This introduces canto-dictd.exe: a per-user, windowless, resident process that loads the dictionary once and answers candidate-resolution requests over a per-user named pipe. The IME becomes a thin client, so loading happens once per logon session (at startup or first use), not per activation.

Pieces

  • CandidateEngine — the romanization → candidate segmentation/ordering logic, factored out of the text service verbatim so it runs where the dictionary lives (service, and the in-process fallback).
  • DictProtocol — a small message-framed pipe protocol (SETDICT / RESOLVE / PING), header-only, shared by client and service. Per-user pipe name (derived from the user SID).
  • DictService (canto-dictd.exe) — loads the user's selected dictionary at startup and serves lookups; single instance per user via a named mutex; windowless (WIN32 subsystem).
  • DictClient — connects (spawning the service if absent), pushes the dictionary selection (SETDICT), resolves buffers. Falls back to an in-process load if the service is unreachable, so the IME never breaks.
  • TextService no longer loads or holds the dictionary; BuildCandidates asks the client. The old background-load plumbing (WM_CANTO_DICT_READY / BeginLoadDictionary / OnDictionaryLoaded) is removed.
  • Installer ships the service, starts it at each user's logon (HKLM Run, running as that user so it reads their settings), and stops it on uninstall.

Design notes / open questions

  • One round-trip per keystroke: the whole candidate resolution runs server-side (not granular per-key lookups), so a keystroke is a single pipe request.
  • Authoritative selection: the service preloads from its own read of HKCU (defaulting to Traditional), but the IME's SETDICT is authoritative and corrects it (e.g. a Simplified variant) on first activation.
  • Fallback: if the service can't be reached, the DLL loads the dictionary in-process once and resolves locally — same result, just without the shared-load speedup.

CI-verified compiling only. The real latency win + the service lifecycle (logon autostart, on-demand spawn, reconnect, uninstall kill) need testing on the VM.

🤖 Generated with Claude Code

The dictionary was parsed on every switch into the IME. Introduce
canto-dictd.exe: a per-user, windowless, resident process that loads the
dictionary once and answers candidate-resolution requests over a per-user
named pipe. The IME becomes a thin client.

- CandidateEngine: the romanization -> candidate segmentation logic,
  factored out of the text service so it runs where the dictionary lives.
- DictProtocol: a small message-framed pipe protocol (SETDICT / RESOLVE /
  PING) shared by client and service.
- DictService: loads the user's dictionary at startup and serves lookups;
  single instance per user via a named mutex.
- DictClient: connects (spawning the service if absent), pushes the
  dictionary selection, resolves buffers. Falls back to an in-process
  load if the service is unreachable, so the IME never breaks.
- TextService no longer loads/holds the dictionary; BuildCandidates asks
  the client. Async-load plumbing removed.
- Installer ships the service, starts it at each user's logon (HKLM Run),
  and stops it on uninstall.

Loading now happens once per logon session (at startup or first use),
not per activation.
The service should run only for users who have added CantoIME, not for
everyone at logon.

- Drop the unconditional HKLM logon autostart.
- The IME registers a per-user logon autostart (HKCU Run) when it
  activates, i.e. when the keyboard has been added and used. Users who
  never add CantoIME never get it, so the service never starts for them.
  The IME also starts the service on demand at activation.
- On update, the installer restarts the service only if that per-user
  marker already exists (keyboard added before), running it as the
  original user; a fresh install does not start it.
- The installer stops any running service before copying files so an
  update swaps the executable cleanly, and removes the autostart marker
  on uninstall.
First-switch start was too late. Now the service starts early (at every
user's logon, via HKLM Run) but self-gates: at startup it checks via TSF
whether any CantoIME variant is enabled for the user (IsEnabledLanguage-
Profile across all five variants) and exits immediately if not, so it
never stays resident for users who have not added the keyboard.

- Service: KeyboardIsAdded() check before loading; inconclusive -> run.
- Installer: HKLM Run autostart for all users; start it once post-install
  (self-gates, so a fresh not-yet-added install just exits, an already-
  added update stays); still kills the old instance before an update.
- Dropped the IME per-user autostart marker and the installer's
  ServiceWasEnabled logic (superseded by the self-gate).
@Kenny2github
Kenny2github deleted the perf/dictionary-service branch October 23, 2026 02:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants