1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2020 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import XCTest
@testable import NIO
import Dispatch
import NIOConcurrencyHelpers
class NIOThreadPoolTest: XCTestCase {
func testThreadNamesAreSetUp() {
let numberOfThreads = 11
let pool = NIOThreadPool(numberOfThreads: numberOfThreads)
pool.start()
defer {
XCTAssertNoThrow(try pool.syncShutdownGracefully())
}
var allThreadNames: Set<String> = []
let lock = Lock()
let threadNameCollectionSem = DispatchSemaphore(value: 0)
let threadBlockingSem = DispatchSemaphore(value: 0)
// let's use up all the threads
for i in (0..<numberOfThreads) {
pool.submit { s in
switch s {
case .cancelled:
XCTFail("work item \(i) cancelled")
case .active:
lock.withLock {
allThreadNames.formUnion([NIOThread.current.currentName ?? "n/a"])
}
threadNameCollectionSem.signal()
}
threadBlockingSem.wait()
}
}
// now, let's wait for all the threads to have done their work
(0..<numberOfThreads).forEach { _ in
threadNameCollectionSem.wait()
}
// and finally, let them exit
(0..<numberOfThreads).forEach { _ in
threadBlockingSem.signal()
}
let localAllThreads = lock.withLock { allThreadNames }
for threadNumber in (0..<numberOfThreads) {
XCTAssert(localAllThreads.contains("TP-#\(threadNumber)"), "\(localAllThreads)")
}
}
func testThreadPoolStartsMultipleTimes() throws {
let numberOfThreads = 1
let pool = NIOThreadPool(numberOfThreads: numberOfThreads)
pool.start()
defer {
XCTAssertNoThrow(try pool.syncShutdownGracefully())
}
let completionGroup = DispatchGroup()
// The lock here is arguably redundant with the dispatchgroup, but let's make
// this test thread-safe even if I screw up.
let lock = Lock()
var threadOne = Thread?.none
var threadTwo = Thread?.none
completionGroup.enter()
pool.submit { s in
precondition(s == .active)
lock.withLockVoid {
XCTAssertEqual(threadOne, nil)
threadOne = Thread.current
}
completionGroup.leave()
}
// Now start the thread pool again. This must not destroy existing threads, so our thread should be the same.
pool.start()
completionGroup.enter()
pool.submit { s in
precondition(s == .active)
lock.withLockVoid {
XCTAssertEqual(threadTwo, nil)
threadTwo = Thread.current
}
completionGroup.leave()
}
completionGroup.wait()
lock.withLockVoid {
XCTAssertNotNil(threadOne)
XCTAssertNotNil(threadTwo)
XCTAssertEqual(threadOne, threadTwo)
}
}
}
|