fix(windows-ime): eliminate thread deadlock causing AppHangB1 in host processes#745
fix(windows-ime): eliminate thread deadlock causing AppHangB1 in host processes#745scientificmonster wants to merge 2 commits into
Conversation
PR Reviewer Guide 🔍(Review updated until commit 03c7ef7)Here are some key observations to aid the review process:
|
…rocesses Replace SendMessageTimeoutW with PostMessage + WaitForMultipleObjects in SubmitTextFromPipe. The old code caused a deadlock when Deactivate() (called by TSF on the owner thread) blocked on thread_.join() while the pipe thread was blocked on SendMessageTimeoutW waiting for the owner thread's message pump. The fix introduces a shutdown_event that Stop() signals before joining, so the pipe thread can be unblocked from its wait even if the owner thread is blocked. This prevents the 2-second deadlock that caused AppHangB1 in Explorer, Chrome, Clash Verge, and other host processes. Fixes: Open-Less#665
7b7273b to
1ae4876
Compare
|
Persistent review updated to latest commit 1ae4876 |
appergb
left a comment
There was a problem hiding this comment.
Blocking issue: SubmitTextFromPipe now posts a pointer to a stack-allocated SubmitTextRequest via PostMessageW. If WaitForMultipleObjects returns because of timeout or shutdown_event, the function closes completion_event and returns while the owner thread may still later process kSubmitTextMessage, dereference the stale stack pointer, and call SetEvent on a closed handle. The request lifetime needs to outlive the posted message, or the message must be cancelled/acknowledged before returning. Full platform CI is also missing; currently only PR-Agent has run.
Heap-allocate SubmitTextRequest with atomic ref-counting (ref_count=2: pipe thread + queued message). Store string copies instead of pointers so the request's data survives HandleSubmitLine's return when the wait times out. Drain pending kSubmitTextMessage in DestroyMessageWindow to release any reference left by a shutdown-race message. The pipe thread releases its reference after WaitForMultipleObjects; the message thread releases its reference after processing (or the PeekMessage drain during teardown). Whichever releases last closes the event handle and deletes the request — no dangling pointer, no stale SetEvent.
|
Persistent review updated to latest commit 03c7ef7 |
User description
Summary
Fixes #665 — the intermittent
AppHangB1inexplorer.exe,chrome.exe,clash-verge.exeand other host processes caused byOpenLessIme.dll.Root Cause
OpenLessPipeServer::Stop()callsthread_.join(), which blocks the calling thread (often the TSF owner/UI thread). Meanwhile, the pipe server thread can be inSubmitTextFromPipe()→SendMessageTimeoutW(), which blocks waiting for the owner thread's message pump. Classic circular wait deadlock lasting up to 2 seconds (theSendMessageTimeoutWtimeout), causing the host process to appear hung. Windows detects this asAppHangB1and may restart the process.Fix
Replace
SendMessageTimeoutWwithPostMessageW+WaitForMultipleObjectsin the cross-thread path ofSubmitTextFromPipe. Ashutdown_event(manual-reset event owned byOpenLessPipeServer) is added to the wait array. WhenStop()is called, it signals the shutdown event before callingthread_.join(), unblocking the pipe thread immediately.Changes (4 files, +69/-14)
ipc_client.hHANDLE shutdown_event_memberipc_client.cppStop()before join; pass toSubmitTextFromPipetext_service.hHANDLE shutdown_event = nullptrparametertext_service.cppPostMessageW+WaitForMultipleObjectsreplacesSendMessageTimeoutW;MessageWindowProcsignals completion eventArchitecture
Test Plan
OpenLessIme.dllwith Visual Studio (openOpenLessIme.sln, build Release x64 + x86)windows-ime\x64\andx86\HKLM\SOFTWARE\Microsoft\CTF\TIP\{6B9F3F4F-5EE7-42D6-9C61-9F80B03A5D7D}\Enable = 1)AppHangB1events in Event ViewerOpenLessIme.dllis loaded in Explorer but no hangs occurEnvironment Confirmed Affected
explorer.exe,chrome.exe,clash-verge.exePR Type
Bug fix
Description
Replace SendMessageTimeoutW with PostMessageW + WaitForMultipleObjects to prevent deadlock
Introduce shutdown_event to unblock pipe thread during Stop() before join
Heap-allocate SubmitTextRequest with atomic ref-count to avoid use-after-free
Drain pending kSubmitTextMessage in DestroyMessageWindow for clean shutdown
Diagram Walkthrough
File Walkthrough
ipc_client.cpp
Manage shutdown event lifecycle and signal in Stop()openless-all/app/windows-ime/src/ipc_client.cpp
shutdown_event_in constructor/destructorStop()beforethread_.join()to unblock pipe threadshutdown_event_toSubmitTextFromPipefor the wait arraytext_service.cpp
Eliminate deadlock with event-driven communicationopenless-all/app/windows-ime/src/text_service.cpp
SendMessageTimeoutWwithPostMessageW+WaitForMultipleObjectsSubmitTextRequestwith atomic ref-count to preventuse-after-free
kSubmitTextMessageinDestroyMessageWindowfor cleanupipc_client.h
Add shutdown event handle memberopenless-all/app/windows-ime/src/ipc_client.h
HANDLE shutdown_event_member variabletext_service.h
Update method signature to accept shutdown eventopenless-all/app/windows-ime/src/text_service.h
HANDLE shutdown_eventparameter toSubmitTextFromPipe