1515 * for the result
1616 * otherwise Variable is initialized
1717 *
18- * Note 1: This assumes that fields cannot have `null` as normal value. Once we have
19- * nullability checking, this should be the standard case. We can still accommodate
20- * fields that can be null by representing `null` with a special value (say `NULL`)
21- * and storing `NULL` instead of `null` in the field. The necessary tweaks are added
22- * as comment lines to the code below.
2318 *
2419 * A lazy val `x: A = rhs` is compiled to the following code scheme:
25- *
26- * private var _x: AnyRef = null
27- * def x: A =
28- * _x match
29- * case current: A =>
30- * current
31- * case null =>
32- * if CAS(_x, null, Evaluating) then
33- * var result = rhs
34- * // if result == null then result == NULL
35- * if !CAS(_x, Evaluating, result) then
36- * val lock = _x.asInstanceOf[Waiting]
37- * _x = result
38- * lock.release(result)
39- * x
40- * case Evaluating =>
41- * CAS(x, Evaluating, new Waiting)
42- * x
43- * case current: Waiting =>
44- * _x = current.awaitRelease()
45- * x
46- * // case NULL =>
47- * // null
48- *
20+
21+ private @volatile var _x: AnyRef = null
22+
23+ @tailrec def x: A =
24+ _x match
25+ case current: A =>
26+ current
27+ case null =>
28+ if CAS(_x, null, Evaluating) then
29+ var result: A = null
30+ try
31+ result = rhs
32+ if result == null then result = NULL // drop if A is non-nullable
33+ finally
34+ if !CAS(_x, Evaluating, result) then
35+ val lock = _x.asInstanceOf[Waiting]
36+ CAS(_x, lock, result)
37+ lock.release()
38+ x
39+ case Evaluating =>
40+ CAS(x, Evaluating, new Waiting)
41+ x
42+ case current: Waiting =>
43+ _x = current.awaitRelease()
44+ x
45+ case NULL => null // drop if A is non-nullable
4946
5047 * The code makes use of the following runtime class:
51- *
52- * class Waiting:
53- *
54- * private var done = false
55- * private var result: AnyRef = _
56- *
57- * def release(result: AnyRef): Unit = synchronized:
58- * this.result = result
59- * done = true
60- * notifyAll()
61- *
62- * def awaitRelease(): AnyRef = synchronized:
63- * while !done do wait()
64- * result
65- *
66- * Note 2: The code assumes that the getter result type `A` is disjoint from the type
48+
49+ class Waiting:
50+ private var done = false
51+ def release(): Unit = synchronized:
52+ done = true
53+ notifyAll()
54+
55+ def awaitRelease(): Unit = synchronized:
56+ while !done do wait()
57+
58+ * Note: The code assumes that the getter result type `A` is disjoint from the type
6759 * of `Evaluating` and the `Waiting` class. If this is not the case (e.g. `A` is AnyRef),
6860 * then the conditions in the match have to be re-ordered so that case `_x: A` becomes
6961 * the final default case.
7567 * whether cache has updated
7668 * - no synchronization operations on reads after the first one
7769 * - If there is contention, we see in addition
78- * - for the initializing thread: a synchronized notifyAll
70+ * - for the initializing thread: another CAS and a synchronized notifyAll
7971 * - for a reading thread: 0 or 1 CAS and a synchronized wait
8072 *
8173 * Code sizes for getter:
8274 *
83- * this scheme, if nulls are excluded in type: 72 bytes
84- * current Dotty scheme: 131 bytes
85- * Scala 2 scheme: 39 bytes + 1 exception handler
75+ * this scheme, if nulls are excluded in type: 86 bytes
76+ * current Dotty scheme: 125 bytes
77+ * Scala 2 scheme: 39 bytes
8678 *
8779 * Advantages of the scheme:
8880 *
9587 *
9688 * Disadvantages:
9789 *
98- * - does not work for local lazy vals (but maybe these could be unsynchronized anyway?)
9990 * - lazy vals of primitive types are boxed
10091 */
10192import sun .misc .Unsafe ._
@@ -106,31 +97,37 @@ class C {
10697 println(s " initialize $name" ); " result"
10798 }
10899
109- private [this ] var _x : AnyRef = null
100+ @ volatile private [this ] var _x : AnyRef = _
110101
111- // Expansion of: lazy val x: String = init
102+ // Expansion of: lazy val x: String = init("x")
112103
113104 def x : String = {
114105 val current = _x
115106 if (current.isInstanceOf [String ])
116107 current.asInstanceOf [String ]
117108 else
118- x$lzy_compute
109+ x$lzy
119110 }
120111
121- def x$lzy_compute : String = {
112+ def x$lzy : String = {
122113 val current = _x
123114 if (current.isInstanceOf [String ])
124115 current.asInstanceOf [String ]
125116 else {
126117 val offset = C .x_offset
127118 if (current == null ) {
128- if (LazyRuntime .isUnitialized(this , offset))
129- LazyRuntime .initialize(this , offset, init(" x" ))
119+ if (LazyRuntime .isUnitialized(this , offset)) {
120+ try LazyRuntime .initialize(this , offset, init(" x" ))
121+ catch {
122+ case ex : Throwable =>
123+ LazyRuntime .initialize(this , offset, null )
124+ throw ex
125+ }
126+ }
130127 }
131128 else
132129 LazyRuntime .awaitInitialized(this , offset, current)
133- x$lzy_compute
130+ x$lzy
134131 }
135132 }
136133
@@ -164,13 +161,13 @@ object LazyRuntime {
164161 def initialize (base : Object , offset : Long , result : Object ): Unit =
165162 if (! unsafe.compareAndSwapObject(base, offset, Evaluating , result)) {
166163 val lock = unsafe.getObject(base, offset).asInstanceOf [Waiting ]
167- unsafe.putObject (base, offset, result)
168- lock.release(result )
164+ unsafe.compareAndSwapObject (base, offset, lock , result)
165+ lock.release()
169166 }
170167
171168 def awaitInitialized (base : Object , offset : Long , current : Object ): Unit =
172169 if (current.isInstanceOf [Waiting ])
173- unsafe.putObject(base, offset, current.asInstanceOf [Waiting ].awaitRelease() )
170+ current.asInstanceOf [Waiting ].awaitRelease()
174171 else
175172 unsafe.compareAndSwapObject(base, offset, Evaluating , new Waiting )
176173}
@@ -180,17 +177,14 @@ class LazyControl
180177class Waiting extends LazyControl {
181178
182179 private var done = false
183- private var result : AnyRef = _
184180
185- def release (result : AnyRef ) = synchronized {
186- this .result = result
181+ def release (): Unit = synchronized {
187182 done = true
188183 notifyAll()
189184 }
190185
191- def awaitRelease (): AnyRef = synchronized {
186+ def awaitRelease (): Unit = synchronized {
192187 while (! done) wait()
193- result
194188 }
195189}
196190
0 commit comments