Modern Concurrency and Synchronization for Swift.
##Features
Atomic<T>
class for numbers and strings.dispatch
keyword for firing off background routines.Chan<T>
for concurrent communication.Mutex
, Cond
, Once
, WaitGroup
Int
, Int8
, Int16
, Int32
, Int64
, UInt
, UInt8
, UInt16
, UInt32
, UInt64
, Float
, Double
, Bool
, String
==
, !=
, &&
, ||
, <=
, >=
, >
, <
, !
+
, -
, *
, /
, %
, <<
, >>
, ^
, &
, &+
, &-
, &*
, ++
, --
, +=
, -=
, *=
, /=
, %=
, +=
, <<=
, >>=
, ^=
, &=
var anum = IntA(100) // IntA is an alias for Atomic<Int>.
anum += 15 // Adds a value atomically.
let res = anum % 4 // Modulo operation atomically.
print("\(anum) \(res)") // prints '115 3'.
Safe adds an uncomplicated method for dispatching routines.
dispatch {
print("Background")
}
print("Foreground")
A new Chan<T>
class provides a clean and simple model for concurrently sharing objects. Chan<T>
is modeled after Go channels.
Sharing Memory by Communicating
let jobs = Chan<Int>(5) // buffered channel
let done = Chan<Bool>() // unbuffered channel
dispatch {
for ;; {
if let j = <-jobs {
print("received job \(j)")
} else {
print("received all jobs")
done <- true
return
}
}
}
for var j = 1; j <= 3; j++ {
jobs <- j
print("sent job \(j)")
}
jobs.close()
print("sent all jobs")
<-done
A channel can also be iterated through.
while let j = <-jobs {
print("received job \(j)")
}
print("received all jobs")
The _select
keyword is a multiway communications multiplexer that works on multiple channels. _select
, _case
, and _default
start with underscores so that they do not conflict with the select
, case
, and default
syscall and keywords. When a _select
encounters multiple channels with data, the chosen _case
is selected at random
let jobs1 = Chan<Int>()
let jobs2 = Chan<Int>()
dispatch {
for ;; {
_select {
_case(jobs1){ j in
print("received 1: \(j)")
}
_case(jobs2){ j in
print("received 2: \(j)")
}
}
}
}
for var j = 1; ; j++ {
jobs1 <- (j * 1000)
jobs2 <- (j * 2000)
NSThread.sleepForTimeInterval(1)
}
A _select
can contain a single _default
for non-blocking operations.
_select {
_case(jobs1){ j in
print("received 1: \(j)")
}
_case(jobs2){ j in
print("received 2: \(j)")
}
_default {
print("channels not ready")
}
}
Incredibly useful sync APIs.
let m = Mutex()
m.lock()
m.unlock()
m.lock {
// this block is locked
}
let c = Cond(Mutex())
c.wait() // wait for signal.
c.wait(0.25) // wait for signal or 250ms to pass.
c.signal() // signal to one wait.
c.broadcast() // signal to all waits.
func f(){
print("hey there")
}
let o = Once()
o.doit(f) // runs once
o.doit(f) // noop: cannot run twice
let dosomething : (NSTimeInterval, WaitGroup)->() = { (delay, wg) in
NSThread.sleepForTimeInterval(delay)
print("Function in background, duration: \(delay)")
wg.done()
}
let wg = WaitGroup()
wg.add(1)
dispatch { dosomething(0.40, wg) }
wg.add(1)
dispatch { dosomething(0.30, wg) }
wg.add(1)
dispatch { dosomething(0.15, wg) }
wg.add(1)
dispatch { dosomething(0.60, wg) }
wg.wait()
print("done")
##Installation (iOS and OS X)
Add the following to your Cartfile:
github "tidwall/Safe"
Then run carthage update
.
Follow the current instructions in Carthage’s README
for up to date installation instructions.
The import Safe
directive is required in order to access Safe features.
Add the following to your Podfile:
use_frameworks!
pod 'Safe'
Then run pod install
with CocoaPods 0.36 or newer.
The import Safe
directive is required in order to access Safe features.
Add the following to your Package.swift
dependencies: [
...
.Package(url: "https://github.com/tidwall/Safe", majorVersion: 1, minor: 2)
]
Then run swift build
.
The import Safe
directive is required in order to access Safe features.
Copy the Source/*.swift
file into your project.
There is no need for import Safe
when manually installing.
Josh Baker @tidwall
The Safe source code is available under the MIT License.