Created
July 11, 2011 14:51
-
-
Save jrudolph/1076016 to your computer and use it in GitHub Desktop.
LazyVal Locking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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