Last active
September 28, 2022 15:54
-
-
Save groue/cd2a3f5b18b0ddfa2da6db469fc20cc3 to your computer and use it in GitHub Desktop.
Semaphore.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PlaygroundSupport | |
PlaygroundPage.current.needsIndefiniteExecution = true | |
Task { | |
do { | |
print("Start, 1 concurrent task") | |
let semaphore = Semaphore(value: 1) | |
await withThrowingTaskGroup(of: Void.self) { group in | |
for i in 0..<10 { | |
group.addTask { | |
try await semaphore.run { | |
print("Start task \(i)") | |
try await Task.sleep(nanoseconds: UInt64.random(in: 500_000_000...1_000_000_000)) | |
print("End task \(i)") | |
} | |
} | |
} | |
} | |
print("Done") | |
await semaphore.run { | |
print("One last because it's fun") | |
} | |
} | |
do { | |
print("Start, 2 concurrent tasks") | |
let semaphore = Semaphore(value: 2) | |
await withThrowingTaskGroup(of: Void.self) { group in | |
for i in 0..<10 { | |
group.addTask { | |
try await semaphore.run { | |
print("Start task \(i)") | |
try await Task.sleep(nanoseconds: UInt64.random(in: 500_000_000...1_000_000_000)) | |
print("End task \(i)") | |
} | |
} | |
} | |
} | |
print("Done") | |
await semaphore.run { | |
print("One last because it's fun") | |
} | |
} | |
do { | |
print("Start, 100 concurrent tasks") | |
let semaphore = Semaphore(value: 100) | |
await withThrowingTaskGroup(of: Void.self) { group in | |
for i in 0..<10 { | |
group.addTask { | |
try await semaphore.run { | |
print("Start task \(i)") | |
try await Task.sleep(nanoseconds: UInt64.random(in: 500_000_000...1_000_000_000)) | |
print("End task \(i)") | |
} | |
} | |
} | |
} | |
print("Done") | |
await semaphore.run { | |
print("One last because it's fun") | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// A Semaphore for Swift Concurrency | |
/// | |
/// https://forums.swift.org/t/actor-races/60439/47 | |
public actor Semaphore { | |
private var value: Int | |
private var continuations: [UnsafeContinuation<Void, Never>] = [] | |
init(value: Int) { | |
self.value = value | |
} | |
/// Waits for, or decrements, a semaphore. | |
/// | |
/// Decrement the counting semaphore. If the resulting value is less than | |
/// zero, this function waits for a signal to occur before returning. | |
public func wait() async { | |
if value <= 0 { | |
await withUnsafeContinuation { continuation in | |
continuations.insert(continuation, at: 0) | |
} | |
} | |
assert(value > 0) | |
value -= 1 | |
} | |
/// Signals (increments) a semaphore. | |
/// | |
/// Increment the counting semaphore. If the previous value was less than | |
/// zero, this function wakes a task currently waiting in ``wait()``. | |
@discardableResult | |
public func signal() -> Int { | |
value += 1 | |
if value > 0, let continuation = continuations.popLast() { | |
continuation.resume() | |
} | |
return value | |
} | |
/// Convenience method that waits, run an async function, and signals. | |
public func run<T>(execute: @Sendable @escaping () async throws -> T) async rethrows -> T { | |
await wait() | |
defer { signal() } | |
return try await execute() | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment