@@ -14,7 +14,6 @@ use self::utils::SmallByteSlice;
1414use super :: ValidatingFuncTranslator ;
1515use super :: { FuncToValidate , FuncTranslationDriver , FuncTranslator , TranslationError } ;
1616use crate :: {
17- Config ,
1817 Error ,
1918 TrapCode ,
2019 core:: { Fuel , FuelCostsProvider , hint} ,
@@ -58,26 +57,21 @@ const LEN_BUCKET0: u64 = 1 << LEN_BUCKET0_LOG2;
5857const MAX_BUCKETS : usize = Funcs :: required_buckets_for_len ( MAX_FUNCS ) ;
5958
6059/// A data structure to store and manage [`FuncEntry`] definitions.
61- #[ derive( Debug ) ]
60+ ///
61+ /// # Note
62+ ///
63+ /// The [`WasmFeatures`] required to compile a [`FuncEntry`] are not stored here but in the
64+ /// [`StoreInner`](crate::store::StoreInner) driving the compilation, where the executor
65+ /// reaches them without a dependent load.
66+ #[ derive( Debug , Default ) ]
6267pub struct CodeMap {
6368 /// The append-only, lock-free-readable storage for all [`FuncEntry`] definitions.
6469 funcs : Funcs ,
6570 /// Serializes concurrent writers ([`Self::alloc_funcs`]); readers never take this lock.
6671 alloc_lock : Mutex < ( ) > ,
67- /// Shared Wasm features across all [`FuncEntry`] definitions within the [`CodeMap`].
68- features : WasmFeatures ,
6972}
7073
7174impl CodeMap {
72- /// Creates a new [`CodeMap`].
73- pub fn new ( config : & Config ) -> Self {
74- Self {
75- funcs : Funcs :: default ( ) ,
76- alloc_lock : Mutex :: new ( ( ) ) ,
77- features : config. wasm_features ( ) ,
78- }
79- }
80-
8175 /// Allocates `amount` new uninitialized [`EngineFunc`] to the [`CodeMap`].
8276 ///
8377 /// # Note
@@ -95,6 +89,14 @@ impl CodeMap {
9589 }
9690 }
9791
92+ /// Returns a shared reference to the [`FuncEntry`] of `func` if published.
93+ ///
94+ /// Returns `None` if `func` is not (yet) published to this [`CodeMap`].
95+ #[ inline]
96+ pub fn entry ( & self , func : EngineFunc ) -> Option < & FuncEntry > {
97+ self . funcs . get ( func)
98+ }
99+
98100 /// Initializes the [`EngineFunc`] with its [`CompiledFuncEntry`].
99101 ///
100102 /// # Panics
@@ -134,110 +136,6 @@ impl CodeMap {
134136 func_to_validate,
135137 ) ) ;
136138 }
137-
138- /// Returns a cheap, lock-free [`CodeView`] snapshot of this [`CodeMap`].
139- ///
140- /// # Note
141- ///
142- /// The snapshot borrows `self` and caches the currently published function count; it is used by
143- /// the executor to resolve calls without locking or per-call atomics.
144- #[ inline]
145- pub fn view ( & self ) -> CodeView < ' _ > {
146- // Note: a single `Acquire` load establishes the happens-before for every bucket published before
147- // this length; subsequent per-call reads through the snapshot are plain (non-atomic).
148- let len_funcs = self . funcs . len_funcs . load ( Ordering :: Acquire ) ;
149- CodeView {
150- code_map : self ,
151- len_funcs,
152- }
153- }
154- }
155-
156- /// A cheap, lock-free, stale-but-valid snapshot of a [`CodeMap`].
157- ///
158- /// Borrows the source [`CodeMap`] and caches the number of published functions, so the executor
159- /// can resolve functions without locking and without any atomic load on the per-call path.
160- ///
161- /// # Note
162- ///
163- /// - Represents a potentially stale view: functions appended after the snapshot are not visible
164- /// until [`CodeView::refresh`] is called. Since [`Funcs`] is append-only and pointer-stable, a
165- /// stale view is never invalid, only outdated.
166- /// - Holding `&CodeMap` is sound under both Stacked and Tree Borrows because no `&mut CodeMap`
167- /// (or `&mut Funcs`) is ever formed while a view is alive: all publication goes through interior
168- /// mutability under the writer lock.
169- /// - Upon execution an [`Engine`] derives a [`CodeView`] of the current [`CodeMap`] state and uses
170- /// it to drive call-based executions without taking the writer lock. After a host function call
171- /// the view must be [`refresh`](CodeView::refresh)ed, since the host may have appended functions
172- /// (e.g. by compiling and instantiating new modules) that the resuming Wasm can reach.
173- ///
174- /// [`Engine`]: crate::Engine
175- #[ derive( Copy , Clone ) ]
176- pub struct CodeView < ' a > {
177- /// The source [`CodeMap`]. The bucket-array base address and `features` are stable for its
178- /// lifetime; only the published function count grows (tracked by `len_funcs`).
179- code_map : & ' a CodeMap ,
180- /// Cached number of published [`FuncEntry`] definitions; bounds visibility.
181- ///
182- /// # Note
183- ///
184- /// This is only updated by [`CodeView::refresh`], so the per-call read path performs no atomic load.
185- len_funcs : usize ,
186- }
187-
188- impl fmt:: Debug for CodeView < ' _ > {
189- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
190- f. debug_struct ( "CodeView" )
191- . field ( "len_funcs" , & self . len_funcs )
192- . finish_non_exhaustive ( )
193- }
194- }
195-
196- impl < ' a > CodeView < ' a > {
197- /// Re-materializes the snapshot to reflect functions published since it was last (re)created.
198- ///
199- /// Must be called after a host function call: the host may have compiled and/or instantiated
200- /// new Wasm modules (appending to the [`CodeMap`]) that the resuming Wasm code can then reach.
201- #[ inline]
202- pub fn refresh ( & mut self ) {
203- self . len_funcs = self . code_map . funcs . len_funcs . load ( Ordering :: Acquire ) ;
204- }
205-
206- /// Returns a shared reference to the [`FuncEntry`] of `func` if visible in this snapshot.
207- ///
208- /// Returns `None` if `func` is not (yet) visible to this snapshot.
209- #[ inline]
210- pub fn entry ( & self , func : EngineFunc ) -> Option < & ' a FuncEntry > {
211- // Safety: `len_funcs` is loaded via acquire upon creation of `self` so that `buckets` are published.
212- unsafe { self . code_map . funcs . get_within ( func, self . len_funcs ) }
213- }
214-
215- /// Returns the [`CompiledFuncRef`] of `func`, compiling it lazily if still uncompiled.
216- ///
217- /// Returns `None` if the `func` index is out of bounds for `self`.
218- ///
219- /// # Errors
220- ///
221- /// - If translation or Wasm validation of `func` failed.
222- /// - If `fuel` ran out in case fuel consumption is enabled.
223- #[ track_caller]
224- #[ inline]
225- pub fn get_or_compile (
226- & self ,
227- fuel : Option < & mut Fuel > ,
228- func : EngineFunc ,
229- ) -> Result < Option < CompiledFuncRef < ' a > > , Error > {
230- let Some ( entry) = self . entry ( func) else {
231- return Ok ( None ) ;
232- } ;
233- let compiled = entry. get_or_compile ( fuel, & self . code_map . features ) ?;
234- Ok ( Some ( compiled) )
235- }
236-
237- /// Returns the [`WasmFeatures`] of the underlying [`CodeMap`].
238- pub fn features ( & self ) -> & WasmFeatures {
239- & self . code_map . features
240- }
241139}
242140
243141/// An append-only collection for [`FuncEntry`] definitions.
@@ -250,8 +148,8 @@ pub struct Funcs {
250148 /// - The first `required_buckets_for_len(len_funcs)` slots are `Some` and, once published,
251149 /// are never written or moved again.
252150 /// - The `buckets` array lives behind an [`UnsafeCell`] so that new buckets can be
253- /// published through a shared `&Funcs` (no `&mut Funcs` is ever formed), which is what lets a
254- /// [`CodeView`] snapshot read `buckets` lock-free and without atomics on the per-call path .
151+ /// published through a shared `&Funcs` (no `&mut Funcs` is ever formed), which is what lets
152+ /// readers resolve a [`FuncEntry`] lock-free.
255153 buckets : UnsafeCell < [ Option < RawFuncsBucket > ; MAX_BUCKETS ] > ,
256154 /// The number of [`FuncEntry`] definitions published across all `buckets`.
257155 ///
0 commit comments