Skip to content

Commit 34be70d

Browse files
committed
Implement near-heap-limit termination
1 parent 2de3378 commit 34be70d

5 files changed

Lines changed: 227 additions & 102 deletions

File tree

crates/core/src/config.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,12 @@ pub struct V8HeapPolicyConfig {
185185
pub heap_gc_trigger_fraction: f64,
186186
#[serde(default = "def_retire", deserialize_with = "de_fraction")]
187187
pub heap_retire_fraction: f64,
188-
#[serde(default, rename = "heap-limit-mb", deserialize_with = "de_limit_mb")]
189-
pub heap_limit_bytes: Option<usize>,
188+
#[serde(
189+
default = "def_heap_limit",
190+
rename = "heap-limit-mb",
191+
deserialize_with = "de_limit_mb"
192+
)]
193+
pub heap_limit_bytes: usize,
190194
}
191195

192196
impl Default for V8HeapPolicyConfig {
@@ -196,7 +200,7 @@ impl Default for V8HeapPolicyConfig {
196200
heap_check_time_interval: def_time_interval(),
197201
heap_gc_trigger_fraction: def_gc_trigger(),
198202
heap_retire_fraction: def_retire(),
199-
heap_limit_bytes: None,
203+
heap_limit_bytes: def_heap_limit(),
200204
}
201205
}
202206
}
@@ -237,6 +241,12 @@ fn def_retire() -> f64 {
237241
0.75
238242
}
239243

244+
/// Default heap limit, in bytes
245+
fn def_heap_limit() -> usize {
246+
// 1 GiB
247+
1024 * 1024 * 1024
248+
}
249+
240250
fn de_nz_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
241251
where
242252
D: serde::Deserializer<'de>,
@@ -289,22 +299,20 @@ where
289299
}
290300
}
291301

292-
fn de_limit_mb<'de, D>(deserializer: D) -> Result<Option<usize>, D::Error>
302+
fn de_limit_mb<'de, D>(deserializer: D) -> Result<usize, D::Error>
293303
where
294304
D: serde::Deserializer<'de>,
295305
{
296306
let value = u64::deserialize(deserializer)?;
297307
if value == 0 {
298-
return Ok(None);
308+
return Ok(def_heap_limit());
299309
}
300310

301311
let bytes = value
302312
.checked_mul(1024 * 1024)
303313
.ok_or_else(|| serde::de::Error::custom("heap-limit-mb is too large"))?;
304314

305-
usize::try_from(bytes)
306-
.map(Some)
307-
.map_err(|_| serde::de::Error::custom("heap-limit-mb does not fit in usize"))
315+
usize::try_from(bytes).map_err(|_| serde::de::Error::custom("heap-limit-mb does not fit in usize"))
308316
}
309317

310318
#[cfg(test)]
@@ -420,7 +428,7 @@ mod tests {
420428
);
421429
assert_eq!(config.v8_heap_policy.heap_gc_trigger_fraction, 0.67);
422430
assert_eq!(config.v8_heap_policy.heap_retire_fraction, 0.75);
423-
assert_eq!(config.v8_heap_policy.heap_limit_bytes, None);
431+
assert_eq!(config.v8_heap_policy.heap_limit_bytes, 1024 * 1024 * 1024);
424432
}
425433

426434
#[test]
@@ -443,6 +451,6 @@ mod tests {
443451
);
444452
assert_eq!(config.v8_heap_policy.heap_gc_trigger_fraction, 0.6);
445453
assert_eq!(config.v8_heap_policy.heap_retire_fraction, 0.8);
446-
assert_eq!(config.v8_heap_policy.heap_limit_bytes, Some(256 * 1024 * 1024));
454+
assert_eq!(config.v8_heap_policy.heap_limit_bytes, 256 * 1024 * 1024);
447455
}
448456
}

crates/core/src/host/v8/error.rs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ pub(crate) struct ExceptionThrown {
217217

218218
impl ExceptionThrown {
219219
/// Turns a caught JS exception in `scope` into a [`JSError`].
220-
pub(crate) fn into_error(self, scope: &mut PinTryCatch) -> JsError {
220+
pub(crate) fn into_error(self, scope: &mut PinTryCatch) -> Result<JsError, UnknownJsError> {
221221
JsError::from_caught(scope)
222222
}
223223
}
@@ -254,12 +254,15 @@ pub(super) enum ErrorOrException<Exc> {
254254
Exception(Exc),
255255
}
256256

257-
impl<Exc> ErrorOrException<Exc> {
258-
pub(super) fn map_exception<Exc2>(self, f: impl FnOnce(Exc) -> Exc2) -> ErrorOrException<Exc2> {
259-
match self {
257+
impl ErrorOrException<ExceptionThrown> {
258+
pub(super) fn exc_into_error(
259+
self,
260+
scope: &mut PinTryCatch<'_, '_, '_, '_>,
261+
) -> Result<ErrorOrException<JsError>, UnknownJsError> {
262+
Ok(match self {
260263
ErrorOrException::Err(e) => ErrorOrException::Err(e),
261-
ErrorOrException::Exception(exc) => ErrorOrException::Exception(f(exc)),
262-
}
264+
ErrorOrException::Exception(exc) => ErrorOrException::Exception(exc.into_error(scope)?),
265+
})
263266
}
264267
}
265268

@@ -275,6 +278,12 @@ impl From<ExceptionThrown> for ErrorOrException<ExceptionThrown> {
275278
}
276279
}
277280

281+
impl From<JsError> for ErrorOrException<JsError> {
282+
fn from(e: JsError) -> Self {
283+
Self::Exception(e)
284+
}
285+
}
286+
278287
impl From<ErrorOrException<JsError>> for anyhow::Error {
279288
fn from(err: ErrorOrException<JsError>) -> Self {
280289
match err {
@@ -528,23 +537,41 @@ fn get_or_insert_slot<T: 'static>(isolate: &mut v8::Isolate, default: impl FnOnc
528537

529538
impl JsError {
530539
/// Turns a caught JS exception in `scope` into a [`JSError`].
531-
fn from_caught(scope: &mut PinTryCatch<'_, '_, '_, '_>) -> Self {
532-
match scope.message() {
533-
Some(message) => Self {
534-
trace: message
535-
.get_stack_trace(scope)
536-
.map(|trace| JsStackTrace::from_trace(scope, trace))
537-
.unwrap_or_default(),
538-
msg: message.get(scope).to_rust_string_lossy(scope),
539-
},
540-
None => Self {
541-
trace: JsStackTrace::default(),
542-
msg: "unknown error".to_owned(),
543-
},
540+
fn from_caught(scope: &mut PinTryCatch<'_, '_, '_, '_>) -> Result<Self, UnknownJsError> {
541+
let message = scope.message().ok_or(UnknownJsError)?;
542+
Ok(Self {
543+
trace: message
544+
.get_stack_trace(scope)
545+
.map(|trace| JsStackTrace::from_trace(scope, trace))
546+
.unwrap_or_default(),
547+
msg: message.get(scope).to_rust_string_lossy(scope),
548+
})
549+
}
550+
}
551+
552+
pub(super) struct UnknownJsError;
553+
554+
impl From<UnknownJsError> for JsError {
555+
fn from(_: UnknownJsError) -> Self {
556+
Self {
557+
trace: JsStackTrace::default(),
558+
msg: "unknown error".to_owned(),
544559
}
545560
}
546561
}
547562

563+
impl From<UnknownJsError> for ErrorOrException<JsError> {
564+
fn from(e: UnknownJsError) -> Self {
565+
Self::Exception(e.into())
566+
}
567+
}
568+
569+
impl From<UnknownJsError> for anyhow::Error {
570+
fn from(e: UnknownJsError) -> Self {
571+
JsError::from(e).into()
572+
}
573+
}
574+
548575
pub(super) fn log_traceback(replica_ctx: &ReplicaContext, func_type: &str, func: &str, e: &anyhow::Error) {
549576
log::info!("{func_type} \"{func}\" runtime error: {e:}");
550577
if let Some(js_err) = e.downcast_ref::<JsError>() {
@@ -573,7 +600,7 @@ pub(super) fn catch_exception<'scope, T>(
573600
body: impl FnOnce(&mut PinTryCatch<'scope, '_, '_, '_>) -> Result<T, ErrorOrException<ExceptionThrown>>,
574601
) -> Result<T, ErrorOrException<JsError>> {
575602
tc_scope!(scope, scope);
576-
body(scope).map_err(|e| e.map_exception(|exc| exc.into_error(scope)))
603+
body(scope).map_err(|e| e.exc_into_error(scope).unwrap_or_else(Into::into))
577604
}
578605

579606
pub(super) type PinTryCatch<'scope, 'iso, 'x, 's> = PinnedRef<'x, TryCatch<'s, 'scope, HandleScope<'iso>>>;

0 commit comments

Comments
 (0)