Race conditions with lazy variables


(Dan Zimmerman) #1

Hey,

Today I ran into an issue where I had a lazy instance variable that was being initialized multiple times due to a race condition. For example:

import Darwin
import Dispatch

func doSomethingIntense() {
	usleep(100000)
}

var counter: Int = 0

class Cat {
	lazy var sound: String = {
		doSomethingIntense()
      let isFive = counter == 5
		counter += 1
		print("Getting \(Unmanaged.passUnretained(self).toOpaque()).sound")
		return isFive ? "doctorate denied" : "meow"
	}()
}

let cat = Cat()
for _ in 1..<10 {
  if #available(OSX 10.10, *) {
      usleep(100);
      DispatchQueue.global(qos: .background).async {
          print("The cat says \(cat.sound)")
      }
  }
}

dispatchMain()

I was wondering what the "swifty" way to fix this issue would be (assuming I only want the lazy initializer to be called once) - since dispatch_once was taken out I don't see a clean way to do this without interacting with pthread_mutex's directly.

I was also wondering if there's been any discussion surrounding this - i.e. I can't imagine an instance where I have a lazy variable initialized via a closure that I'm ok with it being initialized twice.