Skip to content

Commit 0e0c8f3

Browse files
committed
Use Swift Concurrency in context initialization
1 parent 15dde00 commit 0e0c8f3

File tree

1 file changed

+41
-82
lines changed

1 file changed

+41
-82
lines changed

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 41 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public class DocumentationContext {
218218
self.linkResolver = LinkResolver(dataProvider: dataProvider)
219219

220220
ResolvedTopicReference.enableReferenceCaching(for: inputs.id)
221-
try register()
221+
try await register()
222222
}
223223

224224
/// Perform semantic analysis on a given `document` at a given `source` location and append any problems found to `problems`.
@@ -1931,7 +1931,7 @@ public class DocumentationContext {
19311931
/**
19321932
Register a documentation bundle with this context.
19331933
*/
1934-
private func register() throws {
1934+
private func register() async throws {
19351935
try shouldContinueRegistration()
19361936

19371937
let currentFeatureFlags: FeatureFlags?
@@ -1963,111 +1963,61 @@ public class DocumentationContext {
19631963
}
19641964
}
19651965

1966-
// Note: Each bundle is registered and processed separately.
1967-
// Documents and symbols may both reference each other so the bundle is registered in 4 steps
1968-
1969-
// In the bundle discovery phase all tasks run in parallel as they don't depend on each other.
1970-
let discoveryGroup = DispatchGroup()
1971-
let discoveryQueue = DispatchQueue(label: "org.swift.docc.Discovery", qos: .unspecified, attributes: .concurrent, autoreleaseFrequency: .workItem)
1972-
1973-
let discoveryError = Synchronized<(any Error)?>(nil)
1966+
// Documents and symbols may both reference each other so the inputs is registered in 4 steps
19741967

1975-
// Load all bundle symbol graphs into the loader.
1976-
var symbolGraphLoader: SymbolGraphLoader!
1977-
var hierarchyBasedResolver: PathHierarchyBasedLinkResolver!
1978-
1979-
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in
1980-
symbolGraphLoader = SymbolGraphLoader(
1968+
// Load symbol information and construct data structures that only rely on symbol information.
1969+
async let loadSymbols = { [signposter, inputs, dataProvider, configuration] in
1970+
var symbolGraphLoader = SymbolGraphLoader(
19811971
bundle: inputs,
19821972
dataProvider: dataProvider,
19831973
symbolGraphTransformer: configuration.convertServiceConfiguration.symbolGraphTransformer
19841974
)
19851975

1986-
do {
1987-
try signposter.withIntervalSignpost("Load symbols", id: signposter.makeSignpostID()) {
1976+
try signposter.withIntervalSignpost("Load symbols", id: signposter.makeSignpostID()) {
1977+
try autoreleasepool {
19881978
try symbolGraphLoader.loadAll()
19891979
}
1990-
hierarchyBasedResolver = signposter.withIntervalSignpost("Build PathHierarchy", id: signposter.makeSignpostID()) {
1980+
}
1981+
try shouldContinueRegistration()
1982+
let hierarchyBasedResolver = signposter.withIntervalSignpost("Build PathHierarchy", id: signposter.makeSignpostID()) {
1983+
autoreleasepool {
19911984
PathHierarchyBasedLinkResolver(pathHierarchy: PathHierarchy(
19921985
symbolGraphLoader: symbolGraphLoader,
19931986
bundleName: urlReadablePath(inputs.displayName),
19941987
knownDisambiguatedPathComponents: configuration.convertServiceConfiguration.knownDisambiguatedSymbolPathComponents
19951988
))
19961989
}
1997-
1998-
self.snippetResolver = SnippetResolver(symbolGraphLoader: symbolGraphLoader)
1999-
} catch {
2000-
// Pipe the error out of the dispatch queue.
2001-
discoveryError.sync({
2002-
if $0 == nil { $0 = error }
2003-
})
20041990
}
2005-
}
1991+
1992+
let snippetResolver = SnippetResolver(symbolGraphLoader: symbolGraphLoader)
1993+
1994+
return (symbolGraphLoader, hierarchyBasedResolver, snippetResolver)
1995+
}()
20061996

2007-
// First, all the resources are added since they don't reference anything else.
2008-
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in
2009-
do {
2010-
try signposter.withIntervalSignpost("Load resources", id: signposter.makeSignpostID()) {
2011-
try self.registerMiscResources()
2012-
}
2013-
} catch {
2014-
// Pipe the error out of the dispatch queue.
2015-
discoveryError.sync({
2016-
if $0 == nil { $0 = error }
2017-
})
1997+
// Load resources like images and videos
1998+
async let loadResources: Void = try signposter.withIntervalSignpost("Load resources", id: signposter.makeSignpostID()) {
1999+
try autoreleasepool {
2000+
try self.registerMiscResources()
20182001
}
20192002
}
20202003

2021-
// Second, all the documents and symbols are added.
2022-
//
2023-
// Note: Documents and symbols may look up resources at this point but shouldn't lookup other documents or
2024-
// symbols or attempt to resolve links/references since the topic graph may not contain all documents
2025-
// or all symbols yet.
2026-
var result: (
2027-
tutorialTableOfContentsResults: [SemanticResult<TutorialTableOfContents>],
2028-
tutorials: [SemanticResult<Tutorial>],
2029-
tutorialArticles: [SemanticResult<TutorialArticle>],
2030-
articles: [SemanticResult<Article>],
2031-
documentationExtensions: [SemanticResult<Article>]
2032-
)!
2033-
2034-
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in
2035-
do {
2036-
result = try signposter.withIntervalSignpost("Load documents", id: signposter.makeSignpostID()) {
2037-
try self.registerDocuments()
2038-
}
2039-
} catch {
2040-
// Pipe the error out of the dispatch queue.
2041-
discoveryError.sync({
2042-
if $0 == nil { $0 = error }
2043-
})
2004+
// Load documents
2005+
async let loadDocuments = try signposter.withIntervalSignpost("Load documents", id: signposter.makeSignpostID()) {
2006+
try autoreleasepool {
2007+
try self.registerDocuments()
20442008
}
20452009
}
20462010

2047-
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in
2048-
do {
2049-
try signposter.withIntervalSignpost("Load external resolvers", id: signposter.makeSignpostID()) {
2050-
try linkResolver.loadExternalResolvers(dependencyArchives: configuration.externalDocumentationConfiguration.dependencyArchives)
2051-
}
2052-
} catch {
2053-
// Pipe the error out of the dispatch queue.
2054-
discoveryError.sync({
2055-
if $0 == nil { $0 = error }
2056-
})
2011+
// Load any external resolvers
2012+
async let loadExternalResolvers: Void = try signposter.withIntervalSignpost("Load external resolvers", id: signposter.makeSignpostID()) {
2013+
try autoreleasepool {
2014+
try linkResolver.loadExternalResolvers(dependencyArchives: configuration.externalDocumentationConfiguration.dependencyArchives)
20572015
}
20582016
}
20592017

2060-
discoveryGroup.wait()
2061-
2062-
try shouldContinueRegistration()
2063-
2064-
// Re-throw discovery errors
2065-
if let encounteredError = discoveryError.sync({ $0 }) {
2066-
throw encounteredError
2067-
}
2068-
20692018
// All discovery went well, process the inputs.
2070-
let (tutorialTableOfContentsResults, tutorials, tutorialArticles, allArticles, documentationExtensions) = result
2019+
let (tutorialTableOfContentsResults, tutorials, tutorialArticles, allArticles, documentationExtensions) = try await loadDocuments
2020+
try shouldContinueRegistration()
20712021
var (otherArticles, rootPageArticles) = splitArticles(allArticles)
20722022

20732023
let globalOptions = (allArticles + documentationExtensions).compactMap { article in
@@ -2107,7 +2057,10 @@ public class DocumentationContext {
21072057
options = globalOptions.first
21082058
}
21092059

2060+
let (symbolGraphLoader, hierarchyBasedResolver, snippetResolver) = try await loadSymbols
2061+
try shouldContinueRegistration()
21102062
self.linkResolver.localResolver = hierarchyBasedResolver
2063+
self.snippetResolver = snippetResolver
21112064
hierarchyBasedResolver.addMappingForRoots(bundle: inputs)
21122065
for tutorial in tutorials {
21132066
hierarchyBasedResolver.addTutorial(tutorial)
@@ -2120,9 +2073,10 @@ public class DocumentationContext {
21202073
}
21212074

21222075
registerRootPages(from: rootPageArticles)
2076+
21232077
try registerSymbols(symbolGraphLoader: symbolGraphLoader, documentationExtensions: documentationExtensions)
21242078
// We don't need to keep the loader in memory after we've registered all symbols.
2125-
symbolGraphLoader = nil
2079+
_ = consume symbolGraphLoader
21262080

21272081
try shouldContinueRegistration()
21282082

@@ -2149,12 +2103,17 @@ public class DocumentationContext {
21492103
try shouldContinueRegistration()
21502104
}
21512105

2106+
_ = try await loadExternalResolvers
2107+
21522108
// Third, any processing that relies on resolving other content is done, mainly resolving links.
21532109
preResolveExternalLinks(semanticObjects:
21542110
tutorialTableOfContentsResults.map(referencedSemanticObject) +
21552111
tutorials.map(referencedSemanticObject) +
21562112
tutorialArticles.map(referencedSemanticObject))
21572113

2114+
// References to resources aren't used until the links are resolved
2115+
_ = try await loadResources
2116+
21582117
resolveLinks(
21592118
tutorialTableOfContents: tutorialTableOfContentsResults,
21602119
tutorials: tutorials,

0 commit comments

Comments
 (0)