2121
2222use std:: sync:: Arc ;
2323use crate :: error:: { Error , WasmError } ;
24- use parking_lot:: { Mutex , RwLock } ;
24+ use parking_lot:: Mutex ;
2525use codec:: Decode ;
2626use sp_core:: traits:: { Externalities , RuntimeCode , FetchRuntimeCode } ;
2727use sp_version:: RuntimeVersion ;
@@ -53,11 +53,77 @@ struct VersionedRuntime {
5353 /// Runtime version according to `Core_version` if any.
5454 version : Option < RuntimeVersion > ,
5555 /// Cached instance pool.
56- instances : RwLock < [ Option < Arc < Mutex < Box < dyn WasmInstance > > > > ; MAX_INSTANCES ] > ,
56+ instances : Vec < Mutex < Option < Box < dyn WasmInstance > > > > ,
57+ }
58+
59+ impl VersionedRuntime {
60+ /// Run the given closure `f` with an instance of this runtime.
61+ fn with_instance < ' c , R , F > (
62+ & self ,
63+ ext : & mut dyn Externalities ,
64+ f : F ,
65+ ) -> Result < R , Error >
66+ where F : FnOnce (
67+ & dyn WasmInstance ,
68+ Option < & RuntimeVersion > ,
69+ & mut dyn Externalities )
70+ -> Result < R , Error > ,
71+ {
72+ // Find a free instance
73+ let instance = self . instances
74+ . iter ( )
75+ . enumerate ( )
76+ . find_map ( |( index, i) | i. try_lock ( ) . map ( |i| ( index, i) ) ) ;
77+
78+ match instance {
79+ Some ( ( index, mut locked) ) => {
80+ let ( instance, new_inst) = locked. take ( )
81+ . map ( |r| Ok ( ( r, false ) ) )
82+ . unwrap_or_else ( || self . module . new_instance ( ) . map ( |i| ( i, true ) ) ) ?;
83+
84+ let result = f ( & * instance, self . version . as_ref ( ) , ext) ;
85+ if let Err ( e) = & result {
86+ if new_inst {
87+ log:: warn!(
88+ target: "wasm-runtime" ,
89+ "Fresh runtime instance failed with {:?}" ,
90+ e,
91+ )
92+ } else {
93+ log:: warn!(
94+ target: "wasm-runtime" ,
95+ "Evicting failed runtime instance: {:?}" ,
96+ e,
97+ ) ;
98+ }
99+ } else {
100+ * locked = Some ( instance) ;
101+
102+ if new_inst {
103+ log:: debug!(
104+ target: "wasm-runtime" ,
105+ "Allocated WASM instance {}/{}" ,
106+ index + 1 ,
107+ self . instances. len( ) ,
108+ ) ;
109+ }
110+ }
111+
112+ result
113+ } ,
114+ None => {
115+ log:: warn!( target: "wasm-runtime" , "Ran out of free WASM instances" ) ;
116+
117+ // Allocate a new instance
118+ let instance = self . module . new_instance ( ) ?;
119+
120+ f ( & * instance, self . version . as_ref ( ) , ext)
121+ }
122+ }
123+ }
57124}
58125
59126const MAX_RUNTIMES : usize = 2 ;
60- const MAX_INSTANCES : usize = 8 ;
61127
62128/// Cache for the runtimes.
63129///
@@ -69,20 +135,22 @@ const MAX_INSTANCES: usize = 8;
69135/// the memory reset to the initial memory. So, one runtime instance is reused for every fetch
70136/// request.
71137///
72- /// For now the cache grows indefinitely, but that should be fine for now since runtimes can only be
73- /// upgraded rarely and there are no other ways to make the node to execute some other runtime.
138+ /// The size of cache is equal to `MAX_RUNTIMES`.
74139pub struct RuntimeCache {
75140 /// A cache of runtimes along with metadata.
76141 ///
77142 /// Runtimes sorted by recent usage. The most recently used is at the front.
78143 runtimes : Mutex < [ Option < Arc < VersionedRuntime > > ; MAX_RUNTIMES ] > ,
144+ /// The size of the instances cache for each runtime.
145+ max_runtime_instances : usize ,
79146}
80147
81148impl RuntimeCache {
82149 /// Creates a new instance of a runtimes cache.
83- pub fn new ( ) -> RuntimeCache {
150+ pub fn new ( max_runtime_instances : usize ) -> RuntimeCache {
84151 RuntimeCache {
85152 runtimes : Default :: default ( ) ,
153+ max_runtime_instances,
86154 }
87155 }
88156
@@ -103,6 +171,8 @@ impl RuntimeCache {
103171 ///
104172 /// `allow_missing_func_imports` - Ignore missing function imports.
105173 ///
174+ /// `max_runtime_instances` - The size of the instances cache.
175+ ///
106176 /// `f` - Function to execute.
107177 ///
108178 /// # Returns result of `f` wrapped in an additonal result.
@@ -154,6 +224,7 @@ impl RuntimeCache {
154224 heap_pages,
155225 host_functions. into ( ) ,
156226 allow_missing_func_imports,
227+ self . max_runtime_instances ,
157228 ) ;
158229 if let Err ( ref err) = result {
159230 log:: warn!( target: "wasm-runtime" , "Cannot create a runtime: {:?}" , err) ;
@@ -179,53 +250,7 @@ impl RuntimeCache {
179250 }
180251 drop ( runtimes) ;
181252
182- let result = {
183- // Find a free instance
184- let instance_pool = runtime. instances . read ( ) . clone ( ) ;
185- let instance = instance_pool
186- . iter ( )
187- . find_map ( |i| i. as_ref ( ) . and_then ( |i| i. try_lock ( ) ) ) ;
188- if let Some ( mut locked) = instance {
189- let result = f ( & * * locked, runtime. version . as_ref ( ) , ext) ;
190- if let Err ( e) = & result {
191- log:: warn!( target: "wasm-runtime" , "Evicting failed runtime instance: {:?}" , e) ;
192- * locked = runtime. module . new_instance ( ) ?;
193- }
194- result
195- } else {
196- // Allocate a new instance
197- let instance = runtime. module . new_instance ( ) ?;
198-
199- let result = f ( & * instance, runtime. version . as_ref ( ) , ext) ;
200- match & result {
201- Ok ( _) => {
202- let mut instance_pool = runtime. instances . write ( ) ;
203- if let Some ( ref mut slot) = instance_pool. iter_mut ( ) . find ( |s| s. is_none ( ) ) {
204- * * slot = Some ( Arc :: new ( Mutex :: new ( instance) ) ) ;
205- log:: debug!(
206- target: "wasm-runtime" ,
207- "Allocated WASM instance {}/{}" ,
208- instance_pool. len( ) ,
209- MAX_INSTANCES ,
210- ) ;
211- } else {
212- log:: warn!( target: "wasm-runtime" , "Ran out of free WASM instances" ) ;
213- }
214- }
215- Err ( e) => {
216- log:: warn!(
217- target:
218- "wasm-runtime" ,
219- "Fresh runtime instance failed with {:?}" ,
220- e,
221- ) ;
222- }
223- }
224- result
225- }
226- } ;
227-
228- Ok ( result)
253+ Ok ( runtime. with_instance ( ext, f) )
229254 }
230255}
231256
@@ -264,6 +289,7 @@ fn create_versioned_wasm_runtime(
264289 heap_pages : u64 ,
265290 host_functions : Vec < & ' static dyn Function > ,
266291 allow_missing_func_imports : bool ,
292+ max_instances : usize ,
267293) -> Result < VersionedRuntime , WasmError > {
268294 #[ cfg( not( target_os = "unknown" ) ) ]
269295 let time = std:: time:: Instant :: now ( ) ;
@@ -303,13 +329,16 @@ fn create_versioned_wasm_runtime(
303329 time. elapsed( ) . as_millis( ) ,
304330 ) ;
305331
332+ let mut instances = Vec :: with_capacity ( max_instances) ;
333+ instances. resize_with ( max_instances, || Mutex :: new ( None ) ) ;
334+
306335 Ok ( VersionedRuntime {
307336 code_hash,
308337 module : runtime,
309338 version,
310339 heap_pages,
311340 wasm_method,
312- instances : Default :: default ( ) ,
341+ instances,
313342 } )
314343}
315344
0 commit comments