Skip to content

Commit 07983fc

Browse files
committed
Sync with SwiftPM
1 parent ae8ccef commit 07983fc

File tree

3 files changed

+109
-9
lines changed

3 files changed

+109
-9
lines changed

Sources/TSCBasic/SynchronizedQueue.swift

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,27 @@
1111
/// This class can be used as a shared queue between multiple threads providing
1212
/// thread safe APIs.
1313
public final class SynchronizedQueue<Element> {
14-
/// Storage for queued elements.
15-
private var storage: [Element]
14+
15+
/// Linked list node.
16+
private final class Node {
17+
var value: Element
18+
var next: Node?
19+
init(_ value: Element) {
20+
self.value = value
21+
}
22+
}
23+
24+
/// Head node of the queue.
25+
private var head: Node? = nil
26+
27+
/// Tail node of the queue.
28+
private weak var tail: Node? = nil
1629

1730
/// Condition variable to block the thread trying dequeue and queue is empty.
1831
private var notEmptyCondition: Condition
1932

2033
/// Create a default instance of queue.
2134
public init() {
22-
storage = []
2335
notEmptyCondition = Condition()
2436
}
2537

@@ -29,7 +41,15 @@ public final class SynchronizedQueue<Element> {
2941
/// - element: The element to be enqueued.
3042
public func enqueue(_ element: Element) {
3143
notEmptyCondition.whileLocked {
32-
storage.append(element)
44+
let node = Node(element)
45+
if head == nil {
46+
head = node
47+
} else {
48+
tail?.next = node
49+
}
50+
// Update the tail node.
51+
tail = node
52+
3353
// Signal a thread blocked on dequeue.
3454
notEmptyCondition.signal()
3555
}
@@ -41,12 +61,17 @@ public final class SynchronizedQueue<Element> {
4161
public func dequeue() -> Element {
4262
return notEmptyCondition.whileLocked {
4363
// Wait until we have an element available in the queue.
44-
while storage.isEmpty {
64+
while head == nil {
4565
notEmptyCondition.wait()
4666
}
47-
48-
// FIXME: This is O(n) operation, optimize.
49-
return storage.removeFirst()
67+
68+
// There are elements in the queue, `head` is not nil.
69+
let element = head!.value
70+
71+
// Remove the first node.
72+
head = head!.next
73+
74+
return element
5075
}
5176
}
5277
}

Sources/TSCUtility/StringExtensions.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ extension String {
9696
}
9797

9898
public func spm_multilineIndent(count: Int) -> String {
99+
let indent = String(repeating: " ", count: count)
99100
return self
100101
.split(separator: "\n")
101-
.map{ String(repeating: " ", count: count) + $0 }
102+
.map { indent + $0 }
102103
.joined(separator: "\n")
103104
}
104105
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import XCTest
12+
13+
import TSCBasic
14+
import TSCTestSupport
15+
16+
class SynchronizedQueuePerfTests: XCTestCasePerf {
17+
18+
// Mock the UnitTest struct in SwiftPM/SwiftTestTool.swift
19+
struct Item {
20+
let productPath: AbsolutePath
21+
22+
let name: String
23+
24+
let testCase: String
25+
26+
var specifier: String {
27+
return testCase + "/" + name
28+
}
29+
}
30+
31+
32+
func testEnqueueDequeue_10000() {
33+
let queue = SynchronizedQueue<Item>()
34+
let test = Item(productPath: AbsolutePath.root, name: "TestName", testCase: "TestCaseName")
35+
measure {
36+
let N = 10000
37+
for _ in 0..<N {
38+
queue.enqueue(test)
39+
}
40+
for _ in 0..<N {
41+
let _ = queue.dequeue()
42+
}
43+
}
44+
}
45+
46+
func testEnqueueDequeue_1000() {
47+
let queue = SynchronizedQueue<Item>()
48+
let test = Item(productPath: AbsolutePath.root, name: "TestName", testCase: "TestCaseName")
49+
measure {
50+
let N = 1000
51+
for _ in 0..<N {
52+
queue.enqueue(test)
53+
}
54+
for _ in 0..<N {
55+
let _ = queue.dequeue()
56+
}
57+
}
58+
}
59+
60+
func testEnqueueDequeue_100() {
61+
let queue = SynchronizedQueue<Item>()
62+
let test = Item(productPath: AbsolutePath.root, name: "TestName", testCase: "TestCaseName")
63+
measure {
64+
let N = 100
65+
for _ in 0..<N {
66+
queue.enqueue(test)
67+
}
68+
for _ in 0..<N {
69+
let _ = queue.dequeue()
70+
}
71+
}
72+
}
73+
74+
}

0 commit comments

Comments
 (0)