@@ -26,47 +26,60 @@ import LanguageServerProtocol
2626/// conn.send(...) // handled by server
2727/// conn.close()
2828/// ```
29- ///
30- /// - Note: Unchecked sendable conformance because shared state is guarded by `queue`.
31- public final class LocalConnection : Connection , @unchecked Sendable {
32- enum State {
29+ public final class LocalConnection : Connection , Sendable {
30+ private enum State {
3331 case ready, started, closed
3432 }
3533
3634 /// A name of the endpoint for this connection, used for logging, e.g. `clangd`.
3735 private let name : String
3836
3937 /// The queue guarding `_nextRequestID`.
40- let queue : DispatchQueue = DispatchQueue ( label: " local-connection-queue " )
38+ private let queue : DispatchQueue = DispatchQueue ( label: " local-connection-queue " )
4139
42- var _nextRequestID : Int = 0
40+ /// - Important: Must only be accessed from `queue`
41+ nonisolated ( unsafe) private var _nextRequestID : Int = 0
4342
44- var state : State = . ready
43+ /// - Important: Must only be accessed from `queue`
44+ nonisolated ( unsafe) private var state : State = . ready
4545
46- var handler : MessageHandler ? = nil
46+ /// - Important: Must only be accessed from `queue`
47+ nonisolated ( unsafe) private var handler : MessageHandler ? = nil
4748
4849 public init ( name: String ) {
4950 self . name = name
5051 }
5152
5253 deinit {
53- if state != . closed {
54- close ( )
54+ queue. sync {
55+ if state != . closed {
56+ closeAssumingOnQueue ( )
57+ }
5558 }
5659 }
5760
5861 public func start( handler: MessageHandler ) {
59- precondition ( state == . ready)
60- state = . started
61- self . handler = handler
62+ queue. sync {
63+ precondition ( state == . ready)
64+ state = . started
65+ self . handler = handler
66+ }
6267 }
6368
64- public func close( ) {
69+ /// - Important: Must only be called from `queue`
70+ private func closeAssumingOnQueue( ) {
71+ dispatchPrecondition ( condition: . onQueue( queue) )
6572 precondition ( state != . closed)
6673 handler = nil
6774 state = . closed
6875 }
6976
77+ public func close( ) {
78+ queue. sync {
79+ closeAssumingOnQueue ( )
80+ }
81+ }
82+
7083 func nextRequestID( ) -> RequestID {
7184 return queue. sync {
7285 _nextRequestID += 1
@@ -81,7 +94,10 @@ public final class LocalConnection: Connection, @unchecked Sendable {
8194 \( notification. forLogging)
8295 """
8396 )
84- self . handler? . handle ( notification)
97+ guard let handler = queue. sync ( execute: { handler } ) else {
98+ return
99+ }
100+ handler. handle ( notification)
85101 }
86102
87103 public func send< Request: RequestType > (
@@ -97,7 +113,7 @@ public final class LocalConnection: Connection, @unchecked Sendable {
97113 """
98114 )
99115
100- guard let handler = self . handler else {
116+ guard let handler = queue . sync ( execute : { handler } ) else {
101117 logger. info (
102118 """
103119 Replying to request \( id, privacy: . public) with .serverCancelled because no handler is specified in \( self . name, privacy: . public)
0 commit comments