Various concurrency related tools, including Lock and async stream additions etc.
dependencies: [
.package(url: "https://github.com/ordo-one/package-concurrency-helpers", .upToNextMajor(from: "0.0.1")),
]
and then add the dependency to your target, e.g.:
.executableTarget(
name: "MyExecutableTarget",
dependencies: [
.product(name: "SwiftConcurrencyHelpers", package: "package-concurrency-helpers")
]),
-
Safely call a blocking function from an async context:
let result = await forBlockingFunc { // Call your blocking function here. }
-
Run async code in a non-async context:
let result = runSync { await someFunction() }
-
Simple locks, including a variant that sleeps while waiting for the lock:
let lock = Lock() let result = lock.withLock { … }
…and a variant that spins waiting for the lock:
let lock = Spinlock() let result = lock.withLock { … // Make sure whatever you do here is quick; other threads may be busy waiting. }
-
A simple thread-safe box for a value:
let safeValue = Protected(myValue) safeValue.write { // Have exclusive access to the contents here. $0.someField = true } safeValue.read { // Have exclusive, read-only access to the contents here. if $0.someField { … } }
-
An unsafe
Sendable
wrapper for value types:var someNonSendable = … let wrapper = UnsafeTransfer(someNonSendable) Task.detached { let value = wrapper.wrappedValue // Use `value` here, instead of the original name `someNonSendable`. }
There is also an
UnsafeMutableTransfer
variant, for mutable values. -
Yield a value to an
AsyncStream
with back-pressure support (waiting in a Concurrency-safe way until the stream is ready to accept the message):guard yieldWithBackPressure(message: …, to: continuation) else { // Stream was closed or this task has been cancelled. }
-
A simple reference-typed box for value types:
let a = Box(5) let b = a b.value += 1 // 'b' and 'a' both now hold 6.
It inherits the
Equatable
,Comparable
,CustomStringConvertible
, andCustomDebugStringConvertible
conformances of the contained type. -
Determine if a debugger is attached (a Swift version of Apple's C
AmIBeingDebugged
):if isBeingDebugged { // Running under, or attached by, a debugger of some kind. } else { // *Probably* not being debugged. }
Note that this is not foolproof; debuggers can hide themselves from detection.
-
A backport of Apple's AsyncStream.makeStream(of:bufferingPolicy:), to make it available in Swift 5.8 and earlier as well.
-
Calculate the next highest power of two, above a given integer:
let roundedUp = nearestPowerOf2(27) // 'roundedUp' is 32.
-
Track the rate of an event, and run a closure every N events:
let monitor = ProcessingRate(interval: 10) while true { monitor.checkpoint { // Runs every 10 calls to `checkpoint`. print("Current rate: \(monitor.ratePerSecond) Hz") } }
-
Count events, then at designated checkpoints run a closure & reset the count if a set duration has passed since the last reset (or since the counter was created, if never before reset).
let monitor = TimeIntervalCounter(clock: ContinuousClock(), timeInterval: .seconds(5)) while true { if monitor.incremenet() { // It has been at least five seconds since the monitor was last reset. } monitor.checkpoint { // Runs at most once every five seconds. print("Current count: \(monitor.count)") // The count will automatically be reset to zero when this closure exits. } }