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 ;
@@ -59,11 +59,77 @@ struct VersionedRuntime {
5959 /// Runtime version according to `Core_version` if any.
6060 version : Option < RuntimeVersion > ,
6161 /// Cached instance pool.
62- instances : RwLock < [ Option < Arc < Mutex < Box < dyn WasmInstance > > > > ; MAX_INSTANCES ] > ,
62+ instances : Vec < Mutex < Option < Box < dyn WasmInstance > > > > ,
63+ }
64+
65+ impl VersionedRuntime {
66+ /// Run the given closure `f` with an instance of this runtime.
67+ fn with_instance < ' c , R , F > (
68+ & self ,
69+ ext : & mut dyn Externalities ,
70+ f : F ,
71+ ) -> Result < R , Error >
72+ where F : FnOnce (
73+ & dyn WasmInstance ,
74+ Option < & RuntimeVersion > ,
75+ & mut dyn Externalities )
76+ -> Result < R , Error > ,
77+ {
78+ // Find a free instance
79+ let instance = self . instances
80+ . iter ( )
81+ . enumerate ( )
82+ . find_map ( |( index, i) | i. try_lock ( ) . map ( |i| ( index, i) ) ) ;
83+
84+ match instance {
85+ Some ( ( index, mut locked) ) => {
86+ let ( instance, new_inst) = locked. take ( )
87+ . map ( |r| Ok ( ( r, false ) ) )
88+ . unwrap_or_else ( || self . module . new_instance ( ) . map ( |i| ( i, true ) ) ) ?;
89+
90+ let result = f ( & * instance, self . version . as_ref ( ) , ext) ;
91+ if let Err ( e) = & result {
92+ if new_inst {
93+ log:: warn!(
94+ target: "wasm-runtime" ,
95+ "Fresh runtime instance failed with {:?}" ,
96+ e,
97+ )
98+ } else {
99+ log:: warn!(
100+ target: "wasm-runtime" ,
101+ "Evicting failed runtime instance: {:?}" ,
102+ e,
103+ ) ;
104+ }
105+ } else {
106+ * locked = Some ( instance) ;
107+
108+ if new_inst {
109+ log:: debug!(
110+ target: "wasm-runtime" ,
111+ "Allocated WASM instance {}/{}" ,
112+ index + 1 ,
113+ self . instances. len( ) ,
114+ ) ;
115+ }
116+ }
117+
118+ result
119+ } ,
120+ None => {
121+ log:: warn!( target: "wasm-runtime" , "Ran out of free WASM instances" ) ;
122+
123+ // Allocate a new instance
124+ let instance = self . module . new_instance ( ) ?;
125+
126+ f ( & * instance, self . version . as_ref ( ) , ext)
127+ }
128+ }
129+ }
63130}
64131
65132const MAX_RUNTIMES : usize = 2 ;
66- const MAX_INSTANCES : usize = 8 ;
67133
68134/// Cache for the runtimes.
69135///
@@ -75,20 +141,22 @@ const MAX_INSTANCES: usize = 8;
75141/// the memory reset to the initial memory. So, one runtime instance is reused for every fetch
76142/// request.
77143///
78- /// For now the cache grows indefinitely, but that should be fine for now since runtimes can only be
79- /// upgraded rarely and there are no other ways to make the node to execute some other runtime.
144+ /// The size of cache is equal to `MAX_RUNTIMES`.
80145pub struct RuntimeCache {
81146 /// A cache of runtimes along with metadata.
82147 ///
83148 /// Runtimes sorted by recent usage. The most recently used is at the front.
84149 runtimes : Mutex < [ Option < Arc < VersionedRuntime > > ; MAX_RUNTIMES ] > ,
150+ /// The size of the instances cache for each runtime.
151+ max_runtime_instances : usize ,
85152}
86153
87154impl RuntimeCache {
88155 /// Creates a new instance of a runtimes cache.
89- pub fn new ( ) -> RuntimeCache {
156+ pub fn new ( max_runtime_instances : usize ) -> RuntimeCache {
90157 RuntimeCache {
91158 runtimes : Default :: default ( ) ,
159+ max_runtime_instances,
92160 }
93161 }
94162
@@ -109,6 +177,8 @@ impl RuntimeCache {
109177 ///
110178 /// `allow_missing_func_imports` - Ignore missing function imports.
111179 ///
180+ /// `max_runtime_instances` - The size of the instances cache.
181+ ///
112182 /// `f` - Function to execute.
113183 ///
114184 /// # Returns result of `f` wrapped in an additonal result.
@@ -160,6 +230,7 @@ impl RuntimeCache {
160230 heap_pages,
161231 host_functions. into ( ) ,
162232 allow_missing_func_imports,
233+ self . max_runtime_instances ,
163234 ) ;
164235 if let Err ( ref err) = result {
165236 log:: warn!( target: "wasm-runtime" , "Cannot create a runtime: {:?}" , err) ;
@@ -185,53 +256,7 @@ impl RuntimeCache {
185256 }
186257 drop ( runtimes) ;
187258
188- let result = {
189- // Find a free instance
190- let instance_pool = runtime. instances . read ( ) . clone ( ) ;
191- let instance = instance_pool
192- . iter ( )
193- . find_map ( |i| i. as_ref ( ) . and_then ( |i| i. try_lock ( ) ) ) ;
194- if let Some ( mut locked) = instance {
195- let result = f ( & * * locked, runtime. version . as_ref ( ) , ext) ;
196- if let Err ( e) = & result {
197- log:: warn!( target: "wasm-runtime" , "Evicting failed runtime instance: {:?}" , e) ;
198- * locked = runtime. module . new_instance ( ) ?;
199- }
200- result
201- } else {
202- // Allocate a new instance
203- let instance = runtime. module . new_instance ( ) ?;
204-
205- let result = f ( & * instance, runtime. version . as_ref ( ) , ext) ;
206- match & result {
207- Ok ( _) => {
208- let mut instance_pool = runtime. instances . write ( ) ;
209- if let Some ( ref mut slot) = instance_pool. iter_mut ( ) . find ( |s| s. is_none ( ) ) {
210- * * slot = Some ( Arc :: new ( Mutex :: new ( instance) ) ) ;
211- log:: debug!(
212- target: "wasm-runtime" ,
213- "Allocated WASM instance {}/{}" ,
214- instance_pool. len( ) ,
215- MAX_INSTANCES ,
216- ) ;
217- } else {
218- log:: warn!( target: "wasm-runtime" , "Ran out of free WASM instances" ) ;
219- }
220- }
221- Err ( e) => {
222- log:: warn!(
223- target:
224- "wasm-runtime" ,
225- "Fresh runtime instance failed with {:?}" ,
226- e,
227- ) ;
228- }
229- }
230- result
231- }
232- } ;
233-
234- Ok ( result)
259+ Ok ( runtime. with_instance ( ext, f) )
235260 }
236261}
237262
@@ -270,6 +295,7 @@ fn create_versioned_wasm_runtime(
270295 heap_pages : u64 ,
271296 host_functions : Vec < & ' static dyn Function > ,
272297 allow_missing_func_imports : bool ,
298+ max_instances : usize ,
273299) -> Result < VersionedRuntime , WasmError > {
274300 #[ cfg( not( target_os = "unknown" ) ) ]
275301 let time = std:: time:: Instant :: now ( ) ;
@@ -309,13 +335,16 @@ fn create_versioned_wasm_runtime(
309335 time. elapsed( ) . as_millis( ) ,
310336 ) ;
311337
338+ let mut instances = Vec :: with_capacity ( max_instances) ;
339+ instances. resize_with ( max_instances, || Mutex :: new ( None ) ) ;
340+
312341 Ok ( VersionedRuntime {
313342 code_hash,
314343 module : runtime,
315344 version,
316345 heap_pages,
317346 wasm_method,
318- instances : Default :: default ( ) ,
347+ instances,
319348 } )
320349}
321350
0 commit comments