40
40
*/
41
41
package org .graalvm .wasm ;
42
42
43
+ import java .lang .invoke .MethodHandles ;
44
+ import java .lang .invoke .VarHandle ;
45
+
43
46
import org .graalvm .wasm .constants .BytecodeBitEncoding ;
44
47
import org .graalvm .wasm .memory .WasmMemory ;
45
48
46
49
import com .oracle .truffle .api .CallTarget ;
50
+ import com .oracle .truffle .api .CompilerAsserts ;
47
51
import com .oracle .truffle .api .CompilerDirectives ;
48
52
import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
49
53
import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
@@ -108,10 +112,20 @@ public abstract class RuntimeState {
108
112
*/
109
113
private final int droppedDataInstanceOffset ;
110
114
111
- @ CompilationFinal private volatile Linker .LinkState linkState ;
115
+ @ CompilationFinal private Linker .LinkState linkState ;
112
116
113
117
@ CompilationFinal private int startFunctionIndex ;
114
118
119
+ static final VarHandle LINK_STATE ;
120
+
121
+ static {
122
+ try {
123
+ LINK_STATE = MethodHandles .lookup ().findVarHandle (RuntimeState .class , "linkState" , Linker .LinkState .class );
124
+ } catch (NoSuchFieldException | IllegalAccessException e ) {
125
+ throw new ExceptionInInitializerError (e );
126
+ }
127
+ }
128
+
115
129
private void ensureGlobalsCapacity (int index ) {
116
130
while (index >= globalAddresses .length ) {
117
131
final int [] nGlobalAddresses = new int [globalAddresses .length * 2 ];
@@ -153,30 +167,22 @@ public RuntimeState(WasmStore store, WasmModule module, int numberOfFunctions, i
153
167
154
168
private void checkNotLinked () {
155
169
// The symbol table must be read-only after the module gets linked.
156
- if (linkState == Linker .LinkState .linked ) {
170
+ Linker .LinkState state = linkState ();
171
+ if (state == Linker .LinkState .linked || state == Linker .LinkState .failed ) {
157
172
throw CompilerDirectives .shouldNotReachHere ("The engine tried to modify the instance after linking." );
158
173
}
159
174
}
160
175
161
176
public void setLinkInProgress () {
162
- if (linkState != Linker .LinkState .nonLinked ) {
163
- throw CompilerDirectives .shouldNotReachHere ("Can only switch to in-progress state when not linked." );
164
- }
165
- this .linkState = Linker .LinkState .inProgress ;
177
+ setLinkState (Linker .LinkState .nonLinked , Linker .LinkState .inProgress , "Can only switch to in-progress state when not linked." );
166
178
}
167
179
168
180
public void setLinkCompleted () {
169
- if (linkState != Linker .LinkState .inProgress ) {
170
- throw CompilerDirectives .shouldNotReachHere ("Can only switch to linked state when linking is in-progress." );
171
- }
172
- this .linkState = Linker .LinkState .linked ;
181
+ setLinkState (Linker .LinkState .inProgress , Linker .LinkState .linked , "Can only switch to linked state when linking is in-progress." );
173
182
}
174
183
175
184
public void setLinkFailed () {
176
- if (linkState != Linker .LinkState .inProgress ) {
177
- throw CompilerDirectives .shouldNotReachHere ("Can only switch to failed state when linking is in-progress." );
178
- }
179
- this .linkState = Linker .LinkState .failed ;
185
+ setLinkState (Linker .LinkState .inProgress , Linker .LinkState .failed , "Can only switch to failed state when linking is in-progress." );
180
186
}
181
187
182
188
public WasmStore store () {
@@ -188,23 +194,46 @@ public WasmContext context() {
188
194
}
189
195
190
196
public Linker .LinkState linkState () {
191
- return linkState ;
197
+ CompilerAsserts .neverPartOfCompilation ();
198
+ return (Linker .LinkState ) LINK_STATE .getVolatile (this );
199
+ }
200
+
201
+ private void setLinkState (Linker .LinkState expectedState , Linker .LinkState newState , String message ) {
202
+ assert expectedState != Linker .LinkState .linked && expectedState != Linker .LinkState .failed : expectedState ;
203
+ assert Thread .holdsLock (store ());
204
+ if (!LINK_STATE .compareAndSet (this , expectedState , newState )) {
205
+ /*
206
+ * setLinkState is always invoked while the linker is holding a store lock, so we should
207
+ * always see the expected state here and the CAS should never fail.
208
+ */
209
+ throw CompilerDirectives .shouldNotReachHere (message );
210
+ }
192
211
}
193
212
194
213
public boolean isNonLinked () {
195
- return linkState == Linker .LinkState .nonLinked ;
214
+ return linkState () == Linker .LinkState .nonLinked ;
196
215
}
197
216
198
217
public boolean isLinkInProgress () {
199
- return linkState == Linker .LinkState .inProgress ;
218
+ return linkState () == Linker .LinkState .inProgress ;
200
219
}
201
220
202
221
public boolean isLinkCompleted () {
222
+ return linkState () == Linker .LinkState .linked ;
223
+ }
224
+
225
+ /**
226
+ * Non-volatile link state check for use in compiled code. May read a stale value, which is OK,
227
+ * since in that case we'll just (deoptimize and) enter the slow path, and check again. Once
228
+ * this method has returned true, i.e., we've reached the state {@link Linker.LinkState#linked},
229
+ * we can safely rely on the module to be linked, and stay linked, since it is a final state.
230
+ */
231
+ public boolean isLinkCompletedFastPath () {
203
232
return linkState == Linker .LinkState .linked ;
204
233
}
205
234
206
235
public boolean isLinkFailed () {
207
- return linkState == Linker .LinkState .failed ;
236
+ return linkState () == Linker .LinkState .failed ;
208
237
}
209
238
210
239
public SymbolTable symbolTable () {
0 commit comments