4040 */
4141package org .graalvm .wasm ;
4242
43+ import java .lang .invoke .MethodHandles ;
44+ import java .lang .invoke .VarHandle ;
45+
4346import org .graalvm .wasm .constants .BytecodeBitEncoding ;
4447import org .graalvm .wasm .memory .WasmMemory ;
4548
4649import com .oracle .truffle .api .CallTarget ;
50+ import com .oracle .truffle .api .CompilerAsserts ;
4751import com .oracle .truffle .api .CompilerDirectives ;
4852import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
4953import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
@@ -108,10 +112,20 @@ public abstract class RuntimeState {
108112 */
109113 private final int droppedDataInstanceOffset ;
110114
111- @ CompilationFinal private volatile Linker .LinkState linkState ;
115+ @ CompilationFinal private Linker .LinkState linkState ;
112116
113117 @ CompilationFinal private int startFunctionIndex ;
114118
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+
115129 private void ensureGlobalsCapacity (int index ) {
116130 while (index >= globalAddresses .length ) {
117131 final int [] nGlobalAddresses = new int [globalAddresses .length * 2 ];
@@ -153,30 +167,22 @@ public RuntimeState(WasmStore store, WasmModule module, int numberOfFunctions, i
153167
154168 private void checkNotLinked () {
155169 // 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 ) {
157172 throw CompilerDirectives .shouldNotReachHere ("The engine tried to modify the instance after linking." );
158173 }
159174 }
160175
161176 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." );
166178 }
167179
168180 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." );
173182 }
174183
175184 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." );
180186 }
181187
182188 public WasmStore store () {
@@ -188,23 +194,46 @@ public WasmContext context() {
188194 }
189195
190196 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+ }
192211 }
193212
194213 public boolean isNonLinked () {
195- return linkState == Linker .LinkState .nonLinked ;
214+ return linkState () == Linker .LinkState .nonLinked ;
196215 }
197216
198217 public boolean isLinkInProgress () {
199- return linkState == Linker .LinkState .inProgress ;
218+ return linkState () == Linker .LinkState .inProgress ;
200219 }
201220
202221 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 () {
203232 return linkState == Linker .LinkState .linked ;
204233 }
205234
206235 public boolean isLinkFailed () {
207- return linkState == Linker .LinkState .failed ;
236+ return linkState () == Linker .LinkState .failed ;
208237 }
209238
210239 public SymbolTable symbolTable () {
0 commit comments