diff options
author | Dmitry Petrashko <dmitry.petrashko@gmail.com> | 2014-03-14 12:56:11 +0100 |
---|---|---|
committer | Dmitry Petrashko <dmitry.petrashko@gmail.com> | 2014-03-19 14:13:47 +0100 |
commit | 5cbd2fbc8409b446f8751792b006693e1d091055 (patch) | |
tree | 6440bff96597df0e10e69a66ec673091f92ea284 /src/dotty/runtime/LazyVals.scala | |
parent | 46856320f4e21d94b3c5c29a921efac40e12421f (diff) | |
download | dotty-5cbd2fbc8409b446f8751792b006693e1d091055.tar.gz dotty-5cbd2fbc8409b446f8751792b006693e1d091055.tar.bz2 dotty-5cbd2fbc8409b446f8751792b006693e1d091055.zip |
LazyVals phase.
Creates accessors for lazy vals:
1) lazy local vals are rewritten to dotty.runtime.Lazy*** holders
2) for a non-volatile field lazy val create a non-thread-safe accessor and flag:
2.a) if lazy val type indicates that val is not nullable, uses null value as a flag
2.b) else uses boolean flag for sake of performance, method size, and
allowing more jvm optimizations
3) for a volatile field lazy val use double locking scheme, that guaranties no
spurious deadlocks, using long bits as bitmaps and creating companion
objects to store offsets needed for unsafe methods.
Conflicts:
test/dotc/tests.scala
Diffstat (limited to 'src/dotty/runtime/LazyVals.scala')
-rw-r--r-- | src/dotty/runtime/LazyVals.scala | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/dotty/runtime/LazyVals.scala b/src/dotty/runtime/LazyVals.scala new file mode 100644 index 000000000..4130d4d60 --- /dev/null +++ b/src/dotty/runtime/LazyVals.scala @@ -0,0 +1,79 @@ +package dotty.runtime + +import scala.annotation.tailrec + +/** + * Helper methods used in thread-safe lazy vals. + */ +object LazyVals { + private val unsafe = scala.concurrent.util.Unsafe.instance + + final val BITS_PER_LAZY_VAL = 2 + final val LAZY_VAL_MASK = 3 + + @inline def STATE(cur: Long, ord: Long) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK + @inline def CAS(t: Object, offset: Long, e: Long, v: Long, ord: Int) = { + val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL) + val n = (e & mask) | (v << (ord * BITS_PER_LAZY_VAL)) + compareAndSet(t, offset, e, n) + } + @inline def setFlag(t: Object, offset: Long, v: Int, ord: Int) = { + var retry = true + while (retry) { + val cur = get(t, offset) + if (STATE(cur, ord) == 1) retry = CAS(t, offset, cur, v, ord) + else { + // cur == 2, somebody is waiting on monitor + if (CAS(t, offset, cur, v, ord)) { + val monitor = getMonitor(t, ord) + monitor.synchronized { + monitor.notifyAll() + } + retry = false + } + } + } + } + @inline def wait4Notification(t: Object, offset: Long, cur: Long, ord: Int) = { + var retry = true + while (retry) { + val cur = get(t, offset) + val state = STATE(cur, ord) + if (state == 1) CAS(t, offset, cur, 2, ord) + else if (state == 2) { + val monitor = getMonitor(t, ord) + monitor.synchronized { + monitor.wait() + } + } + else retry = false + } + } + + @inline def compareAndSet(t: Object, off: Long, e: Long, v: Long) = unsafe.compareAndSwapLong(t, off, e, v) + @inline def get(t: Object, off: Long) = unsafe.getLongVolatile(t, off) + + val processors: Int = java.lang.Runtime.getRuntime.availableProcessors() + val base: Int = 8 * processors * processors + val monitors: Array[Object] = (0 to base).map { + x => new Object() + }.toArray + + @inline def getMonitor(obj: Object, fieldId: Int = 0) = { + var id = (java.lang.System.identityHashCode(obj) + fieldId) % base + if (id < 0) id += base + monitors(id) + } + + @inline def getOffset(obj: Object, name: String) = unsafe.objectFieldOffset(obj.getClass.getDeclaredField(name)) + + object Names { + final val state = "STATE" + final val cas = "CAS" + final val setFlag = "setFlag" + final val wait4Notification = "wait4Notification" + final val compareAndSet = "compareAndSet" + final val get = "get" + final val getOffset = "getOffset" + } +} |