qed
qed

Reputation: 23134

What is the purpose of an empty loop in this volatile var example?

Code:

  class Page(val txt: String, var position: Int)

  val rand = new Random()
  object Volatile {
    val pages = for(i <- 1 to 5) yield new Page("Na" * rand.nextInt(1000) + " Batman!", -1)
    @volatile var found = false
    for (p <- pages) yield thread {
      var i = 0
      while (i < p.txt.length && !found) if (p.txt(i) == '!') {
        p.position = i
        found = true
      } else i += 1
      while(!found) {}
      log(s"results: ${pages.map(_.position)}")
    }
  }

What is the purpose of the empty loop while(!found) {}?

Upvotes: 1

Views: 103

Answers (3)

qed
qed

Reputation: 23134

Following @Silly Freak's suggestions, I have modified the code to use synchronized instead of volatile:

  object AntiVolatile {
    val pages = for (i <- 1 to 15) yield new Page("!Na" * rand.nextInt(1000) + " Batman!", -1)
    var found = Some(false)

    def run(): Unit = {
      for (p <- pages) yield thread {
        var i = 0
        var foundInThread = found.get
        while (i < p.txt.length && !foundInThread)
          if (p.txt(i) == '!') {
            found.synchronized {
              found match {
                case Some(true) => foundInThread = true
                case Some(false) => {
                  p.position = i
                  found = Some(true)
                  Thread.sleep(1)
                }
                case _ =>
              }
            }
          } else i += 1
        // if still not found, wait for another thread to find it.
        def wait(): Unit = {
          found match {
            case Some(false) => wait()
            case _ =>
          }
        }
        wait()
        log(s"results: ${pages.map(_.position)}")
      }
    }
  }

No more data race:

Thread-29: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-27: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-28: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-26: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-30: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-31: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-32: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-25: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-33: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-34: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-39: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-38: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-37: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-36: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
Thread-35: results: Vector(0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)

Upvotes: 0

Silly Freak
Silly Freak

Reputation: 4231

The first loop: while (i < p.txt.length && !found) has two conditions for stopping: when the search space is exhausted, or when a match was found. Note that the found variable is used in multiple threads: thread {...} presumably spawns a new thread.

So there are several possibilities how the threads' loops can play out:

  • the thread finds a '!' and sets found. The first loop breaks, the second doesn't execute.
  • another thread sets found. The first loop breaks, the second doesn't execute.
  • all characters were searched. The first loop breaks, the second executes until another thread finds a '!'.

The third one is probably the one you forgot. The consequence is that results are only logged once all loops are finished.


Granted, I can't tell you why that code looks like it does: each thread, separately prints the results for all pages, i.e. if there are n pages, n^2 results are logged. Also, all threads access a shared object without synchronization. If the goal was to find exactly one exclamation mark, the code fails. Anyway, these seem like separate issues.

Upvotes: 2

Carcigenicate
Carcigenicate

Reputation: 45806

If looks like a busy wait. It will continue to check the status of found, and execution won't continue until found is true.

Upvotes: 1

Related Questions