Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign Mozc to not rely on synchronous edit sessions in Windows #821

Open
yukawa opened this issue Oct 15, 2023 · 0 comments
Open

Redesign Mozc to not rely on synchronous edit sessions in Windows #821

yukawa opened this issue Oct 15, 2023 · 0 comments

Comments

@yukawa
Copy link
Collaborator

yukawa commented Oct 15, 2023

Description

Spun off from

There are several places where Mozc requests synchronous edit sessions with explicitly setting TF_ES_SYNC. However, as discussed in #819, it'd be better to redesign Mozc to not rely on synchronous edit sessions for application compatibility reasons (perhaps solely because of Microsoft Word though).

Environment

  • OS: Windows 11 22H2
  • Related Applications: Microsoft Word for Microsoft 365 MSO (Version 2309 Build 16.0.16827.20166) 64 bit

References

Rules of Text Services - TSF Aware

https://learn.microsoft.com/en-us/archive/blogs/tsfaware/rules-of-text-services

The second rule of writing a text service is: Avoid Synchronous edit sessions. There are a number of text stores (namely, the 500 pound gorilla of text stores - Microsoft Word) that never grant synchronous edit sessions. (The only exception is when Microsoft Word asks for reconversions.) If you start your design with this assumption, you will avoid a painful redesign when you start testing with Microsoft Word, and you find that nothing works.

The third rule of writing a text service is: Text service code is hard to maintain. Because document modifications have to be done in an edit session, which may not run synchronously, you can't write code that expects to, for example, grab some text, inspect it, and do something from (for example) an event handler, or a keystroke handler. Instead, your handlers consist of a lot of edit sessions, and the edit sessions usually end by firing a callback (or expecting some other event handler to fire as a result of the change in the edit session).

The end result is a big pile of spaghetti code. The only way to avoid this would be to write your text service in a language other than C, which has the following feature set:

  • supports closures and anonymous functions (to simplify the inherently asynchronous nature of text services)
  • doesn't have a large runtime (because text services get loaded into a lot of processes),
  • compiles to efficient native code (because text services run inside the event loop, and have to be fast)
  • supports COM efficiently (because TSF is COM-based)

As far as I know, no language has that feature set. The last three features are much more important than the first one; if your text service slows down the system by 50%, or increases the working set of the typical application by 30%, people aren't going to leave your text service running for very long.

Luckily, C++ has become much more powerful since the original article was written 17 years ago and the first feature is already available.

As a side note, Microsoft has been working on making Text Services Framework out-of-process, which is expected to mitigate these issues a lot when it becomes available to 3rd party IME developers.

Support Dictation With Text Services Framework - MSDN Magazine 2007 July

https://learn.microsoft.com/en-us/archive/msdn-magazine/2007/july/speak-up-support-dictation-with-text-services-framework

There are only a few instances where your application must grant a synchronous lock request. Your application must grant a synchronous lock if your application (or control) asks a text service to do something such as correcting text (via ITfFunctionProvider, the use of which is outside the scope of this article). Your application must also grant a synchronous lock whenever you call any method on the ITfKeystrokeMgr interface. Your application must also grant a synchronous lock when you do not use ITfKeystrokeMgr, and the lock request comes from within GetMessage or PeekMessage.

Aside from these exceptions, your application is not required to grant a synchronous lock request; it is perfectly allowable to only grant asynchronous lock requests. It is also perfectly allowable to treat asynchronous lock requests as synchronous lock requests.

From our observation in #819 and the previous quote in MSDN Magazine, it looks to be safer to assume the following rule can never be guaranteed in Microsoft Word.

Your application must also grant a synchronous lock whenever you call any method on the ITfKeystrokeMgr interface. Your application must also grant a synchronous lock when you do not use ITfKeystrokeMgr, and the lock request comes from within GetMessage or PeekMessage

ITextStoreACP::RequestLock method (textstor.h)

https://learn.microsoft.com/en-us/windows/win32/api/textstor/nf-textstor-itextstoreacp-requestlock

This method uses the ITextStoreACPSink::OnLockGranted method to lock the document. Applications must never modify the document or send change notifications using the ITextStoreACPSink::OnTextChange method from within the ITextStoreACP::RequestLock method. If the application has pending changes to report, the application can only respond to the asynchronous lock request.

Applications should not attempt to queue multiple ITextStoreACP::RequestLock method calls, because the application requires only a single callback. If the caller makes several read requests and one or more write requests, however, the callback should be for write access.

Successful requests for synchronous locks supersede requests for asynchronous locks. Unsuccessful requests for synchronous locks do not supersede requests for asynchronous locks. The implementation must still serve the outstanding asynchronous request, if one exists.

If the lock is granted before the ITextStoreACP::RequestLock method returns, the phrSession parameter will receive the HRESULT returned by the ITextStoreACPSink::OnLockGranted method. If the call is successful, but the lock will be granted later, the phrSession parameter receives the TS_S_ASYNC flag. The phrSession parameter should be ignored if ITextStoreACP::RequestLock returns anything other than S_OK.

A caller should never call this method reentrantly, except in the case that the caller holds a read-only lock. In this case the method can be called reentrantly to ask for an asynchronous write lock. The write lock will be granted later, after the read-only lock ends.

For more information about document locks, see Document Locks.

@yukawa yukawa changed the title Resign Mozc for Windows to not rely on synchronous edit sessions Redesign Mozc for Windows to not rely on synchronous edit sessions Oct 15, 2023
@yukawa yukawa changed the title Redesign Mozc for Windows to not rely on synchronous edit sessions Redesign Mozc to not rely on synchronous edit sessions in Windows Oct 15, 2023
hiroyuki-komatsu pushed a commit that referenced this issue Oct 16, 2023
This is a workaround for Microsoft Word's suddenly stopping accepting
synchronous edit sessions after moving ruler UI.

While MS Word's stack trace after entering the failure mode strongly
implies that this is an unexpected behavior in Microsoft Word [1],
realistically Mozc needs to work around this by avoiding synchronous
edit sessions whenever possible as TSF team has recommended [2].

Also MS-IME in Windows 11 22H2 does use asynchronous edit sessions
even from the key event handler unless

  "Use previous version of Microsoft IME"

settings is enabled.  This means that stopping relying on synchronous
edit sessions in Mozc also benefits the general app compatibility.

The problem is that redesigning edit session handling is a relatively
big project [3].  As a quick workaround, this commit aims to address
the main typing issue in MS Word by tweaking 'OnOutputReceivedImpl'.
The following features remains to be unavailable in the failure mode.

 * Reconversion triggered from IMEs.
 * Undo-commit

Closes #819 with known issues.

 [1]: #819
 [2]: https://learn.microsoft.com/en-us/archive/blogs/tsfaware/rules-of-text-services
 [3]: #821

PiperOrigin-RevId: 573696936
coooooooozy pushed a commit to coooooooozy/mozc that referenced this issue Nov 26, 2023
This is a workaround for Microsoft Word's suddenly stopping accepting
synchronous edit sessions after moving ruler UI.

While MS Word's stack trace after entering the failure mode strongly
implies that this is an unexpected behavior in Microsoft Word [1],
realistically Mozc needs to work around this by avoiding synchronous
edit sessions whenever possible as TSF team has recommended [2].

Also MS-IME in Windows 11 22H2 does use asynchronous edit sessions
even from the key event handler unless

  "Use previous version of Microsoft IME"

settings is enabled.  This means that stopping relying on synchronous
edit sessions in Mozc also benefits the general app compatibility.

The problem is that redesigning edit session handling is a relatively
big project [3].  As a quick workaround, this commit aims to address
the main typing issue in MS Word by tweaking 'OnOutputReceivedImpl'.
The following features remains to be unavailable in the failure mode.

 * Reconversion triggered from IMEs.
 * Undo-commit

Closes google#819 with known issues.

 [1]: google#819
 [2]: https://learn.microsoft.com/en-us/archive/blogs/tsfaware/rules-of-text-services
 [3]: google#821

PiperOrigin-RevId: 573696936
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

No branches or pull requests

1 participant