views:

177

answers:

2

I am trying to learn Scala and find it a great language so far. I learn from "Beginning Scala" by David Pollak. In chapter 3 there is this piece of code, which illustrates how to write multi-threaded code without synchronized blocks (this code is copied from the book, it's available for download from Apress site, I don't mean to break any laws here):

import java.util.concurrent.atomic.{AtomicReference => AtomR, AtomicLong}
import java.util.Random
import scala.collection.immutable.TreeHashMap

object Multics {
  type MT = Map[String, Int]

  val info: AtomR[MT] = new AtomR(TreeHashMap.empty)
  val clashCnt = new AtomicLong

  def main(argv: Array[String]) {
     runThread {
      repeatEvery(1000) {
        println("Clash Count: "+clashCnt+" Total: "+
                info.get.foldLeft(0)(_ + _._2))
      } }

    for (i  old + (name -> 0)}
      repeatEvery(ran.nextInt(100)) {
        doSet(info){old => old + (name -> (old(name) + 1))}
        cnt = cnt + 1
        if (cnt != info.get()(name))
          throw new Exception("Thread: "+name+" failed")
      } }
  }

  def runThread(f: => Unit) =
  (new Thread(new Runnable {def run(): Unit = f})).start

  def doSet[T](atom: AtomR[T])(update: T => T) {
    val old = atom.get
    if (atom.compareAndSet(old, update(old))) ()
    else {
      clashCnt.incrementAndGet
      doSet(atom)(update)
    }
  }

  def repeatEvery(len: => Int)(body: => Unit): Unit = {
    try {
      while(true) {

        Thread.sleep(len)
        body
      }
    } catch {
      case e => e.printStackTrace; System.exit(1)
    }
  }
} 

From what I understand, there is potential starvation problem in function doSet (unlucky thread might always clash and cause StackOverflowException). Am I right and if so, how can this code be improved to fix this issue?

A: 

It looks like its using Compare and Swap (http://en.wikipedia.org/wiki/Compare-and-swap) instead of locking.

The general idea with this approach is that you loop until you manage to set the value consistently.

I don't know enough about this to answer if there is a starvation problem. My guess is that in theory there is a starvation problem, but in practice it will be fine.

Alex Black
+2  A: 

First of all, I think a big chuck of the code you pasted from the book example is missing; it makes it quite difficult to understand your question.

Secondly, it is true that doSet() can potentially be called many times recursively but a StackOverflowException will not occur in this case because of one saving grace: tail recursion. doSet() is called recursively at the end of of the function (no more processing is done after the recursive call) and can not be overridden (defined inside an object) which qualify it to be optimized into a loop without any stack overhead.

In 2.8.0, there is an annotation called @scala.annotation.tailrec which, when used on a def, will ensure that the recursive call inside the def is indeed tail recursive.

Walter Chang
The whole code is pasted, you just need to scroll it.
Ula Krukar
If I understand your answer correctly, starvation is possible here (although highly unlikely) and it will result in an infinite loop that won't be stopped even by StackOverflowException?
Ula Krukar
What I was saying is that doSet() might be called many times recursively but since no consumption of stack frames are involved, there is no danger of stack overflow. As for thread starvation, it really depends on your definition of starvation. With today's hardware, I can't really see any thread out of the 2000 started getting starved for more than a couple of seconds.
Walter Chang