Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
张重言
deno
提交
f425b514
D
deno
项目概览
张重言
/
deno
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
deno
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
f425b514
编写于
8月 12, 2020
作者:
M
Marcus Weiner
提交者:
GitHub
8月 12, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
core: memory limits & callbacks (#6914)
上级
eed77aa0
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
172 addition
and
10 deletion
+172
-10
core/core_isolate.rs
core/core_isolate.rs
+172
-10
未找到文件。
core/core_isolate.rs
浏览文件 @
f425b514
...
...
@@ -20,9 +20,11 @@ use futures::stream::FuturesUnordered;
use
futures
::
stream
::
StreamExt
;
use
futures
::
task
::
AtomicWaker
;
use
futures
::
Future
;
use
std
::
any
::
Any
;
use
std
::
cell
::
RefCell
;
use
std
::
collections
::
HashMap
;
use
std
::
convert
::
From
;
use
std
::
ffi
::
c_void
;
use
std
::
mem
::
forget
;
use
std
::
ops
::
Deref
;
use
std
::
ops
::
DerefMut
;
...
...
@@ -71,8 +73,25 @@ pub enum StartupData<'a> {
None
,
}
impl
StartupData
<
'_
>
{
fn
into_options
(
self
)
->
(
Option
<
OwnedScript
>
,
Option
<
Snapshot
>
)
{
match
self
{
Self
::
Script
(
script
)
=>
(
Some
(
script
.into
()),
None
),
Self
::
Snapshot
(
snapshot
)
=>
(
None
,
Some
(
snapshot
)),
Self
::
None
=>
(
None
,
None
),
}
}
}
type
JSErrorCreateFn
=
dyn
Fn
(
JSError
)
->
ErrBox
;
/// Objects that need to live as long as the isolate
#[derive(Default)]
struct
IsolateAllocations
{
near_heap_limit_callback_data
:
Option
<
(
Box
<
RefCell
<
dyn
Any
>>
,
v8
::
NearHeapLimitCallback
)
>
,
}
/// A single execution context of JavaScript. Corresponds roughly to the "Web
/// Worker" concept in the DOM. An CoreIsolate is a Future that can be used with
/// Tokio. The CoreIsolate future completes when there is an error or when all
...
...
@@ -89,6 +108,7 @@ pub struct CoreIsolate {
has_snapshotted
:
bool
,
needs_init
:
bool
,
startup_script
:
Option
<
OwnedScript
>
,
allocations
:
IsolateAllocations
,
}
/// Internal state for CoreIsolate which is stored in one of v8::Isolate's
...
...
@@ -162,25 +182,75 @@ pub unsafe fn v8_init() {
v8
::
V8
::
set_flags_from_command_line
(
argv
);
}
/// Minimum and maximum bytes of heap used in an isolate
pub
struct
HeapLimits
{
/// By default V8 starts with a small heap and dynamically grows it to match
/// the set of live objects. This may lead to ineffective garbage collections
/// at startup if the live set is large. Setting the initial heap size avoids
/// such garbage collections. Note that this does not affect young generation
/// garbage collections.
pub
initial
:
usize
,
/// When the heap size approaches `max`, V8 will perform series of
/// garbage collections and invoke the
/// [NearHeapLimitCallback](TODO).
/// If the garbage collections do not help and the callback does not
/// increase the limit, then V8 will crash with V8::FatalProcessOutOfMemory.
pub
max
:
usize
,
}
pub
(
crate
)
struct
IsolateOptions
{
will_snapshot
:
bool
,
startup_script
:
Option
<
OwnedScript
>
,
startup_snapshot
:
Option
<
Snapshot
>
,
heap_limits
:
Option
<
HeapLimits
>
,
}
impl
CoreIsolate
{
/// startup_data defines the snapshot or script used at startup to initialize
/// the isolate.
pub
fn
new
(
startup_data
:
StartupData
,
will_snapshot
:
bool
)
->
Self
{
let
(
startup_script
,
startup_snapshot
)
=
startup_data
.into_options
();
let
options
=
IsolateOptions
{
will_snapshot
,
startup_script
,
startup_snapshot
,
heap_limits
:
None
,
};
Self
::
from_options
(
options
)
}
/// This is useful for controlling memory usage of scripts.
///
/// See [`HeapLimits`](struct.HeapLimits.html) for more details.
///
/// Make sure to use [`add_near_heap_limit_callback`](#method.add_near_heap_limit_callback)
/// to prevent v8 from crashing when reaching the upper limit.
pub
fn
with_heap_limits
(
startup_data
:
StartupData
,
heap_limits
:
HeapLimits
,
)
->
Self
{
let
(
startup_script
,
startup_snapshot
)
=
startup_data
.into_options
();
let
options
=
IsolateOptions
{
will_snapshot
:
false
,
startup_script
,
startup_snapshot
,
heap_limits
:
Some
(
heap_limits
),
};
Self
::
from_options
(
options
)
}
fn
from_options
(
options
:
IsolateOptions
)
->
Self
{
static
DENO_INIT
:
Once
=
Once
::
new
();
DENO_INIT
.call_once
(||
{
unsafe
{
v8_init
()
};
});
let
(
startup_script
,
startup_snapshot
)
=
match
startup_data
{
StartupData
::
Script
(
script
)
=>
(
Some
(
script
.into
()),
None
),
StartupData
::
Snapshot
(
snapshot
)
=>
(
None
,
Some
(
snapshot
)),
StartupData
::
None
=>
(
None
,
None
),
};
let
global_context
;
let
(
mut
isolate
,
maybe_snapshot_creator
)
=
if
will_snapshot
{
let
(
mut
isolate
,
maybe_snapshot_creator
)
=
if
options
.
will_snapshot
{
// TODO(ry) Support loading snapshots before snapshotting.
assert
!
(
startup_snapshot
.is_none
());
assert
!
(
options
.
startup_snapshot
.is_none
());
let
mut
creator
=
v8
::
SnapshotCreator
::
new
(
Some
(
&
bindings
::
EXTERNAL_REFERENCES
));
let
isolate
=
unsafe
{
creator
.get_owned_isolate
()
};
...
...
@@ -195,7 +265,7 @@ impl CoreIsolate {
}
else
{
let
mut
params
=
v8
::
Isolate
::
create_params
()
.external_references
(
&**
bindings
::
EXTERNAL_REFERENCES
);
let
snapshot_loaded
=
if
let
Some
(
snapshot
)
=
startup_snapshot
{
let
snapshot_loaded
=
if
let
Some
(
snapshot
)
=
options
.
startup_snapshot
{
params
=
match
snapshot
{
Snapshot
::
Static
(
data
)
=>
params
.snapshot_blob
(
data
),
Snapshot
::
JustCreated
(
data
)
=>
params
.snapshot_blob
(
data
),
...
...
@@ -206,6 +276,10 @@ impl CoreIsolate {
false
};
if
let
Some
(
heap_limits
)
=
options
.heap_limits
{
params
=
params
.heap_limits
(
heap_limits
.initial
,
heap_limits
.max
)
}
let
isolate
=
v8
::
Isolate
::
new
(
params
);
let
mut
isolate
=
CoreIsolate
::
setup_isolate
(
isolate
);
{
...
...
@@ -243,7 +317,8 @@ impl CoreIsolate {
snapshot_creator
:
maybe_snapshot_creator
,
has_snapshotted
:
false
,
needs_init
:
true
,
startup_script
,
startup_script
:
options
.startup_script
,
allocations
:
IsolateAllocations
::
default
(),
}
}
...
...
@@ -353,6 +428,49 @@ impl CoreIsolate {
let
mut
state
=
state_rc
.borrow_mut
();
state
.op_registry
.register
(
name
,
op
)
}
/// Registers a callback on the isolate when the memory limits are approached.
/// Use this to prevent V8 from crashing the process when reaching the limit.
///
/// Calls the closure with the current heap limit and the initial heap limit.
/// The return value of the closure is set as the new limit.
pub
fn
add_near_heap_limit_callback
<
C
>
(
&
mut
self
,
cb
:
C
)
where
C
:
FnMut
(
usize
,
usize
)
->
usize
+
'static
,
{
let
boxed_cb
=
Box
::
new
(
RefCell
::
new
(
cb
));
let
data
=
boxed_cb
.as_ptr
()
as
*
mut
c_void
;
self
.allocations.near_heap_limit_callback_data
=
Some
((
boxed_cb
,
near_heap_limit_callback
::
<
C
>
));
self
.v8_isolate
.as_mut
()
.unwrap
()
.add_near_heap_limit_callback
(
near_heap_limit_callback
::
<
C
>
,
data
);
}
pub
fn
remove_near_heap_limit_callback
(
&
mut
self
,
heap_limit
:
usize
)
{
if
let
Some
((
_
,
cb
))
=
self
.allocations.near_heap_limit_callback_data
.take
()
{
self
.v8_isolate
.as_mut
()
.unwrap
()
.remove_near_heap_limit_callback
(
cb
,
heap_limit
);
}
}
}
extern
"C"
fn
near_heap_limit_callback
<
F
>
(
data
:
*
mut
c_void
,
current_heap_limit
:
usize
,
initial_heap_limit
:
usize
,
)
->
usize
where
F
:
FnMut
(
usize
,
usize
)
->
usize
,
{
let
callback
=
unsafe
{
&
mut
*
(
data
as
*
mut
F
)
};
callback
(
current_heap_limit
,
initial_heap_limit
)
}
impl
Future
for
CoreIsolate
{
...
...
@@ -1188,4 +1306,48 @@ pub mod tests {
let
mut
isolate2
=
CoreIsolate
::
new
(
startup_data
,
false
);
js_check
(
isolate2
.execute
(
"check.js"
,
"if (a != 3) throw Error('x')"
));
}
#[test]
fn
test_heap_limits
()
{
let
heap_limits
=
HeapLimits
{
initial
:
0
,
max
:
20
*
1024
,
// 20 kB
};
let
mut
isolate
=
CoreIsolate
::
with_heap_limits
(
StartupData
::
None
,
heap_limits
);
let
cb_handle
=
isolate
.thread_safe_handle
();
let
callback_invoke_count
=
Rc
::
new
(
AtomicUsize
::
default
());
let
inner_invoke_count
=
Rc
::
clone
(
&
callback_invoke_count
);
isolate
.add_near_heap_limit_callback
(
move
|
current_limit
,
_
initial_limit
|
{
inner_invoke_count
.fetch_add
(
1
,
Ordering
::
SeqCst
);
cb_handle
.terminate_execution
();
current_limit
*
2
},
);
let
err
=
isolate
.execute
(
"script name"
,
r#"let s = ""; while(true) { s += "Hello"; }"#
,
)
.expect_err
(
"script should fail"
);
assert_eq!
(
"Uncaught Error: execution terminated"
,
err
.downcast
::
<
JSError
>
()
.unwrap
()
.message
);
assert
!
(
callback_invoke_count
.load
(
Ordering
::
SeqCst
)
>
0
)
}
#[test]
fn
test_heap_limit_cb_remove
()
{
let
mut
isolate
=
CoreIsolate
::
new
(
StartupData
::
None
,
false
);
isolate
.add_near_heap_limit_callback
(|
current_limit
,
_
initial_limit
|
{
current_limit
*
2
});
isolate
.remove_near_heap_limit_callback
(
20
*
1024
);
assert
!
(
isolate
.allocations.near_heap_limit_callback_data
.is_none
());
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录