import UIKit
class ViewController: UIViewController {
var button : UIButton? = nil
let dispatchQueue = DispatchQueue(label: "com.prit.TestGCD.DispatchQueue",
attributes: .concurrent)
let resource = DispatchSemaphore(value: 1)
let readerMutex = DispatchSemaphore(value:1)
var readCount = 0
let writerMutex = DispatchSemaphore(value:1)
var writeCount = 0
let readTry = DispatchSemaphore(value: 1)
var runReadCodeBlock: ((String)->())?
var runWriteCodeBlock: ((String)->())?
override func viewDidLoad() {
runReadCodeBlock = { processName in
print("\(processName) reading stuff..")
Thread.sleep(forTimeInterval: 3)
print("\(processName) done...")
}
runWriteCodeBlock = { processName in
print("\(processName) writing stuff....please wait")
Thread.sleep(forTimeInterval: 5)
print("\(processName) all done!")
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let dispatchGroup = DispatchGroup()
(DispatchQueue.global(qos: .userInitiated)).async(group: dispatchGroup, qos: .userInitiated, flags: .assignCurrentContext) {
() -> Void
in
self.reader(name: "reader X")
}
(DispatchQueue.global(qos: .userInitiated)).async(group: dispatchGroup, qos: .userInitiated, flags: .assignCurrentContext) {
() -> Void
in
self.writer("writerC")
}
}
private func grabResource(semaphore : DispatchSemaphore, processName : String, semaphoreName : String) -> Bool {
if semaphore.wait(timeout: .distantFuture) == .success {
print("\(processName) grabs the \(semaphoreName)")
return true
} else {
print("\(processName) waiting for \(semaphoreName) timed out")
return false
}
}
private func letGoResource(semaphore : DispatchSemaphore, name : String, semaphoreName : String) {
print("\(name) lets go of the \(semaphoreName)")
semaphore.signal()
}
// due to both writers and readers trying to grab the readyTry semaphore, writers will have a fair chance of getting the semaphore, instead of
// bothersome future readers that just keeps coming in. Writers will finally come in, wait and grab for the readTry. Once it succeeds, it will
// naturally grab the resource semaphore, does its writing, and let's go after its done.
// Then it will grab writerMutex in order to decrement writeCount. Once there's no more writers, we let go of readTry.
// say a writer has readTry. It does its write, and more writers come in.
public func writer(_ name : String) {
if grabResource(semaphore: writerMutex, processName: name, semaphoreName: "writerMutex") {
writeCount += 1
if (writeCount == 1) {
// if you're the first writer, must lock the other Readers out via ReadTry.
_ = grabResource(semaphore: readTry, processName: name, semaphoreName: "readTry")
}
letGoResource(semaphore: writerMutex, name: name, semaphoreName: "writerMutex")
}
// reserve the resource for yourself to write
if grabResource(semaphore: resource, processName: name, semaphoreName: "resource") {
runWriteCodeBlock?(name)
letGoResource(semaphore: resource, name: name, semaphoreName: "resource")
}
// The very first writer has a hold on readyTry so that no other "additional" readers can come in.
// when we're done writing, we decrement. IF WE'RE THE LAST WRITER, make sure to let go of readyTry, so other readers can come in.
// however, this starves readers because reader's first execution is to try to grab readTry. Since its already been held by the first
// writer, the reader will fail. However, future writers come in and do not need to grab readTry again. It was already held by the first
// writer and thus, future writers will simply wait for the resource to do its writing, then decrement writeCount.
// ONLY the last writer can let go of readTry. And this is where it starves readers.
if grabResource(semaphore: writerMutex, processName: name, semaphoreName: "writerMutex") {
writeCount -= 1
if writeCount == 0 {
letGoResource(semaphore: readTry, name: name, semaphoreName: "readTry")
}
letGoResource(semaphore: writerMutex, name: name, semaphoreName: "writerMutex")
}
}
public func reader(name : String) {
// reader and writer will fight for readTry
if grabResource(semaphore: readTry, processName: name, semaphoreName: "readTry") {
if grabResource(semaphore: readerMutex, processName: name, semaphoreName: "readerMutex") {
readCount += 1
if readCount == 1 {
// if we're the first reader, we must lock the resource mutex so we can read. This is also for additional Readers to read from the resource. That way, while all readers are reading, no writers can write.
// Due to readTry, if annoying future reads come in, these future reads MUST FIGHT with future write via readTry. Thus, this gives
// fair chance for future writes to grab readTry and do its writing. Once a future write grabs readyTry, No future reads will
// be able to grab readTry, and thus, each existing read will eventually finish reading, decreasing readCount back to 0.
// This releases the resource for the next waiting Writer to use.
_ = grabResource(semaphore: resource, processName: name, semaphoreName: "resource")
}
letGoResource(semaphore: readerMutex, name: name, semaphoreName: "readerMutex")
}
letGoResource(semaphore: readTry, name: name, semaphoreName: "readTry")
}
// So the writer will wait for the reader to release the readtry and then the writer will immediately lock it for itself and all subsequent writers.
// However, the writer will not be able to access the resource until the current reader has released the resource, which only occurs after the reader is finished with the resource in the critical section. This is shown below when the readCount == 0, and the last reader let's go
// of resource.
runReadCodeBlock?(name) // reading is performed
// grab reader mutex, because we're gunna decrease the readCount
if grabResource(semaphore: readerMutex, processName: name, semaphoreName: "readerMutex") {
readCount -= 1
if readCount == 0 {
// we're the last reader, we must release the resource semaphore
letGoResource(semaphore: resource, name: name, semaphoreName: "resource")
}
}
letGoResource(semaphore: readerMutex, name: name, semaphoreName: "readerMutex")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}