Skip to content

Commit 58fb699

Browse files
authored
[Collections] testPopulateTargetTrie sometimes gets stuck (#3522)
Motivation: `PackageCollectionsStorageTests/testPopulateTargetTrie` gets stuck sometimes because `targetTrieReady` is already set and `callback` is only invoked in the body of `memoize`. https://ci.swift.org/job/oss-swift-package-ubuntu-20_04-aarch64/79/console Modifications: Rearrange logic in `populateTargetTrie` such that `callback` is invoked outside of `memoize` body. Also, add `populateTargetTrieLock` to prevent more than one thread from calling `memoize` at the same time.
1 parent 50c958b commit 58fb699

File tree

2 files changed

+19
-16
lines changed

2 files changed

+19
-16
lines changed

Sources/PackageCollections/Storage/SQLitePackageCollectionsStorage.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ final class SQLitePackageCollectionsStorage: PackageCollectionsStorage, Closable
4545

4646
// Targets have in-memory trie in addition to SQLite FTS as optimization
4747
private let targetTrie = Trie<CollectionPackage>()
48-
private var targetTrieReady = ThreadSafeBox<Bool>()
48+
private var targetTrieReady: Bool?
49+
private let populateTargetTrieLock = Lock()
4950

5051
init(location: SQLite.Location? = nil, configuration: Configuration = .init(), diagnosticsEngine: DiagnosticsEngine? = nil) {
5152
self.location = location ?? .path(localFileSystem.swiftPMCacheDirectory.appending(components: "package-collection.db"))
@@ -503,7 +504,7 @@ final class SQLitePackageCollectionsStorage: PackageCollectionsStorage, Closable
503504
var matchingCollections = Set<Model.CollectionIdentifier>()
504505

505506
// Trie is more performant for target search; use it if available
506-
if self.targetTrieReady.get() ?? false {
507+
if self.populateTargetTrieLock.withLock({ self.targetTrieReady }) ?? false {
507508
do {
508509
switch type {
509510
case .exactMatch:
@@ -761,13 +762,18 @@ final class SQLitePackageCollectionsStorage: PackageCollectionsStorage, Closable
761762

762763
internal func populateTargetTrie(callback: @escaping (Result<Void, Error>) -> Void = { _ in }) {
763764
DispatchQueue.sharedConcurrent.async(group: nil, qos: .background, flags: .assignCurrentContext) {
764-
self.targetTrieReady.memoize {
765-
do {
765+
do {
766+
try self.populateTargetTrieLock.withLock { // Prevent race to populate targetTrie
767+
// Exit early if we've already done the computation before
768+
guard self.targetTrieReady == nil else {
769+
return
770+
}
771+
766772
// since running on low priority thread make sure the database has not already gone away
767773
switch (try self.withStateLock { self.state }) {
768774
case .disconnected, .disconnecting:
769-
callback(.success(()))
770-
return false
775+
self.targetTrieReady = false
776+
return
771777
default:
772778
break
773779
}
@@ -796,12 +802,11 @@ final class SQLitePackageCollectionsStorage: PackageCollectionsStorage, Closable
796802
}
797803
}
798804
}
799-
callback(.success(()))
800-
return true
801-
} catch {
802-
callback(.failure(error))
803-
return false
805+
self.targetTrieReady = true
804806
}
807+
callback(.success(()))
808+
} catch {
809+
callback(.failure(error))
805810
}
806811
}
807812
}

Tests/PackageCollectionsTests/PackageCollectionsStorageTests.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2020 Apple Inc. and the Swift project authors
4+
Copyright (c) 2020-2021 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -231,10 +231,8 @@ class PackageCollectionsStorageTests: XCTestCase {
231231
do {
232232
try tsc_await { callback in storage2.populateTargetTrie(callback: callback) }
233233

234-
do {
235-
let searchResult = try tsc_await { callback in storage2.searchTargets(query: targetName, type: .exactMatch, callback: callback) }
236-
XCTAssert(searchResult.items.count > 0, "should get results")
237-
}
234+
let searchResult = try tsc_await { callback in storage2.searchTargets(query: targetName, type: .exactMatch, callback: callback) }
235+
XCTAssert(searchResult.items.count > 0, "should get results")
238236
} catch {
239237
// It's possible that some platforms don't have support FTS
240238
XCTAssertEqual(false, storage2.useSearchIndices.get(), "populateTargetTrie should fail only if FTS is not available")

0 commit comments

Comments
 (0)