Skip to content

Commit ac0a76d

Browse files
authored
Use swift-atomics (#1453)
Motivation: NIO deprecated its atomics in apple/swift-nio#2204. Modifications: - Use swift-atomics in the `QPSBenchmark` sub-package - Use a lock in the one test which used `NIOAtomic` Result: Not using deprecated code.
1 parent d772b68 commit ac0a76d

File tree

3 files changed

+30
-13
lines changed

3 files changed

+30
-13
lines changed

Performance/QPSBenchmark/Package.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ let package = Package(
2727
.package(url: "https://github.com/apple/swift-nio.git", from: "2.32.0"),
2828
.package(url: "https://github.com/apple/swift-log.git", from: "1.4.0"),
2929
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"),
30+
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"),
3031
.package(
3132
url: "https://github.com/swift-server/swift-service-lifecycle.git",
3233
from: "1.0.0-alpha"
@@ -42,6 +43,7 @@ let package = Package(
4243
name: "QPSBenchmark",
4344
dependencies: [
4445
.product(name: "GRPC", package: "grpc-swift"),
46+
.product(name: "Atomics", package: "swift-atomics"),
4547
.product(name: "NIOCore", package: "swift-nio"),
4648
.product(name: "NIOPosix", package: "swift-nio"),
4749
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),

Performance/QPSBenchmark/Sources/QPSBenchmark/Runtime/AsyncClient.swift

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import Atomics
1718
import BenchmarkUtils
1819
import Foundation
1920
import GRPC
@@ -168,8 +169,8 @@ final class AsyncQPSClient<RequestMakerType: RequestMaker>: QPSClient {
168169
/// Succeeds after a stop has been requested and all outstanding requests have completed.
169170
private var stopComplete: EventLoopPromise<Void>
170171

171-
private let running: NIOAtomic<Bool> = .makeAtomic(value: false)
172-
private let outstanding: NIOAtomic<Int> = .makeAtomic(value: 0)
172+
private let running = ManagedAtomic<Bool>(false)
173+
private let outstanding = ManagedAtomic<Int>(0)
173174

174175
private var requestMaker: RequestMakerType
175176

@@ -211,15 +212,20 @@ final class AsyncQPSClient<RequestMakerType: RequestMaker>: QPSClient {
211212
// - when a request finishes it will either start a new request or decrement the
212213
// the atomic counter (if we've been told to stop)
213214
// - if the counter drops to zero we're finished.
214-
let exchangedRunning = self.running.compareAndExchange(expected: false, desired: true)
215-
precondition(exchangedRunning, "launchRequests should only be called once")
215+
let exchangedRunning = self.running.compareExchange(
216+
expected: false,
217+
desired: true,
218+
ordering: .relaxed
219+
)
220+
precondition(exchangedRunning.exchanged, "launchRequests should only be called once")
216221

217222
// We only decrement the outstanding count when running has been changed back to false.
218-
let exchangedOutstanding = self.outstanding.compareAndExchange(
223+
let exchangedOutstanding = self.outstanding.compareExchange(
219224
expected: 0,
220-
desired: self.maxPermittedOutstandingRequests
225+
desired: self.maxPermittedOutstandingRequests,
226+
ordering: .relaxed
221227
)
222-
precondition(exchangedOutstanding, "launchRequests should only be called once")
228+
precondition(exchangedOutstanding.exchanged, "launchRequests should only be called once")
223229

224230
for _ in 0 ..< self.maxPermittedOutstandingRequests {
225231
self.requestMaker.makeRequest().whenComplete { _ in
@@ -229,11 +235,11 @@ final class AsyncQPSClient<RequestMakerType: RequestMaker>: QPSClient {
229235
}
230236

231237
private func makeRequest() {
232-
if self.running.load() {
238+
if self.running.load(ordering: .relaxed) {
233239
self.requestMaker.makeRequest().whenComplete { _ in
234240
self.makeRequest()
235241
}
236-
} else if self.outstanding.sub(1) == 1 {
242+
} else if self.outstanding.loadThenWrappingDecrement(ordering: .relaxed) == 1 {
237243
self.stopIsComplete()
238244
} // else we're no longer running but not all RPCs have finished.
239245
}
@@ -260,7 +266,7 @@ final class AsyncQPSClient<RequestMakerType: RequestMaker>: QPSClient {
260266
/// - returns: A future which can be waited on to signal when all activity has ceased.
261267
func stop() -> EventLoopFuture<Void> {
262268
self.requestMaker.requestStop()
263-
self.running.store(false)
269+
self.running.store(false, ordering: .relaxed)
264270
return self.stopComplete.futureResult
265271
}
266272
}

Tests/GRPCTests/StreamResponseHandlerRetainCycleTests.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,19 @@ final class StreamResponseHandlerRetainCycleTests: GRPCTestCase {
5757

5858
func testHandlerClosureIsReleasedOnceStreamEnds() {
5959
final class Counter {
60-
private let atomic = NIOAtomic.makeAtomic(value: 0)
61-
func increment() { self.atomic.add(1) }
60+
private let lock = Lock()
61+
private var _value = 0
62+
63+
func increment() {
64+
self.lock.withLockVoid {
65+
self._value += 1
66+
}
67+
}
68+
6269
var value: Int {
63-
self.atomic.load()
70+
return self.lock.withLock {
71+
self._value
72+
}
6473
}
6574
}
6675

0 commit comments

Comments
 (0)