I was playing around with a Singleton
to generate unique ID's to conform to Hashable
. Then I realised it wasn't really thread safe. So I switched to OSAtomicIncrement32
because it does not use a closure, so it can be as simple as this:
class HashID {
static let sharedID = HashID()
private var storedGlobalID : Int32 = 0
var newID : Int {
get {
return Int(OSAtomicIncrement32(&storedGlobalID))
}
}
private init() {}
}
Then I wanted to try a HashID for each Type. So I use _stdlib_getDemangledTypeName
reflecting
to feed a Dictionary. But then again it wasn't thread safe. Different threads can now manipulate the Dictionary at the same time.
class HashID {
static let sharedID = HashTypeID()
func idForType(type: Any.Type) -> Int {
let typeName = _stdlib_getDemangledTypeName(type)
guard let currentID = storedGlobalIDForType[typeName] else {
let currentID : Int32 = 0
storedGlobalIDForType[typeName] = currentID
return Int(currentID)
}
let newID = atomicIncrement(currentID)
storedGlobalIDForType[typeName] = newID
return Int(newID)
}
private var storedGlobalIDForType : [String:Int32] = [String:Int32]()
private func atomicIncrement(var value: Int32) -> Int32 {
return OSAtomicIncrement32(&value)
}
private init() {}
}
I can obviously use GCD, but that means using closures and then I can't use a simple function with a return value. Using a completionHandler is just less "clean" and it also makes it more complicated to set this in the init of a class / struct.
This means default ID's and this would make them unhashable until the ID is fetched, or I would have to have an init with a completionHandler of it's own. -> forgot how dispatch_sync works.
As I said, I was just playing around with this code. The first function is perfectly fine. The second however also gives a count of all instances of a type that were created. Which is not the most useful thing ever...
Is there some way I am forgetting to make the access to the Dictionary thread safe?
Updated Code:
credits to Martin R and Nikolai Ruhe
class HashTypeID {
// shared instance
static let sharedID = HashTypeID()
// dict for each type
private var storedGlobalIDForType : [String:Int] = [String:Int]()
// serial queue
private let _serialQueue = dispatch_queue_create("HashQueue", DISPATCH_QUEUE_SERIAL)
func idForType(type: Any.Type) -> Int {
var id : Int = 0
// make it thread safe
dispatch_sync(_serialQueue) {
let typeName = String(reflecting: type)
// check if there is already an ID
guard let currentID = self.storedGlobalIDForType[typeName] else {
// if there isn't an ID, store one
let currentID : Int = 0
self.storedGlobalIDForType[typeName] = currentID
id = Int(currentID)
return
}
// if there is an ID, increment
id = currentID
id++
// store the incremented ID
self.storedGlobalIDForType[typeName] = id
}
return id
}
private init() {}
}