Skip to content

Commit fff9eb5

Browse files
authored
Merge pull request #1382 from ahoppen/stream-index-log
Instead of sending a message to the index log when an indexing task finishes, stream results as they come in
2 parents b7fa9a9 + 09ad77b commit fff9eb5

22 files changed

+226
-174
lines changed

Sources/CAtomics/include/CAtomics.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ typedef struct {
7070
} AtomicUInt32;
7171

7272
__attribute__((swift_name("AtomicUInt32.init(initialValue:)")))
73-
static inline AtomicUInt32 atomic_int_create(uint8_t initialValue) {
73+
static inline AtomicUInt32 atomic_int_create(uint32_t initialValue) {
7474
AtomicUInt32 atomic;
7575
atomic.value = initialValue;
7676
return atomic;

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ extension BuildServerBuildSystem: BuildSystem {
293293

294294
public func prepare(
295295
targets: [ConfiguredTarget],
296-
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
296+
logMessageToIndexLog: @Sendable (_ taskID: IndexTaskID, _ message: String) -> Void
297297
) async throws {
298298
throw PrepareNotSupportedError()
299299
}

Sources/SKCore/BuildSystem.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public protocol BuildSystem: AnyObject, Sendable {
169169
/// dependencies.
170170
func prepare(
171171
targets: [ConfiguredTarget],
172-
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
172+
logMessageToIndexLog: @escaping @Sendable (_ taskID: IndexTaskID, _ message: String) -> Void
173173
) async throws
174174

175175
/// If the build system has knowledge about the language that this document should be compiled in, return it.

Sources/SKCore/BuildSystemManager.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,9 @@ extension BuildSystemManager {
237237

238238
public func prepare(
239239
targets: [ConfiguredTarget],
240-
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
240+
logMessageToIndexLog: @escaping @Sendable (_ taskID: IndexTaskID, _ message: String) -> Void
241241
) async throws {
242-
try await buildSystem?.prepare(targets: targets, indexProcessDidProduceResult: indexProcessDidProduceResult)
242+
try await buildSystem?.prepare(targets: targets, logMessageToIndexLog: logMessageToIndexLog)
243243
}
244244

245245
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) async {

Sources/SKCore/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ add_library(SKCore STATIC
1111
ExperimentalFeatures.swift
1212
FallbackBuildSystem.swift
1313
FileBuildSettings.swift
14-
IndexProcessResult.swift
14+
IndexTaskID.swift
1515
MainFilesProvider.swift
1616
PathPrefixMapping.swift
1717
SplitShellCommand.swift

Sources/SKCore/CompilationDatabaseBuildSystem.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
125125

126126
public func prepare(
127127
targets: [ConfiguredTarget],
128-
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
128+
logMessageToIndexLog: @Sendable (_ taskID: IndexTaskID, _ message: String) -> Void
129129
) async throws {
130130
throw PrepareNotSupportedError()
131131
}

Sources/SKCore/IndexProcessResult.swift

Lines changed: 0 additions & 71 deletions
This file was deleted.

Sources/SKCore/IndexTaskID.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import struct TSCBasic.ProcessResult
14+
15+
/// The ID of a preparation or update indexstore task. This allows us to log messages from multiple concurrently running
16+
/// indexing tasks to the index log while still being able to differentiate them.
17+
public enum IndexTaskID: Sendable {
18+
case preparation(id: UInt32)
19+
case updateIndexStore(id: UInt32)
20+
21+
private static func numberToEmojis(_ number: Int, numEmojis: Int) -> String {
22+
let emojis = ["🟥", "🟩", "🟦", "🟧", "⬜️", "🟪", "⬛️", "🟨", "🟫"]
23+
var number = abs(number)
24+
var result = ""
25+
for _ in 0..<numEmojis {
26+
let (quotient, remainder) = number.quotientAndRemainder(dividingBy: emojis.count)
27+
result += emojis[remainder]
28+
number = quotient
29+
}
30+
return result
31+
}
32+
33+
/// Returns a two-character emoji string that allows easy differentiation between different task IDs.
34+
///
35+
/// This marker is prepended to every line in the index log.
36+
public var emojiRepresentation: String {
37+
// Multiply by 2 and optionally add 1 to make sure preparation and update index store have distinct IDs.
38+
// Run .hashValue to make sure we semi-randomly pick new emoji markers for new tasks
39+
switch self {
40+
case .preparation(id: let id):
41+
return Self.numberToEmojis((id * 2).hashValue, numEmojis: 2)
42+
case .updateIndexStore(id: let id):
43+
return Self.numberToEmojis((id * 2 + 1).hashValue, numEmojis: 2)
44+
}
45+
}
46+
}

Sources/SKSupport/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_library(SKSupport STATIC
1111
DocumentURI+CustomLogStringConvertible.swift
1212
FileSystem.swift
1313
LineTable.swift
14+
PipeAsStringHandler.swift
1415
Process+LaunchWithWorkingDirectoryIfPossible.swift
1516
Process+WaitUntilExitWithCancellation.swift
1617
Random.swift
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Foundation
14+
15+
/// Gathers data from a stdout or stderr pipe. When it has accumulated a full line, calls the handler to handle the
16+
/// string.
17+
public actor PipeAsStringHandler {
18+
/// Queue on which all data from the pipe will be handled. This allows us to have a
19+
/// nonisolated `handle` function but ensure that data gets processed in order.
20+
private let queue = AsyncQueue<Serial>()
21+
private var buffer = Data()
22+
23+
/// The closure that actually handles
24+
private let handler: @Sendable (String) -> Void
25+
26+
public init(handler: @escaping @Sendable (String) -> Void) {
27+
self.handler = handler
28+
}
29+
30+
private func handleDataFromPipeImpl(_ newData: Data) {
31+
self.buffer += newData
32+
while let newlineIndex = self.buffer.firstIndex(of: UInt8(ascii: "\n")) {
33+
// Output a separate log message for every line in the pipe.
34+
// The reason why we don't output multiple lines in a single log message is that
35+
// a) os_log truncates log messages at about 1000 bytes. The assumption is that a single line is usually less
36+
// than 1000 bytes long but if we merge multiple lines into one message, we might easily exceed this limit.
37+
// b) It might be confusing why sometimes a single log message contains one line while sometimes it contains
38+
// multiple.
39+
handler(String(data: self.buffer[...newlineIndex], encoding: .utf8) ?? "<invalid UTF-8>")
40+
buffer = buffer[buffer.index(after: newlineIndex)...]
41+
}
42+
}
43+
44+
public nonisolated func handleDataFromPipe(_ newData: Data) {
45+
queue.async {
46+
await self.handleDataFromPipeImpl(newData)
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)