-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Fix document corruption due to delayed evaluation in protocolFilter #10227
Conversation
|
In other words, I'm concerned that removing the calls to |
The protocol filter is invoked for each message in the queue, giving us an opportunity to make a change to the message or augment what happens when that message is processed. By using The bug here was specifically due to how deferring Removing |
I'm trying to reconcile this with the code that was retained. All of the other messages still use the const defaultHandler: (data: any, callback: (data: any) => Promise<void>) => Promise<void> = async (data, callback: (data: any) => void) => { clients.ActiveClient.notifyWhenLanguageClientReady(() => callback(data)); };
// const invoke1 = async (a: any, next: (a: any) => any) => { await clients.ActiveClient.awaitUntilLanguageClientReady(); return next(a); };
const invoke2 = async (a: any, b: any, next: (a: any, b: any) => any) => { await clients.ActiveClient.awaitUntilLanguageClientReady(); return next(a, b); };
const invoke3 = async (a: any, b: any, c: any, next: (a: any, b: any, c: any) => any) => { await clients.ActiveClient.awaitUntilLanguageClientReady(); return next(a, b, c); };
const invoke4 = async (a: any, b: any, c: any, d: any, next: (a: any, b: any, c: any, d: any) => any) => { await clients.ActiveClient.awaitUntilLanguageClientReady(); return next(a, b, c, d); }; |
I actually overlooked that potential deferral due to I don't believe anything we are doing within the protocol filter justifies delaying allowing those messages to continue to be processed immediately. Can you identify any specific requirement that protocolFilter delay processing a message until something in particular in the task queue has completed? |
It has nothing to do with the language client being ready, but everything to do with the call to It didn't sound like you need to do a deep copy of all message params. Don't we just need to save the document version to check whether the message should be discarded or not? |
This should address #10035 as well as some document corruption issues related to CDD.
The issue arose in protocolFilter.ts due to use of deferred lambdas. That caused some TypeScript objects to be re-evaluated later, when the message was actually sent, instead of within the protocolFilter handler at the time the message was intended to be processed. For example, when processing a
didChange
message, theTextDocument
has a particular document version number, but we would get a more recent version number whensendMessage
was actually called after deferred throughnotifyWhenLanguageClientReady
. The mismatching version numbers resulted in incorrect management of the contents of the buffer cache in the native process.It doesn't seem necessary to defer using
notifyWhenLanguageClientReady
, as the protocolFilter would not be processing messages if it were not ready to.I also removed the call to
processDelayedDidOpen
fromdidChange
, as it could attempt to defer as well. This was a failsafe anyway. We should not be getting calls todidChange
without having previously processed adidOpen
(delayed or otherwise). If we did, that would seem to indicate a more serious issue elsewhere.Alternatively, this perhaps could have been addressed using
await
with the protocol handler, as those can now be async. However, I expect that could be vulnerable to deadlock. For example: If it were to try to wait for message B before allowing delivery of message A to complete, that would deadlock the queue. Instead, the logic within protocolFilter should be careful to ensure thatsendMessage
occurs in a synchronous manner before returning.