1

As I understand, TrieMap.getOrElseUpdate is still not truly atomic, and this fixes only returned result (it could return different instances for different callers before this fix), so the updater function still might be called several times, as documentation (for 2.11.7) says:

Note: This method will invoke op at most once. However, op may be invoked without the result being added to the map if a concurrent process is also trying to add a value corresponding to the same key k.

*I've checked that manually for 2.11.7, still "at least once"

How to guarantee one-time call (if I use TrieMap for factories)?

dk14
  • 22,206
  • 4
  • 51
  • 88

1 Answers1

2

I think this solution should work for my requirements:

trait LazyComp { val get: Int }

val map = new TrieMap[String, LazyComp]()

val count = new AtomicInteger() //just for test, you don't need it

def getSingleton(key: String) = {
  val v = new LazyComp {
    lazy val get = {
      //compute something
      count.incrementAndGet() //just for test, you don't need it
    }
  }
  map.putIfAbsent(key, v).getOrElse(v).get
}

I believe, lazy val actually uses synchronized inside. And also the code inside get should be safe from exceptions

However, performance could be improved in future: SIP-20

Test:

scala> (0 to 10000000).par.map(_ => getSingleton("zzz")).last
res8: Int = 1

P.S. Java has computeIfAbscent method on ConcurrentHashMap which I could use as well.

dk14
  • 22,206
  • 4
  • 51
  • 88