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

Fix a race condition in code completion #899

Merged
merged 3 commits into from
Oct 16, 2023

Conversation

ahoppen
Copy link
Collaborator

@ahoppen ahoppen commented Oct 12, 2023

Each sourcekitd uses a single, global code completion session but we were managing code completion sessions on the SwiftLanguageServer level. This had two issues:

  • Code completion is considered non-blocking on SourceKitServer and thus we could execute two concurrent code completion requests, which would conflict with each other.
  • You could have multiple SwiftLanguageServers that each have a connection to sourcekitd but share the same sourcekitd state. This shouldn't happen in the real world but does happen if we create multiple SourceKitServer instances in tests.

As a preparation for this do the following:

  • Remove client-side filtering for code completion
  • Change CodeCompletionSession to not have a circular reference to SourceKitServer
    • This just cleans up the layering slightly.

@ahoppen
Copy link
Collaborator Author

ahoppen commented Oct 12, 2023

@swift-ci Please test

Sources/SourceKitLSP/Swift/CodeCompletionSession.swift Outdated Show resolved Hide resolved
Sources/SourceKitLSP/Swift/CodeCompletionSession.swift Outdated Show resolved Hide resolved
Comment on lines +43 to +53
private static let completionQueue = AsyncQueue(.serial)

/// The code completion session for each sourcekitd instance.
///
/// `sourcekitd` has a global code completion session, that's why we need to
/// have a global mapping from `sourcekitd` to its currently active code
/// completion session.
///
/// Modification of code completion sessions should only happen on
/// `completionQueue`.
private static var completionSessions: [ObjectIdentifier: CodeCompletionSession] = [:]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't say I love this global state, is there any reason not to store it on SourceKitDImpl instead? FWIW the abstraction with the SourceKitD protocol doesn't seem super compelling to me since it seems like the only other conforming type is FakeSourceKitD, and that's only used to test the registry, it doesn't implement any of the other requirements. Seems like we could turn SourceKitDRegistry it into a generic DylibRegistry<T> type or something, and then delete the SourceKitD protocol.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the abstraction of SourceKitD an SourceKitDImpl is also something that’s on my mind. Expect a follow-up PR for that sometime in the future.

Can't say I love this global state, is there any reason not to store it on SourceKitDImpl instead?

It’s a layering decision: CodeCompletionSession is tied quite closely to the needs of LSP (it takes DocumentSnapshot and returns CompletionList, both of which are defined in LanguageServerProtocol) while the SourceKitD module is a fairly low-level wrapper around the C sourcekitd API. So, I think CodeCompletionSession doesn’t belong in the SourceKitD module and thus it can’t be a member of SourceKitD (unless you are suggesting that SourceKitDImpl has something like codeCompletionSession: Any and I assume that’s not what you are suggesting).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah that's fair, it's unfortunate because it seems like completion sessions are a sourcekid-specific concept. If we wanted to sink CodeCompletionSession down to a lower layer, it does seem like it doesn't have to return a CompletionList, it could return the raw response and let the language server handle transforming it. And it also seems like it doesn't need to store a full DocumentSnapshot, it only needs the DocumentURI I think (though maybe there's a use I'm missing). In particular it looks like the use in guard snapshot.version == self.snapshot.version is now redundant since that should be true by construction.

Either that or we could have a higher level wrapper that encapsulates both a SourceKitD and any session state it needs.

Doesn't have to be done in this PR, but I do think there's probably a better way to model this without the global state.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s re-consider when we remove the SourceKitD/SourceKitDImpl layering. I filed #901

@ahoppen
Copy link
Collaborator Author

ahoppen commented Oct 13, 2023

@swift-ci Please test

@ahoppen
Copy link
Collaborator Author

ahoppen commented Oct 13, 2023

@swift-ci Please test macOS

`SKCompletionOptions.serverSideFiltering` is `true` by default and I know of no editor that disables it. Delete it.

Fixes apple#863
rdar://116703670
…ourceKitServer`

This just clean up the layering slightly.
Each `sourcekitd` uses a single, global code completion session but we were managing code completion sessions on the `SwiftLanguageServer` level. This had two issues:
- Code completion is considered non-blocking on `SourceKitServer` and thus we could execute two concurrent code completion requests, which would conflict with each other.
- You could have multiple `SwiftLanguageServer`s that each have a connection to `sourcekitd` but share the same `sourcekitd` state. This shouldn't happen in the real world but does happen if we create multiple `SourceKitServer` instances in tests.
@ahoppen ahoppen force-pushed the ahoppen/completion-race-condition branch from 29f4b54 to 16aa082 Compare October 16, 2023 17:13
@ahoppen
Copy link
Collaborator Author

ahoppen commented Oct 16, 2023

@swift-ci Please test

@ahoppen
Copy link
Collaborator Author

ahoppen commented Oct 16, 2023

@swift-ci Please test Windows

@ahoppen ahoppen merged commit 80955f8 into apple:main Oct 16, 2023
3 checks passed
@ahoppen ahoppen deleted the ahoppen/completion-race-condition branch October 16, 2023 20:37
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.

Remove client-side filtering for code completion
2 participants