Skip to content

Commit 8db09e1

Browse files
authored
Merge pull request #45 from CertainLach/gc-v2
Implement tracing Gc with rust-gc
2 parents 1f2f94e + 56cee93 commit 8db09e1

File tree

35 files changed

+865
-400
lines changed

35 files changed

+865
-400
lines changed

Cargo.lock

Lines changed: 39 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindings/jsonnet/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ publish = false
1010
[dependencies]
1111
jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.8" }
1212
jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.8" }
13+
jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
1314

1415
[lib]
1516
crate-type = ["cdylib"]

bindings/jsonnet/src/native.rs

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
use jrsonnet_evaluator::{error::Error, native::NativeCallback, EvaluationState, Val};
1+
use jrsonnet_evaluator::{
2+
error::{Error, LocError},
3+
native::{NativeCallback, NativeCallbackHandler},
4+
EvaluationState, Val,
5+
};
6+
use jrsonnet_gc::{unsafe_empty_trace, Finalize, Gc, Trace};
27
use jrsonnet_parser::{Param, ParamsDesc};
38
use std::{
49
ffi::{c_void, CStr},
510
os::raw::{c_char, c_int},
11+
path::Path,
612
rc::Rc,
713
};
814

@@ -12,6 +18,39 @@ type JsonnetNativeCallback = unsafe extern "C" fn(
1218
success: *mut c_int,
1319
) -> *mut Val;
1420

21+
struct JsonnetNativeCallbackHandler {
22+
ctx: *const c_void,
23+
cb: JsonnetNativeCallback,
24+
}
25+
impl Finalize for JsonnetNativeCallbackHandler {}
26+
unsafe impl Trace for JsonnetNativeCallbackHandler {
27+
unsafe_empty_trace!();
28+
}
29+
impl NativeCallbackHandler for JsonnetNativeCallbackHandler {
30+
fn call(&self, _from: Option<Rc<Path>>, args: &[Val]) -> Result<Val, LocError> {
31+
let mut n_args = Vec::new();
32+
for a in args {
33+
n_args.push(Some(Box::new(a.clone())));
34+
}
35+
n_args.push(None);
36+
let mut success = 1;
37+
let v = unsafe {
38+
(self.cb)(
39+
self.ctx,
40+
&n_args as *const _ as *const *const Val,
41+
&mut success,
42+
)
43+
};
44+
let v = unsafe { *Box::from_raw(v) };
45+
if success == 1 {
46+
Ok(v)
47+
} else {
48+
let e = v.try_cast_str("native error").expect("error msg");
49+
Err(Error::RuntimeError(e).into())
50+
}
51+
}
52+
}
53+
1554
/// # Safety
1655
#[no_mangle]
1756
pub unsafe extern "C" fn jsonnet_native_callback(
@@ -35,21 +74,9 @@ pub unsafe extern "C" fn jsonnet_native_callback(
3574

3675
vm.add_native(
3776
name,
38-
Rc::new(NativeCallback::new(params, move |_caller, args| {
39-
let mut n_args = Vec::new();
40-
for a in args {
41-
n_args.push(Some(Box::new(a.clone())));
42-
}
43-
n_args.push(None);
44-
let mut success = 1;
45-
let v = cb(ctx, &n_args as *const _ as *const *const Val, &mut success);
46-
let v = *Box::from_raw(v);
47-
if success == 1 {
48-
Ok(v)
49-
} else {
50-
let e = v.try_cast_str("native error").expect("error msg");
51-
Err(Error::RuntimeError(e).into())
52-
}
53-
})),
77+
Gc::new(NativeCallback::new(
78+
params,
79+
Box::new(JsonnetNativeCallbackHandler { ctx, cb }),
80+
)),
5481
)
5582
}

bindings/jsonnet/src/val_make.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
//! Create values in VM
22
33
use jrsonnet_evaluator::{ArrValue, EvaluationState, ObjValue, Val};
4+
use jrsonnet_gc::Gc;
45
use std::{
56
ffi::CStr,
67
os::raw::{c_char, c_double, c_int},
7-
rc::Rc,
88
};
99

1010
/// # Safety
@@ -38,7 +38,7 @@ pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> *mut Val {
3838

3939
#[no_mangle]
4040
pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val {
41-
Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Rc::new(Vec::new())))))
41+
Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Gc::new(Vec::new())))))
4242
}
4343

4444
#[no_mangle]

bindings/jsonnet/src/val_modify.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
//! In jrsonnet every value is immutable, and this code is probally broken
44
55
use jrsonnet_evaluator::{ArrValue, EvaluationState, LazyBinding, LazyVal, ObjMember, Val};
6+
use jrsonnet_gc::Gc;
67
use jrsonnet_parser::Visibility;
7-
use std::{ffi::CStr, os::raw::c_char, rc::Rc};
8+
use std::{ffi::CStr, os::raw::c_char};
89

910
/// # Safety
1011
///
@@ -22,7 +23,7 @@ pub unsafe extern "C" fn jsonnet_json_array_append(
2223
new.push(item);
2324
}
2425
new.push(LazyVal::new_resolved(val.clone()));
25-
*arr = Val::Arr(ArrValue::Lazy(Rc::new(new)));
26+
*arr = Val::Arr(ArrValue::Lazy(Gc::new(new)));
2627
}
2728
_ => panic!("should receive array"),
2829
}

cmds/jrsonnet/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ edition = "2018"
88
publish = false
99

1010
[features]
11-
default = []
11+
default = ["mimalloc"]
1212
# Use mimalloc as allocator
13-
mimalloc = []
13+
mimalloc = ["mimallocator"]
1414

1515
[dependencies]
1616
jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.8" }
1717
jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.8" }
1818
jrsonnet-cli = { path = "../../crates/jrsonnet-cli", version = "0.3.8" }
19-
# TODO: Fix mimalloc compile errors, and use them
2019
mimallocator = { version = "0.1.3", optional = true }
2120
thiserror = "1.0"
2221

cmds/jrsonnet/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clap::{AppSettings, Clap, IntoApp};
2-
use jrsonnet_cli::{ConfigureState, GeneralOpts, InputOpts, ManifestOpts, OutputOpts};
2+
use jrsonnet_cli::{ConfigureState, GcOpts, GeneralOpts, InputOpts, ManifestOpts, OutputOpts};
33
use jrsonnet_evaluator::{error::LocError, EvaluationState, ManifestFormat};
44
use std::{
55
fs::{create_dir_all, File},
@@ -61,6 +61,8 @@ struct Opts {
6161
output: OutputOpts,
6262
#[clap(flatten)]
6363
debug: DebugOpts,
64+
#[clap(flatten)]
65+
gc: GcOpts,
6466
}
6567

6668
fn main() {
@@ -114,6 +116,7 @@ impl From<LocError> for Error {
114116
}
115117

116118
fn main_catch(opts: Opts) -> bool {
119+
let _printer = opts.gc.stats_printer();
117120
let state = EvaluationState::default();
118121
if let Err(e) = main_real(&state, opts) {
119122
if let Error::Evaluation(e) = e {
@@ -127,6 +130,7 @@ fn main_catch(opts: Opts) -> bool {
127130
}
128131

129132
fn main_real(state: &EvaluationState, opts: Opts) -> Result<(), Error> {
133+
opts.gc.configure_global();
130134
opts.general.configure(&state)?;
131135
opts.manifest.configure(&state)?;
132136

crates/jrsonnet-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ publish = false
1010
[dependencies]
1111
jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.6", features = ["explaining-traces"] }
1212
jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.6" }
13+
jrsonnet-gc = { version = "0.4.2", features = ["derive", "unstable-config", "unstable-stats"] }
1314

1415
[dependencies.clap]
1516
git = "https://github.com/clap-rs/clap"

crates/jrsonnet-cli/src/lib.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,55 @@ impl ConfigureState for GeneralOpts {
9595
Ok(())
9696
}
9797
}
98+
99+
#[derive(Clap)]
100+
#[clap(help_heading = "GARBAGE COLLECTION")]
101+
pub struct GcOpts {
102+
/// Min bytes allocated to start garbage collection
103+
#[clap(long, default_value = "20000000")]
104+
gc_initial_threshold: usize,
105+
/// How much heap should grow after unsuccessful garbage collection
106+
#[clap(long)]
107+
gc_used_space_ratio: Option<f64>,
108+
/// Do not skip gc on exit
109+
#[clap(long)]
110+
gc_collect_on_exit: bool,
111+
/// Print gc stats before exit
112+
#[clap(long)]
113+
gc_print_stats: bool,
114+
/// Force garbage collection before printing stats
115+
/// Useful for checking for memory leaks
116+
/// Does nothing useless --gc-print-stats is specified
117+
#[clap(long)]
118+
gc_collect_before_printing_stats: bool,
119+
}
120+
impl GcOpts {
121+
pub fn stats_printer(&self) -> Option<GcStatsPrinter> {
122+
self.gc_print_stats
123+
.then(|| GcStatsPrinter(self.gc_collect_before_printing_stats))
124+
}
125+
pub fn configure_global(&self) {
126+
jrsonnet_gc::configure(|config| {
127+
config.leak_on_drop = !self.gc_collect_on_exit;
128+
config.threshold = self.gc_initial_threshold;
129+
if let Some(used_space_ratio) = self.gc_used_space_ratio {
130+
config.used_space_ratio = used_space_ratio;
131+
}
132+
});
133+
}
134+
}
135+
pub struct GcStatsPrinter(bool);
136+
impl Drop for GcStatsPrinter {
137+
fn drop(&mut self) {
138+
if self.0 {
139+
jrsonnet_gc::force_collect()
140+
}
141+
eprintln!("=== GC STATS ===");
142+
jrsonnet_gc::configure(|c| {
143+
eprintln!("Final threshold: {:?}", c.threshold);
144+
});
145+
let stats = jrsonnet_gc::stats();
146+
eprintln!("Collections performed: {}", stats.collections_performed);
147+
eprintln!("Bytes still allocated: {}", stats.bytes_allocated);
148+
}
149+
}

crates/jrsonnet-evaluator/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@ jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.8" }
3030
jrsonnet-types = { path = "../jrsonnet-types", version = "0.3.8" }
3131
pathdiff = "0.2.0"
3232

33-
closure = "0.3.0"
34-
3533
md5 = "0.7.0"
3634
base64 = "0.13.0"
3735
rustc-hash = "1.1.0"
3836

3937
thiserror = "1.0"
38+
jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
4039

4140
[dependencies.anyhow]
4241
version = "1.0"

0 commit comments

Comments
 (0)