11use std:: sync:: atomic:: { AtomicBool , AtomicUsize , Ordering } ;
2- use std:: sync:: { Arc , OnceLock , mpsc} ;
2+ use std:: sync:: { Arc , Condvar , Mutex , OnceLock , mpsc} ;
33use std:: thread;
44use std:: time:: { Duration , Instant } ;
55
@@ -142,22 +142,54 @@ struct RuntimeStoreState {
142142 limits : StoreLimits ,
143143}
144144
145+ #[ cfg( test) ]
145146#[ derive( Debug ) ]
146147struct CounterPermit {
147148 counter : & ' static AtomicUsize ,
148149}
149150
151+ struct CompileSlotPool {
152+ active : Mutex < usize > ,
153+ available : Condvar ,
154+ }
155+
156+ struct CompileSlotPermit {
157+ pool : & ' static CompileSlotPool ,
158+ }
159+
160+ #[ cfg( test) ]
150161impl Drop for CounterPermit {
151162 fn drop ( & mut self ) {
152163 self . counter . fetch_sub ( 1 , Ordering :: AcqRel ) ;
153164 }
154165}
155166
167+ impl Drop for CompileSlotPermit {
168+ fn drop ( & mut self ) {
169+ let mut active = self . pool . active . lock ( ) . unwrap_or_else ( |poisoned| {
170+ let _ = poisoned;
171+ std:: process:: abort ( ) ;
172+ } ) ;
173+ * active = active. saturating_sub ( 1 ) ;
174+ self . pool . available . notify_one ( ) ;
175+ }
176+ }
177+
178+ #[ cfg( test) ]
156179fn compile_slots ( ) -> & ' static AtomicUsize {
157180 static SLOTS : OnceLock < AtomicUsize > = OnceLock :: new ( ) ;
158181 SLOTS . get_or_init ( || AtomicUsize :: new ( 0 ) )
159182}
160183
184+ fn compile_slot_pool ( ) -> & ' static CompileSlotPool {
185+ static POOL : OnceLock < CompileSlotPool > = OnceLock :: new ( ) ;
186+ POOL . get_or_init ( || CompileSlotPool {
187+ active : Mutex :: new ( 0 ) ,
188+ available : Condvar :: new ( ) ,
189+ } )
190+ }
191+
192+ #[ cfg( test) ]
161193fn acquire_counter_permit (
162194 counter : & ' static AtomicUsize ,
163195 limit : usize ,
@@ -180,18 +212,35 @@ fn acquire_counter_permit(
180212}
181213
182214fn acquire_counter_permit_with_timeout (
183- counter : & ' static AtomicUsize ,
215+ pool : & ' static CompileSlotPool ,
184216 limit : usize ,
185217 timeout : Duration ,
186- ) -> Result < CounterPermit , WasmExecutionError > {
218+ ) -> Result < CompileSlotPermit , WasmExecutionError > {
187219 let started = Instant :: now ( ) ;
220+ let mut active = pool. active . lock ( ) . unwrap_or_else ( |poisoned| {
221+ let _ = poisoned;
222+ std:: process:: abort ( ) ;
223+ } ) ;
188224 loop {
189- match acquire_counter_permit ( counter, limit) {
190- Ok ( permit) => return Ok ( permit) ,
191- Err ( WasmExecutionError :: CompileConcurrencyLimit ) if started. elapsed ( ) < timeout => {
192- thread:: sleep ( Duration :: from_millis ( 1 ) ) ;
193- }
194- Err ( error) => return Err ( error) ,
225+ if * active < limit {
226+ * active += 1 ;
227+ return Ok ( CompileSlotPermit { pool } ) ;
228+ }
229+ let Some ( remaining) = timeout. checked_sub ( started. elapsed ( ) ) else {
230+ return Err ( WasmExecutionError :: CompileConcurrencyLimit ) ;
231+ } ;
232+ if remaining. is_zero ( ) {
233+ return Err ( WasmExecutionError :: CompileConcurrencyLimit ) ;
234+ }
235+ let ( next_active, wait) = pool
236+ . available
237+ . wait_timeout ( active, remaining)
238+ . unwrap_or_else ( |_| {
239+ std:: process:: abort ( ) ;
240+ } ) ;
241+ active = next_active;
242+ if wait. timed_out ( ) && * active >= limit {
243+ return Err ( WasmExecutionError :: CompileConcurrencyLimit ) ;
195244 }
196245 }
197246}
@@ -331,23 +380,46 @@ impl FluxWasmRuntime {
331380 }
332381
333382 fn compile_module ( & self , plugin : & WasmPluginFile ) -> Result < Module , WasmExecutionError > {
334- self . compile_module_with_counter ( plugin, compile_slots ( ) , MAX_CONCURRENT_COMPILES )
383+ self . compile_module_with_slot_pool ( plugin, compile_slot_pool ( ) , MAX_CONCURRENT_COMPILES )
335384 }
336385
337- fn compile_module_with_counter (
386+ fn compile_module_with_slot_pool (
338387 & self ,
339388 plugin : & WasmPluginFile ,
340- counter : & ' static AtomicUsize ,
389+ pool : & ' static CompileSlotPool ,
341390 limit : usize ,
342391 ) -> Result < Module , WasmExecutionError > {
343392 let started = Instant :: now ( ) ;
344393 let compile_permit =
345- acquire_counter_permit_with_timeout ( counter , limit, self . limits . compile_timeout ) ?;
394+ acquire_counter_permit_with_timeout ( pool , limit, self . limits . compile_timeout ) ?;
346395 let remaining_timeout = self
347396 . limits
348397 . compile_timeout
349398 . checked_sub ( started. elapsed ( ) )
350399 . unwrap_or ( Duration :: ZERO ) ;
400+ self . compile_module_with_permit ( plugin, compile_permit, remaining_timeout)
401+ }
402+
403+ #[ cfg( test) ]
404+ fn compile_module_with_counter (
405+ & self ,
406+ plugin : & WasmPluginFile ,
407+ counter : & ' static AtomicUsize ,
408+ limit : usize ,
409+ ) -> Result < Module , WasmExecutionError > {
410+ let compile_permit = acquire_counter_permit ( counter, limit) ?;
411+ self . compile_module_with_permit ( plugin, compile_permit, self . limits . compile_timeout )
412+ }
413+
414+ fn compile_module_with_permit < P > (
415+ & self ,
416+ plugin : & WasmPluginFile ,
417+ compile_permit : P ,
418+ timeout : Duration ,
419+ ) -> Result < Module , WasmExecutionError >
420+ where
421+ P : Send + ' static ,
422+ {
351423 let engine = self . engine . clone ( ) ;
352424 let bytes = plugin. bytes ( ) . to_vec ( ) ;
353425 let ( result_sender, result_receiver) = mpsc:: sync_channel ( 1 ) ;
@@ -358,7 +430,7 @@ impl FluxWasmRuntime {
358430 let _ = result_sender. send ( result) ;
359431 } ) ;
360432
361- match result_receiver. recv_timeout ( remaining_timeout ) {
433+ match result_receiver. recv_timeout ( timeout ) {
362434 Ok ( result) => result,
363435 Err ( mpsc:: RecvTimeoutError :: Timeout ) => Err ( WasmExecutionError :: CompileTimeout {
364436 timeout_ms : self . limits . compile_timeout . as_millis ( ) ,
0 commit comments