15
15
* for the result
16
16
* otherwise Variable is initialized
17
17
*
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.
23
18
*
24
19
* 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
49
46
50
47
* 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
67
59
* of `Evaluating` and the `Waiting` class. If this is not the case (e.g. `A` is AnyRef),
68
60
* then the conditions in the match have to be re-ordered so that case `_x: A` becomes
69
61
* the final default case.
75
67
* whether cache has updated
76
68
* - no synchronization operations on reads after the first one
77
69
* - 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
79
71
* - for a reading thread: 0 or 1 CAS and a synchronized wait
80
72
*
81
73
* Code sizes for getter:
82
74
*
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
86
78
*
87
79
* Advantages of the scheme:
88
80
*
95
87
*
96
88
* Disadvantages:
97
89
*
98
- * - does not work for local lazy vals (but maybe these could be unsynchronized anyway?)
99
90
* - lazy vals of primitive types are boxed
100
91
*/
101
92
import sun .misc .Unsafe ._
@@ -106,31 +97,37 @@ class C {
106
97
println(s " initialize $name" ); " result"
107
98
}
108
99
109
- private [this ] var _x : AnyRef = null
100
+ @ volatile private [this ] var _x : AnyRef = _
110
101
111
- // Expansion of: lazy val x: String = init
102
+ // Expansion of: lazy val x: String = init("x")
112
103
113
104
def x : String = {
114
105
val current = _x
115
106
if (current.isInstanceOf [String ])
116
107
current.asInstanceOf [String ]
117
108
else
118
- x$lzy_compute
109
+ x$lzy
119
110
}
120
111
121
- def x$lzy_compute : String = {
112
+ def x$lzy : String = {
122
113
val current = _x
123
114
if (current.isInstanceOf [String ])
124
115
current.asInstanceOf [String ]
125
116
else {
126
117
val offset = C .x_offset
127
118
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
+ }
130
127
}
131
128
else
132
129
LazyRuntime .awaitInitialized(this , offset, current)
133
- x$lzy_compute
130
+ x$lzy
134
131
}
135
132
}
136
133
@@ -164,13 +161,13 @@ object LazyRuntime {
164
161
def initialize (base : Object , offset : Long , result : Object ): Unit =
165
162
if (! unsafe.compareAndSwapObject(base, offset, Evaluating , result)) {
166
163
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()
169
166
}
170
167
171
168
def awaitInitialized (base : Object , offset : Long , current : Object ): Unit =
172
169
if (current.isInstanceOf [Waiting ])
173
- unsafe.putObject(base, offset, current.asInstanceOf [Waiting ].awaitRelease() )
170
+ current.asInstanceOf [Waiting ].awaitRelease()
174
171
else
175
172
unsafe.compareAndSwapObject(base, offset, Evaluating , new Waiting )
176
173
}
@@ -180,17 +177,14 @@ class LazyControl
180
177
class Waiting extends LazyControl {
181
178
182
179
private var done = false
183
- private var result : AnyRef = _
184
180
185
- def release (result : AnyRef ) = synchronized {
186
- this .result = result
181
+ def release (): Unit = synchronized {
187
182
done = true
188
183
notifyAll()
189
184
}
190
185
191
- def awaitRelease (): AnyRef = synchronized {
186
+ def awaitRelease (): Unit = synchronized {
192
187
while (! done) wait()
193
- result
194
188
}
195
189
}
196
190
0 commit comments