Skip to content

Commit 5cbd2fb

Browse files
committed
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
1 parent 4685632 commit 5cbd2fb

File tree

9 files changed

+864
-7
lines changed

9 files changed

+864
-7
lines changed

src/dotty/runtime/LazyHolders.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package dotty.runtime
2+
3+
/**
4+
* Classes used as holders for local lazy vals
5+
*/
6+
class LazyInt(init: => Int) {
7+
lazy val value = init
8+
}
9+
10+
class LazyLong(init: => Long) {
11+
lazy val value = init
12+
}
13+
14+
class LazyBoolean(init: => Boolean) {
15+
lazy val value = init
16+
}
17+
18+
class LazyDouble(init: => Double) {
19+
lazy val value = init
20+
}
21+
22+
class LazyFloat(init: => Float) {
23+
lazy val value = init
24+
}
25+
26+
class LazyByte(init: => Byte) {
27+
lazy val value = init
28+
}
29+
30+
class LazyRef(init: => AnyRef) {
31+
lazy val value = init
32+
}
33+
34+
class LazyShort(init: => Short) {
35+
lazy val value = init
36+
}
37+
38+
class LazyChar(init: => Char) {
39+
lazy val value = init
40+
}

src/dotty/runtime/LazyVals.scala

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package dotty.runtime
2+
3+
import scala.annotation.tailrec
4+
5+
/**
6+
* Helper methods used in thread-safe lazy vals.
7+
*/
8+
object LazyVals {
9+
private val unsafe = scala.concurrent.util.Unsafe.instance
10+
11+
final val BITS_PER_LAZY_VAL = 2
12+
final val LAZY_VAL_MASK = 3
13+
14+
@inline def STATE(cur: Long, ord: Long) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK
15+
@inline def CAS(t: Object, offset: Long, e: Long, v: Long, ord: Int) = {
16+
val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL)
17+
val n = (e & mask) | (v << (ord * BITS_PER_LAZY_VAL))
18+
compareAndSet(t, offset, e, n)
19+
}
20+
@inline def setFlag(t: Object, offset: Long, v: Int, ord: Int) = {
21+
var retry = true
22+
while (retry) {
23+
val cur = get(t, offset)
24+
if (STATE(cur, ord) == 1) retry = CAS(t, offset, cur, v, ord)
25+
else {
26+
// cur == 2, somebody is waiting on monitor
27+
if (CAS(t, offset, cur, v, ord)) {
28+
val monitor = getMonitor(t, ord)
29+
monitor.synchronized {
30+
monitor.notifyAll()
31+
}
32+
retry = false
33+
}
34+
}
35+
}
36+
}
37+
@inline def wait4Notification(t: Object, offset: Long, cur: Long, ord: Int) = {
38+
var retry = true
39+
while (retry) {
40+
val cur = get(t, offset)
41+
val state = STATE(cur, ord)
42+
if (state == 1) CAS(t, offset, cur, 2, ord)
43+
else if (state == 2) {
44+
val monitor = getMonitor(t, ord)
45+
monitor.synchronized {
46+
monitor.wait()
47+
}
48+
}
49+
else retry = false
50+
}
51+
}
52+
53+
@inline def compareAndSet(t: Object, off: Long, e: Long, v: Long) = unsafe.compareAndSwapLong(t, off, e, v)
54+
@inline def get(t: Object, off: Long) = unsafe.getLongVolatile(t, off)
55+
56+
val processors: Int = java.lang.Runtime.getRuntime.availableProcessors()
57+
val base: Int = 8 * processors * processors
58+
val monitors: Array[Object] = (0 to base).map {
59+
x => new Object()
60+
}.toArray
61+
62+
@inline def getMonitor(obj: Object, fieldId: Int = 0) = {
63+
var id = (java.lang.System.identityHashCode(obj) + fieldId) % base
64+
if (id < 0) id += base
65+
monitors(id)
66+
}
67+
68+
@inline def getOffset(obj: Object, name: String) = unsafe.objectFieldOffset(obj.getClass.getDeclaredField(name))
69+
70+
object Names {
71+
final val state = "STATE"
72+
final val cas = "CAS"
73+
final val setFlag = "setFlag"
74+
final val wait4Notification = "wait4Notification"
75+
final val compareAndSet = "compareAndSet"
76+
final val get = "get"
77+
final val getOffset = "getOffset"
78+
}
79+
}

src/dotty/tools/dotc/Compiler.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,19 @@ import Symbols._
88
import typer.{FrontEnd, Typer, Mode, ImportInfo}
99
import reporting.ConsoleReporter
1010
import dotty.tools.dotc.core.Phases.Phase
11+
import dotty.tools.dotc.transform.{LazyValsCreateCompanionObjects, LazyValTranformContext}
12+
import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer}
13+
import dotty.tools.dotc.transform.PostTyperTransformers.PostTyperTransformer
1114

1215
class Compiler {
1316

14-
def phases: List[Phase] = List(
15-
new FrontEnd,
16-
new transform.SamplePhase)
17+
18+
def phases: List[Phase] = List(new FrontEnd, new transform.SamplePhase)
1719

1820
var runId = 1
19-
def nextRunId = { runId += 1; runId }
21+
def nextRunId = {
22+
runId += 1; runId
23+
}
2024

2125
def rootContext(implicit ctx: Context): Context = {
2226
ctx.definitions.init(ctx)

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ class Definitions {
233233
lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault")
234234
lazy val ThrowsAnnot = ctx.requiredClass("scala.throws")
235235
lazy val UncheckedAnnot = ctx.requiredClass("scala.unchecked")
236+
lazy val VolatileAnnot = ctx.requiredClass("scala.volatile")
236237

237238
// convenient one-parameter method types
238239
def methOfAny(tp: Type) = MethodType(List(AnyType), tp)
@@ -266,6 +267,7 @@ class Definitions {
266267
def JavaRepeatedParamType = JavaRepeatedParamClass.typeRef
267268
def ThrowableType = ThrowableClass.typeRef
268269
def OptionType = OptionClass.typeRef
270+
def VolatileAnnotType = VolatileAnnot.typeRef
269271

270272
def ClassType(arg: Type)(implicit ctx: Context) = {
271273
val ctype = ClassClass.typeRef

src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ object StdNames {
221221
val FAKE_LOCAL_THIS: N = "this$"
222222
val IMPLCLASS_CONSTRUCTOR: N = "$init$"
223223
val LAZY_LOCAL: N = "$lzy"
224+
val LAZY_FIELD_OFFSET: N = "OFFSET$"
224225
val LAZY_SLOW_SUFFIX: N = "$lzycompute"
225226
val LOCAL_SUFFIX: N = " "
226227
val UNIVERSE_BUILD_PREFIX: N = "$u.build."

src/dotty/tools/dotc/transform/CreateCompanionObjects.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ abstract class CreateCompanionObjects(group: TreeTransformer, idx: Int) extends
2525

2626
/** Given class definition should return true if companion object creation should be enforced
2727
*/
28-
def predicate(cls: TypeDef): Boolean
28+
def predicate(cls: TypeDef)(implicit ctx:Context): Boolean
2929

3030
override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = {
3131
@tailrec

0 commit comments

Comments
 (0)