1

I would like to update the value vold of a TrieMap t for some key k iff the new value vnew some predicate p(vold, vnew) is true. Since values can be modified through multiple threads, a synchronized block is needed. The code could t.synchronized but that would block updates to other keys' values. Is it possible to block only for the key that's actually being updated?

Now that I've written out the question, does it make sense to t.keySet.filter(_ == k).head.synchronized (since there's no guarantee that k is the same instance of the key in t)? Is there a more canonical way to do this?

2 Answers 2

1

Since values can be modified through multiple threads, a synchronized block is needed.

The canonical way of working with TrieMap is to use the replace/putIfAbsent/remove methods. Get the old value, calculate the new value, and if replace fails, repeat.

Looking through the API, I don't see anything suitable to do what you want directly, and t.keySet.filter(_ == k) has to iterate through the entire Map; in most cases I'd expect it to be worse than just locking it (unless you have a lot more threads than entries?).

5
  • After digging a bit more into my specific needs, replace will fit them. I'm thinking a replaceIf shouldn't be too difficult and would fit others' potential needs. Would you know how to submit RFE's to Scala?
    – Noel Yap
    Commented Sep 10, 2016 at 16:40
  • I think it would be difficult: replace uses compare-and-swap operation (en.wikipedia.org/wiki/Compare-and-swap) which has special hardware support, and I don't think there is any equivalent which replaceIf could use. Commented Sep 10, 2016 at 16:57
  • Ah, yes, that makes sense. OTOH, the alternative is for the coder to do it themselves and, as we've seen, synchronizing on the key isn't very feasible for the coder but should be trivial from within the TrieMap function.
    – Noel Yap
    Commented Sep 10, 2016 at 17:18
  • Hmm, looking through TrieMap.scala, it's not as trivial as I had first thought.
    – Noel Yap
    Commented Sep 10, 2016 at 18:03
  • What do you think of the replaceIf implementation below?
    – Noel Yap
    Commented Sep 12, 2016 at 13:56
0

If p satisfied the following conditions:

  • antisymmetry (ie p(v0, v1) implies !p(v1, v0) and !p(v0, v1) implies p(v1, v0))
  • transitivity (ie p(v0, v1) and p(v1, v2) implies p(v0, v1)

The following can be implemented (WARNING: the code below is untested):

def replaceIf(key: K, newvalue: V)(predicate: (V, V) => Boolean): Option[V] = {
  val oldvalue = replace(key, newvalue)

  if (oldvalue == None || predicate(newvalue, oldvalue.get)) {
    oldvalue
  } else {
    replaceIf(key, oldvalue.get)(predicate)
  }
}
8
  • This is completely subject to data races. Say you have oldValue and Thread 1 wants to replace it with newValue1 and Thread 2 wants to replace it with newValue2. Both predicate(oldValue, newValue1) and predicate(oldValue, newValue2) are false, so both should fail and leave oldValue in place. Thread 1 replaces oldValue (line 3), while it's calculating predicate(oldValue, newValue1) on line 7 thread 2 executes line 3 and replaces newValue1 with newValue2. It checks predicate(newValue1, newValue2) and reports success. And you've got the wrong result. Commented Sep 12, 2016 at 14:35
  • Wouldn't one or the other thread 'win' during the first replace call such that one of them will see the other's newvalue rather than the existing oldvalue?
    – Noel Yap
    Commented Sep 12, 2016 at 15:36
  • Yes, that's precisely what causes the problem in the example I gave. Commented Sep 12, 2016 at 17:34
  • I've simplified the implementation. I don't see the problem. One thread can replace the oldvalue then another thread can replace newvalue1. In the current implementation, if the function returns newvalue, the caller knows it didn't replace oldvalue.
    – Noel Yap
    Commented Sep 12, 2016 at 19:08
  • For this implementation: say predicate is always false (it satisfies both of your conditions). You get an infinite loop if the map contains key. Commented Sep 12, 2016 at 19:13

Not the answer you're looking for? Browse other questions tagged or ask your own question.