@@ -26,47 +26,60 @@ import LanguageServerProtocol
26
26
/// conn.send(...) // handled by server
27
27
/// conn.close()
28
28
/// ```
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 {
33
31
case ready, started, closed
34
32
}
35
33
36
34
/// A name of the endpoint for this connection, used for logging, e.g. `clangd`.
37
35
private let name : String
38
36
39
37
/// The queue guarding `_nextRequestID`.
40
- let queue : DispatchQueue = DispatchQueue ( label: " local-connection-queue " )
38
+ private let queue : DispatchQueue = DispatchQueue ( label: " local-connection-queue " )
41
39
42
- var _nextRequestID : Int = 0
40
+ /// - Important: Must only be accessed from `queue`
41
+ nonisolated ( unsafe) private var _nextRequestID : Int = 0
43
42
44
- var state : State = . ready
43
+ /// - Important: Must only be accessed from `queue`
44
+ nonisolated ( unsafe) private var state : State = . ready
45
45
46
- var handler : MessageHandler ? = nil
46
+ /// - Important: Must only be accessed from `queue`
47
+ nonisolated ( unsafe) private var handler : MessageHandler ? = nil
47
48
48
49
public init ( name: String ) {
49
50
self . name = name
50
51
}
51
52
52
53
deinit {
53
- if state != . closed {
54
- close ( )
54
+ queue. sync {
55
+ if state != . closed {
56
+ closeAssumingOnQueue ( )
57
+ }
55
58
}
56
59
}
57
60
58
61
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
+ }
62
67
}
63
68
64
- public func close( ) {
69
+ /// - Important: Must only be called from `queue`
70
+ private func closeAssumingOnQueue( ) {
71
+ dispatchPrecondition ( condition: . onQueue( queue) )
65
72
precondition ( state != . closed)
66
73
handler = nil
67
74
state = . closed
68
75
}
69
76
77
+ public func close( ) {
78
+ queue. sync {
79
+ closeAssumingOnQueue ( )
80
+ }
81
+ }
82
+
70
83
func nextRequestID( ) -> RequestID {
71
84
return queue. sync {
72
85
_nextRequestID += 1
@@ -81,7 +94,10 @@ public final class LocalConnection: Connection, @unchecked Sendable {
81
94
\( notification. forLogging)
82
95
"""
83
96
)
84
- self . handler? . handle ( notification)
97
+ guard let handler = queue. sync ( execute: { handler } ) else {
98
+ return
99
+ }
100
+ handler. handle ( notification)
85
101
}
86
102
87
103
public func send< Request: RequestType > (
@@ -97,7 +113,7 @@ public final class LocalConnection: Connection, @unchecked Sendable {
97
113
"""
98
114
)
99
115
100
- guard let handler = self . handler else {
116
+ guard let handler = queue . sync ( execute : { handler } ) else {
101
117
logger. info (
102
118
"""
103
119
Replying to request \( id, privacy: . public) with .serverCancelled because no handler is specified in \( self . name, privacy: . public)
0 commit comments