Skip to content

Instantly share code, notes, and snippets.

@jrudolph
Created July 11, 2011 14:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrudolph/1076016 to your computer and use it in GitHub Desktop.
Save jrudolph/1076016 to your computer and use it in GitHub Desktop.
LazyVal Locking
trait Holder {
def x: String
def y: Int
def getX = {
println("While initializing x")
Thread.sleep(5000);
println("Finished initializing x")
null
}
def getY = {
println("While initializing y")
Thread.sleep(5000);
println("Finished initializing y")
42
}
}
// for comparison
object LazyHolder extends Holder {
lazy val x = getX
lazy val y = getY
}
/**
* Pseudo-code showing current behaviour
*/
object CurrentLazyHolder extends Holder {
private[this] var _x_value: String = null
private[this] var _y_value: Int = 0
private[this] var bitmap = 0
def x =
if ((bitmap & 1) != 0)
_x_value
else
synchronized {
if ((bitmap & 1) != 0)
_x_value
else {
val res = getX
_x_value = res
bitmap |= 1
res
}
}
def y =
if ((bitmap & 2) != 0)
_y_value
else
synchronized {
if ((bitmap & 2) != 0)
_y_value
else {
val res = getY
_y_value = res
bitmap |= 2
res
}
}
}
object DefHolder extends Holder {
def x = getX
def y = getY
}
// can be used globally, just a marker
class LazyValLock
/*
* simple scheme, requires that _y_value is initialized with LazyValLock on initialization
* here we need no global lock on `this`, because we exploit that _y_value always changes monotonically
* from a value of type LazyValLock to the actual value
*/
object SimpleManualLazyHolder extends Holder {
var _x_value: AnyRef = new LazyValLock
var _y_value: AnyRef = new LazyValLock
@volatile var bitmap = 0
def x = {
if ((bitmap & 1) != 0)
_x_value.asInstanceOf[String]
else {
val lock = _x_value
if (!lock.isInstanceOf[LazyValLock]) // x_value may already be changed into the real value, so we have to guard against it
lock.asInstanceOf[String] // safe, once it is no LazyValLock any more, it will be never again
else
lock synchronized {
if ((bitmap & 1) != 0)
_x_value.asInstanceOf[String] // handle accesses run into lock while calculating
else {
val theVal = getX.asInstanceOf[AnyRef]
this synchronized {
_x_value = theVal
bitmap |= 1
}
_x_value.asInstanceOf[String]
}
}
}
}
def y = {
if ((bitmap & 2) != 0)
_y_value.asInstanceOf[Int]
else {
val lock = _y_value
if (!lock.isInstanceOf[LazyValLock]) // y_value may already be changed into the real value, so we have to guard against it
lock.asInstanceOf[Int ] // safe, once it is no LazyValLock any more, it will be never again
else
lock synchronized {
if ((bitmap & 2) != 0)
_y_value.asInstanceOf[Int] // handle accesses run into lock while calculating
else {
val theVal = getY.asInstanceOf[AnyRef]
this synchronized {
_y_value = theVal
bitmap |= 2
}
_y_value.asInstanceOf[Int]
}
}
}
}
}
object AdvancedManualLazyHolder extends Holder {
var _x_value: AnyRef = null
var _y_value: AnyRef = null
var bitmap = 0
// advanced scheme, no cost until x is really called
def x = {
if ((bitmap & 1) != 0)
_x_value.asInstanceOf[String]
else {
// parent object invariant:
// if bitmap bit is set _x_value is initialized (not the other way round)
val lock = this.synchronized {
if ((bitmap & 1) == 0 && _x_value == null)
_x_value = new LazyValLock
_x_value
}
// invariant at this position: if bitmap bit is not set and _x_value has to be a lock or the real value
if (!lock.isInstanceOf[LazyValLock]) // x_value may already be changed into the real value, so we have to guard against it
lock.asInstanceOf[String] // safe, once it is no LazyValLock any more, it will be never again
else
lock synchronized {
if ((bitmap & 1) != 0)
_x_value.asInstanceOf[String] // handle accesses run into lock while calculating
else {
val theVal = getX.asInstanceOf[AnyRef]
this synchronized {
_x_value = theVal
bitmap |= 1
}
_x_value.asInstanceOf[String]
}
}
}
}
// advanced scheme, no cost until x is really called
def y = {
if ((bitmap & 2) != 0)
_y_value.asInstanceOf[Int]
else {
// parent object invariant:
// if bitmap bit is set _x_value is initialized (not the other way round)
val lock = this.synchronized {
if ((bitmap & 2) == 0 && _y_value == null)
_y_value = new LazyValLock
_y_value
}
// invariant at this position: if bitmap bit is not set and _x_value has to be a lock or the real value
if (!lock.isInstanceOf[LazyValLock]) // x_value may already be changed into the real value, so we have to guard against it
lock.asInstanceOf[Int] // safe, once it is no LazyValLock any more, it will be never again
else
lock synchronized {
if ((bitmap & 2) != 0)
_y_value.asInstanceOf[Int] // handle accesses run into lock while calculating
else {
val theVal = getY.asInstanceOf[AnyRef]
this synchronized {
_y_value = theVal
bitmap |= 2
}
_y_value.asInstanceOf[Int]
}
}
}
}
}
object LazyValler extends App {
val holder = CurrentLazyHolder
def x = holder.x
def y = holder.y
def x2 = {Thread.sleep(1000); holder.x}
List(x _, x2 _, y _).par foreach { x =>
println(x())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment