@@ -5,6 +5,7 @@ use crate::rt_worker::worker::DuplexStreamEntry;
5
5
use crate :: utils:: units:: { bytes_to_display, mib_to_bytes} ;
6
6
7
7
use anyhow:: { anyhow, bail, Context , Error } ;
8
+ use base_mem_check:: { MemCheckState , WorkerHeapStatistics } ;
8
9
use cooked_waker:: { IntoWaker , WakeRef } ;
9
10
use cpu_timer:: get_thread_time;
10
11
use ctor:: ctor;
@@ -33,8 +34,7 @@ use std::collections::HashMap;
33
34
use std:: ffi:: c_void;
34
35
use std:: fmt;
35
36
use std:: marker:: PhantomData ;
36
- use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
37
- use std:: sync:: Arc ;
37
+ use std:: sync:: { Arc , RwLock } ;
38
38
use std:: task:: Poll ;
39
39
use std:: time:: Duration ;
40
40
use tokio:: sync:: { mpsc, Notify } ;
@@ -118,25 +118,22 @@ fn get_error_class_name(e: &AnyError) -> &'static str {
118
118
sb_core:: errors_rt:: get_error_class_name ( e) . unwrap_or ( "Error" )
119
119
}
120
120
121
- #[ derive( Default , Clone ) ]
122
- struct MemCheckState {
121
+ #[ derive( Default ) ]
122
+ struct MemCheck {
123
123
drop_token : CancellationToken ,
124
124
limit : Option < usize > ,
125
125
waker : Arc < AtomicWaker > ,
126
126
notify : Arc < Notify > ,
127
- current_bytes : Arc < AtomicUsize > ,
128
-
129
- #[ cfg( debug_assertions) ]
130
- exceeded : Arc < AtomicFlag > ,
127
+ state : Arc < RwLock < MemCheckState > > ,
131
128
}
132
129
133
- impl Drop for MemCheckState {
130
+ impl Drop for MemCheck {
134
131
fn drop ( & mut self ) {
135
132
self . drop_token . cancel ( ) ;
136
133
}
137
134
}
138
135
139
- impl MemCheckState {
136
+ impl MemCheck {
140
137
fn check ( & self , isolate : & mut Isolate ) -> usize {
141
138
let Some ( limit) = self . limit else {
142
139
return 0 ;
@@ -158,25 +155,22 @@ impl MemCheckState {
158
155
. saturating_add ( used_heap_bytes)
159
156
. saturating_add ( external_bytes) ;
160
157
161
- self . current_bytes . store ( total_bytes, Ordering :: Release ) ;
158
+ let heap_stats = WorkerHeapStatistics :: from ( & stats) ;
159
+ let mut state = self . state . write ( ) . unwrap ( ) ;
162
160
163
- if total_bytes >= limit {
164
- self . notify . notify_waiters ( ) ;
161
+ state. current = heap_stats;
165
162
166
- # [ cfg ( debug_assertions ) ]
167
- if !self . exceeded . is_raised ( ) {
168
- self . exceeded . raise ( ) ;
163
+ if total_bytes >= limit {
164
+ if !state . exceeded {
165
+ state . exceeded = true ;
169
166
}
167
+
168
+ drop ( state) ;
169
+ self . notify . notify_waiters ( ) ;
170
170
}
171
171
172
172
total_bytes
173
173
}
174
-
175
- #[ allow( dead_code) ]
176
- #[ cfg( debug_assertions) ]
177
- fn is_exceeded ( & self ) -> bool {
178
- self . exceeded . is_raised ( )
179
- }
180
174
}
181
175
182
176
pub trait GetRuntimeContext {
@@ -201,7 +195,7 @@ pub struct DenoRuntime<RuntimeContext = ()> {
201
195
main_module_id : ModuleId ,
202
196
maybe_inspector : Option < Inspector > ,
203
197
204
- mem_check_state : Arc < MemCheckState > ,
198
+ mem_check : Arc < MemCheck > ,
205
199
waker : Arc < AtomicWaker > ,
206
200
207
201
_phantom_runtime_context : PhantomData < RuntimeContext > ,
@@ -212,7 +206,7 @@ impl<RuntimeContext> Drop for DenoRuntime<RuntimeContext> {
212
206
if self . conf . is_user_worker ( ) {
213
207
self . js_runtime . v8_isolate ( ) . remove_gc_prologue_callback (
214
208
mem_check_gc_prologue_callback_fn,
215
- Arc :: as_ptr ( & self . mem_check_state ) as * mut _ ,
209
+ Arc :: as_ptr ( & self . mem_check ) as * mut _ ,
216
210
) ;
217
211
}
218
212
}
@@ -458,25 +452,25 @@ where
458
452
] ;
459
453
460
454
let mut create_params = None ;
461
- let mut mem_check_state = MemCheckState :: default ( ) ;
455
+ let mut mem_check = MemCheck :: default ( ) ;
462
456
463
457
if conf. is_user_worker ( ) {
464
458
let memory_limit =
465
459
mib_to_bytes ( conf. as_user_worker ( ) . unwrap ( ) . memory_limit_mb ) as usize ;
466
460
467
461
let allocator = CustomAllocator :: new ( memory_limit) ;
468
462
469
- allocator. set_waker ( mem_check_state . waker . clone ( ) ) ;
463
+ allocator. set_waker ( mem_check . waker . clone ( ) ) ;
470
464
471
- mem_check_state . limit = Some ( memory_limit) ;
465
+ mem_check . limit = Some ( memory_limit) ;
472
466
create_params = Some (
473
467
deno_core:: v8:: CreateParams :: default ( )
474
468
. heap_limits ( mib_to_bytes ( 0 ) as usize , memory_limit)
475
469
. array_buffer_allocator ( allocator. into_v8_allocator ( ) ) ,
476
470
)
477
471
} ;
478
472
479
- let mem_check_state = Arc :: new ( mem_check_state ) ;
473
+ let mem_check = Arc :: new ( mem_check ) ;
480
474
let runtime_options = RuntimeOptions {
481
475
extensions,
482
476
is_main : true ,
@@ -543,14 +537,14 @@ where
543
537
if is_user_worker {
544
538
js_runtime. v8_isolate ( ) . add_gc_prologue_callback (
545
539
mem_check_gc_prologue_callback_fn,
546
- Arc :: as_ptr ( & mem_check_state ) as * mut _ ,
540
+ Arc :: as_ptr ( & mem_check ) as * mut _ ,
547
541
GCType :: ALL ,
548
542
) ;
549
543
550
544
js_runtime
551
545
. op_state ( )
552
546
. borrow_mut ( )
553
- . put ( MemCheckWaker :: from ( mem_check_state . waker . clone ( ) ) ) ;
547
+ . put ( MemCheckWaker :: from ( mem_check . waker . clone ( ) ) ) ;
554
548
}
555
549
556
550
js_runtime
@@ -607,8 +601,8 @@ where
607
601
608
602
if is_user_worker {
609
603
drop ( rt:: SUPERVISOR_RT . spawn ( {
610
- let drop_token = mem_check_state . drop_token . clone ( ) ;
611
- let waker = mem_check_state . waker . clone ( ) ;
604
+ let drop_token = mem_check . drop_token . clone ( ) ;
605
+ let waker = mem_check . waker . clone ( ) ;
612
606
613
607
async move {
614
608
// TODO(Nyannyacha): Should we introduce exponential
@@ -641,7 +635,7 @@ where
641
635
main_module_id,
642
636
maybe_inspector,
643
637
644
- mem_check_state ,
638
+ mem_check ,
645
639
waker : Arc :: default ( ) ,
646
640
647
641
_phantom_runtime_context : PhantomData ,
@@ -735,7 +729,7 @@ where
735
729
let is_termination_requested = self . is_termination_requested . clone ( ) ;
736
730
let is_user_worker = self . conf . is_user_worker ( ) ;
737
731
let global_waker = self . waker . clone ( ) ;
738
- let mem_check_state = is_user_worker. then ( || self . mem_check_state . clone ( ) ) ;
732
+ let mem_check = is_user_worker. then ( || self . mem_check . clone ( ) ) ;
739
733
740
734
let poll_result = poll_fn ( |cx| unsafe {
741
735
// INVARIANT: Only can steal current task by other threads when LIFO
@@ -809,7 +803,7 @@ where
809
803
} ) ) ;
810
804
811
805
if is_user_worker {
812
- let mem_state = mem_check_state . as_ref ( ) . unwrap ( ) ;
806
+ let mem_state = mem_check . as_ref ( ) . unwrap ( ) ;
813
807
let total_malloced_bytes = mem_state. check ( js_runtime. v8_isolate ( ) . as_mut ( ) ) ;
814
808
815
809
mem_state. waker . register ( waker) ;
@@ -859,24 +853,31 @@ where
859
853
self . maybe_inspector . clone ( )
860
854
}
861
855
862
- pub fn mem_check_captured_bytes ( & self ) -> Arc < AtomicUsize > {
863
- self . mem_check_state . current_bytes . clone ( )
856
+ pub fn mem_check_state ( & self ) -> Arc < RwLock < MemCheckState > > {
857
+ self . mem_check . state . clone ( )
864
858
}
865
859
866
860
pub fn add_memory_limit_callback < C > ( & self , mut cb : C )
867
861
where
868
862
// XXX(Nyannyacha): Should we relax bounds a bit more?
869
- C : FnMut ( usize ) -> bool + Send + ' static ,
863
+ C : FnMut ( MemCheckState ) -> bool + Send + ' static ,
870
864
{
871
- let notify = self . mem_check_state . notify . clone ( ) ;
872
- let drop_token = self . mem_check_state . drop_token . clone ( ) ;
873
- let current_bytes = self . mem_check_state . current_bytes . clone ( ) ;
865
+ let notify = self . mem_check . notify . clone ( ) ;
866
+ let drop_token = self . mem_check . drop_token . clone ( ) ;
867
+ let state = self . mem_check_state ( ) ;
874
868
875
869
drop ( rt:: SUPERVISOR_RT . spawn ( async move {
876
870
loop {
877
871
tokio:: select! {
878
872
_ = notify. notified( ) => {
879
- if cb( current_bytes. load( Ordering :: Acquire ) ) {
873
+ let state = tokio:: task:: spawn_blocking( {
874
+ let state = state. clone( ) ;
875
+ move || {
876
+ * state. read( ) . unwrap( )
877
+ }
878
+ } ) . await . unwrap( ) ;
879
+
880
+ if cb( state) {
880
881
break ;
881
882
}
882
883
}
@@ -931,7 +932,7 @@ extern "C" fn mem_check_gc_prologue_callback_fn(
931
932
data : * mut c_void ,
932
933
) {
933
934
unsafe {
934
- ( * ( data as * mut MemCheckState ) ) . check ( & mut * isolate) ;
935
+ ( * ( data as * mut MemCheck ) ) . check ( & mut * isolate) ;
935
936
}
936
937
}
937
938
@@ -1607,7 +1608,7 @@ mod test {
1607
1608
assert ! ( result. is_ok( ) , "expected no errors" ) ;
1608
1609
1609
1610
// however, mem checker must be raised because it aggregates heap usage
1610
- assert ! ( user_rt. mem_check_state . is_exceeded ( ) ) ;
1611
+ assert ! ( user_rt. mem_check . state . read ( ) . unwrap ( ) . exceeded ) ;
1611
1612
}
1612
1613
1613
1614
#[ tokio:: test]
@@ -1661,7 +1662,7 @@ mod test {
1661
1662
1662
1663
callback_rx. recv ( ) . await . unwrap ( ) ;
1663
1664
1664
- assert ! ( user_rt. mem_check_state . is_exceeded ( ) ) ;
1665
+ assert ! ( user_rt. mem_check . state . read ( ) . unwrap ( ) . exceeded ) ;
1665
1666
} ;
1666
1667
1667
1668
if timeout ( Duration :: from_secs ( 10 ) , wait_fut) . await . is_err ( ) {
0 commit comments