0

I'm debugging a swift class that I have inherited in a project. There was an attempt to create a thread safe dictionary using a swift dictionary with a semaphore. The code fails in multi threaded environment - the swift dictionary is treated as a struct and is copied on mutation, regardless of the semaphore.

I'm trying to understand: Will replacing a swift struct based dictionary with a core foundation NSMutableDictionary (with a semaphore) ensure thread safety of the code?

I see this answer for Objective-C, but my question is about modifying the swift code.

public class ThreadSafeDictionary<KeyType: Hashable, ValueType> {

    private var dictionary: [KeyType: ValueType] = [:]
    //change to:
    private let dictionary = NSMutableDictionary() //will this make the mutating code thread safe?

    public func add(key: KeyType, value: ValueType) {
    // semaphore wait
    // set value in dictionary
    // semaphore signal
    }
}

Or - is there a way to use swift keywords like mutating or inout to make sure that the semaphores would prevent multiple threads from each working with a copy of a swift dictionary?

Updated: there was a bug in a higher level code, creating 2 independent instances of ThreadSafeDictionary. Fixing that resolved the concurrency issue.

Alex Stone
  • 46,408
  • 55
  • 231
  • 407
  • What does the copy have to do with your problem? The structure is wrapped inside your class and as long as "dictionary" property is being assigned to it all should work. Maybe you left out some very important information on how you use this. – Matic Oblak Jun 17 '19 at 12:42

1 Answers1

2

If you wrap a Swift directory (e.g. struct,) into a Swift class (a reference type), you will somehow lose the copy-on-write-mechanism of the struct (which indeed is intended to prevent multi threading issues). Therefore you will not gain anything if you change the inner directory member from a Swift type to Foundation's NSMutableDirectory. Also, mutating or inout do not imply any thread safety.

To make ThreadSafeDictionary thread safe, you need to wrap all the access methods of the inner directory, as you depicted in the func add...() function. Instead of using semaphores, you could also use GCD with your own serial queue.

Andreas Oetjen
  • 9,889
  • 1
  • 24
  • 34
  • Thank you - I have identified a different bug which was causing 2 instances of ThreadSafeDictionary to be created, making it appear as if the internals variable of the class was duplicated by the calling code. – Alex Stone Jun 17 '19 at 14:17