Skip to content

Instantly share code, notes, and snippets.

@a-voronov
Last active July 13, 2018 15:25
Show Gist options
  • Save a-voronov/85936fb6d491fb4b5625fd1941959639 to your computer and use it in GitHub Desktop.
Save a-voronov/85936fb6d491fb4b5625fd1941959639 to your computer and use it in GitHub Desktop.
Concurrent Reads, Blocking Write
class ReadWriteQueue {
private let specificKey = DispatchSpecificKey<String>()
private let queue: DispatchQueue
private var isAlreadyInQueue: Bool {
return DispatchQueue.getSpecific(key: specificKey) == queue.label
}
init(label: String = "read-write.queue") {
queue = DispatchQueue(label: label, attributes: .concurrent)
queue.setSpecific(key: specificKey, value: label)
}
deinit {
queue.setSpecific(key: specificKey, value: nil)
}
// solving readers-writers problem: any amount of readers can access data at a time, but only one writer is allowed at a time
// - reads are executed concurrently on executing queue, but are executed synchronously on a calling queue
// - write blocks executing queue, but is executed asynchronously on a calling queue so it doesn't have to wait
// note:
// it's fine to have async write, and sync reads, because write blocks queue and reads are executed synchronously;
// so if we want ro read after writing, we'll still be waiting (reads are sync) for write to finish and allow reads to execute;
func write(_ work: @escaping () -> Void) {
if isAlreadyInQueue {
work()
} else {
queue.async(flags: .barrier, execute: work)
}
}
// if we're already executing inside queue, then no need to add task there synchronosuly since it can lead to a deadlock
func read<T>(_ work: () throws -> T) rethrows -> T {
if isAlreadyInQueue {
return try work()
} else {
return try queue.sync(execute: work)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment