concurrency 

To quote Goetz’s Java Concurrency in Practice,

Writing thread-safe code is, at its core, about managing access to state, and in particular to shared, mutable state.

Conveniently we have refactored the inner working of tetrix so each operation is written as transition function from one GameState to another. Here’s a simplified version of AbstractUI:

package com.eed3si9n.tetrix

class AbstractUI {
  import Stage._
  import java.{util => ju}
  
  private[this] var state = newState(...)
  private[this] val timer = new ju.Timer
  timer.scheduleAtFixedRate(new ju.TimerTask {
    def run { state = tick(state) }
  }, 0, 1000)
  def left() {
    state = moveLeft(state)
  }
  def right() {
    state = moveRight(state)
  }
  def view: GameView = state.view
}

The timer modifies state by calling tick(state), and the player can also modify it by calling moveLeft(state) or moveRight(state). This is a textbook example of a thread-unsafe code. Here’s an unlucky run of the timer thread and swing’s event dispatch thread:

timer thread: reads shared state. current piece at (5, 18)
event thread: reads shared state. current piece at (5, 18)
timer thread: calls tick() function
timer thread: tick() returns a new state whose current piece is at (5, 17)
event thread: calls moveLeft() function
event thread: moveLeft() returns a new state whose current piece is at (4, 18)
event thread: writes the new state into shared state. current piece at (4, 18)
timer thread: writes the new state into shared state. current piece at (5, 17)

When the player sees this, either it would look like the left move was completely ignored, or witness the piece jumping diagnally from (4, 18) to (5, 17). This is a race condition.

synchronized 

In this case, because each tasks are short-lived, and because the mutability is simple, we probably could get away with synchronizing on state.

package com.eed3si9n.tetrix

class AbstractUI {
  import Stage._
  import java.{util => ju}
  
  private[this] var state = newState(...)
  private[this] val timer = new ju.Timer
  timer.scheduleAtFixedRate(new ju.TimerTask {
    def run { updateState {tick} }
  }, 0, 1000)
  def left()  = updateState {moveLeft}
  def right() = updateState {moveRight}
  def view: GameView = state.view
  private[this] def updateState(trans: GameState => GameState) {
    synchronized {
      state = trans(state)
    }
  }
}

Using the synchronized clause, reading of state and writing of state is now guaranteed to happen atomically. This approach may not be practical if mutability is spread out more widely, or if background execution of tasks are required.