Skip to content

Commit bad10cd

Browse files
committed
Make SourceKitDRegistry an actor
1 parent b3d2df7 commit bad10cd

File tree

7 files changed

+45
-53
lines changed

7 files changed

+45
-53
lines changed

Sources/Diagnose/SourcekitdRequestCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public struct SourceKitdRequestCommand: AsyncParsableCommand {
4444
public func run() async throws {
4545
var requestString = try String(contentsOf: URL(fileURLWithPath: sourcekitdRequestPath))
4646

47-
let sourcekitd = try DynamicallyLoadedSourceKitD.getOrCreate(
47+
let sourcekitd = try await DynamicallyLoadedSourceKitD.getOrCreate(
4848
dylibPath: try! AbsolutePath(validating: sourcekitdPath)
4949
)
5050

Sources/SourceKitD/DynamicallyLoadedSourceKitD.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ public final class DynamicallyLoadedSourceKitD: SourceKitD {
4747
/// List of notification handlers that will be called for each notification.
4848
private var _notificationHandlers: [WeakSKDNotificationHandler] = []
4949

50-
public static func getOrCreate(dylibPath: AbsolutePath) throws -> SourceKitD {
51-
try SourceKitDRegistry.shared
50+
public static func getOrCreate(dylibPath: AbsolutePath) async throws -> SourceKitD {
51+
try await SourceKitDRegistry.shared
5252
.getOrAdd(dylibPath, create: { try DynamicallyLoadedSourceKitD(dylib: dylibPath) })
5353
}
5454

Sources/SourceKitD/SourceKitDRegistry.swift

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ extension NSLock {
3232
/// * To remove an existing instance, use `remove("path")`, but be aware that if there are any other
3333
/// references to the instances in the program, it can be resurrected if `getOrAdd` is called with
3434
/// the same path. See note on `remove(_:)`
35-
public final class SourceKitDRegistry {
36-
37-
/// Mutex protecting mutable state in the registry.
38-
let lock: NSLock = NSLock()
35+
public actor SourceKitDRegistry {
3936

4037
/// Mapping from path to active SourceKitD instance.
4138
var active: [AbsolutePath: SourceKitD] = [:]
@@ -47,26 +44,24 @@ public final class SourceKitDRegistry {
4744
public init() {}
4845

4946
/// The global shared SourceKitD registry.
50-
public static var shared: SourceKitDRegistry = SourceKitDRegistry()
47+
public static let shared: SourceKitDRegistry = SourceKitDRegistry()
5148

5249
/// Returns the existing SourceKitD for the given path, or creates it and registers it.
5350
public func getOrAdd(
5451
_ key: AbsolutePath,
5552
create: () throws -> SourceKitD
5653
) rethrows -> SourceKitD {
57-
try lock.withLock {
58-
if let existing = active[key] {
59-
return existing
60-
}
61-
if let resurrected = cemetary[key]?.value {
62-
cemetary[key] = nil
63-
active[key] = resurrected
64-
return resurrected
65-
}
66-
let newValue = try create()
67-
active[key] = newValue
68-
return newValue
54+
if let existing = active[key] {
55+
return existing
56+
}
57+
if let resurrected = cemetary[key]?.value {
58+
cemetary[key] = nil
59+
active[key] = resurrected
60+
return resurrected
6961
}
62+
let newValue = try create()
63+
active[key] = newValue
64+
return newValue
7065
}
7166

7267
/// Removes the SourceKitD instance registered for the given path, if any, from the set of active
@@ -77,22 +72,18 @@ public final class SourceKitDRegistry {
7772
/// the same path is looked up again before the original service is deinitialized, the original
7873
/// service is resurrected rather than creating a new instance.
7974
public func remove(_ key: AbsolutePath) -> SourceKitD? {
80-
lock.withLock {
81-
let existing = active.removeValue(forKey: key)
82-
if let existing = existing {
83-
assert(self.cemetary[key]?.value == nil)
84-
cemetary[key] = WeakSourceKitD(value: existing)
85-
}
86-
return existing
75+
let existing = active.removeValue(forKey: key)
76+
if let existing = existing {
77+
assert(self.cemetary[key]?.value == nil)
78+
cemetary[key] = WeakSourceKitD(value: existing)
8779
}
80+
return existing
8881
}
8982

9083
/// Remove all SourceKitD instances, including weak ones.
9184
public func clear() {
92-
lock.withLock {
93-
active.removeAll()
94-
cemetary.removeAll()
95-
}
85+
active.removeAll()
86+
cemetary.removeAll()
9687
}
9788
}
9889

Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
179179
guard let sourcekitd = toolchain.sourcekitd else { return nil }
180180
self.sourceKitServer = sourceKitServer
181181
self.swiftFormat = toolchain.swiftFormat
182-
self.sourcekitd = try DynamicallyLoadedSourceKitD.getOrCreate(dylibPath: sourcekitd)
182+
self.sourcekitd = try await DynamicallyLoadedSourceKitD.getOrCreate(dylibPath: sourcekitd)
183183
self.capabilityRegistry = workspace.capabilityRegistry
184184
self.serverOptions = options
185185
self.documentManager = DocumentManager()

Tests/DiagnoseTests/DiagnoseTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ private struct InProcessSourceKitRequestExecutor: SourceKitRequestExecutor {
211211
func run(request requestYaml: String) async throws -> SourceKitDRequestResult {
212212
logger.info("Sending request: \(requestYaml)")
213213

214-
let sourcekitd = try DynamicallyLoadedSourceKitD.getOrCreate(
214+
let sourcekitd = try await DynamicallyLoadedSourceKitD.getOrCreate(
215215
dylibPath: try! AbsolutePath(validating: sourcekitd.path)
216216
)
217217
let response = try await sourcekitd.run(requestYaml: requestYaml)

Tests/SourceKitDTests/SourceKitDRegistryTests.swift

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,49 +10,50 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import LSPTestSupport
1314
import SourceKitD
1415
import TSCBasic
1516
import XCTest
1617

1718
final class SourceKitDRegistryTests: XCTestCase {
1819

19-
func testAdd() throws {
20+
func testAdd() async throws {
2021
let registry = SourceKitDRegistry()
2122

22-
let a = try FakeSourceKitD.getOrCreate(AbsolutePath(validating: "/a"), in: registry)
23-
let b = try FakeSourceKitD.getOrCreate(AbsolutePath(validating: "/b"), in: registry)
24-
let a2 = try FakeSourceKitD.getOrCreate(AbsolutePath(validating: "/a"), in: registry)
23+
let a = try await FakeSourceKitD.getOrCreate(AbsolutePath(validating: "/a"), in: registry)
24+
let b = try await FakeSourceKitD.getOrCreate(AbsolutePath(validating: "/b"), in: registry)
25+
let a2 = try await FakeSourceKitD.getOrCreate(AbsolutePath(validating: "/a"), in: registry)
2526

2627
XCTAssert(a === a2)
2728
XCTAssert(a !== b)
2829
}
2930

30-
func testRemove() throws {
31+
func testRemove() async throws {
3132
let registry = SourceKitDRegistry()
3233

33-
let a = FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry)
34-
XCTAssert(registry.remove(try AbsolutePath(validating: "/a")) === a)
35-
XCTAssertNil(registry.remove(try AbsolutePath(validating: "/a")))
34+
let a = await FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry)
35+
await assertTrue(registry.remove(try AbsolutePath(validating: "/a")) === a)
36+
await assertNil(registry.remove(try AbsolutePath(validating: "/a")))
3637
}
3738

38-
func testRemoveResurrect() throws {
39+
func testRemoveResurrect() async throws {
3940
let registry = SourceKitDRegistry()
4041

4142
@inline(never)
42-
func scope(registry: SourceKitDRegistry) throws -> Int {
43-
let a = FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry)
43+
func scope(registry: SourceKitDRegistry) async throws -> Int {
44+
let a = await FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry)
4445

45-
XCTAssert(a === FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry))
46-
XCTAssert(registry.remove(try AbsolutePath(validating: "/a")) === a)
46+
await assertTrue(a === FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry))
47+
await assertTrue(registry.remove(try AbsolutePath(validating: "/a")) === a)
4748
// Resurrected.
48-
XCTAssert(a === FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry))
49+
await assertTrue(a === FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry))
4950
// Remove again.
50-
XCTAssert(registry.remove(try AbsolutePath(validating: "/a")) === a)
51+
await assertTrue(registry.remove(try AbsolutePath(validating: "/a")) === a)
5152
return (a as! FakeSourceKitD).token
5253
}
5354

54-
let id = try scope(registry: registry)
55-
let a2 = FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry)
55+
let id = try await scope(registry: registry)
56+
let a2 = await FakeSourceKitD.getOrCreate(try AbsolutePath(validating: "/a"), in: registry)
5657
XCTAssertNotEqual(id, (a2 as! FakeSourceKitD).token)
5758
}
5859
}
@@ -72,8 +73,8 @@ final class FakeSourceKitD: SourceKitD {
7273
nextToken += 1
7374
}
7475

75-
static func getOrCreate(_ path: AbsolutePath, in registry: SourceKitDRegistry) -> SourceKitD {
76-
return registry.getOrAdd(path, create: { Self.init() })
76+
static func getOrCreate(_ path: AbsolutePath, in registry: SourceKitDRegistry) async -> SourceKitD {
77+
return await registry.getOrAdd(path, create: { Self.init() })
7778
}
7879

7980
public func log(request: SKDRequestDictionary) {}

Tests/SourceKitDTests/SourceKitDTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import class TSCBasic.Process
2828
final class SourceKitDTests: XCTestCase {
2929
func testMultipleNotificationHandlers() async throws {
3030
let sourcekitdPath = await ToolchainRegistry.forTesting.default!.sourcekitd!
31-
let sourcekitd = try DynamicallyLoadedSourceKitD.getOrCreate(dylibPath: sourcekitdPath)
31+
let sourcekitd = try await DynamicallyLoadedSourceKitD.getOrCreate(dylibPath: sourcekitdPath)
3232
let keys = sourcekitd.keys
3333
let path = DocumentURI.for(.swift).pseudoPath
3434

0 commit comments

Comments
 (0)