You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# What ?
Split `SharedRuntime` into a trait with three implementations:
- `ForkSafeRuntime` *(native only)*: owns a multi-thread tokio runtime, supports fork
hooks (`before_fork` / `after_fork_parent` / `after_fork_child`) and synchronous
`shutdown`.
- `BasicRuntime` *(native only)*: wraps a library-built or caller-provided
`Arc<tokio::runtime::Runtime>`, no fork hooks, no synchronous shutdown.
- `LocalRuntime` *(wasm32 only)*: single-threaded executor that spawns workers via
`wasm_bindgen_futures::spawn_local`, no fork protocol, async-only.
`BlockingRuntime` *(native only)* is a sub-trait of `SharedRuntime` that adds
`block_on`; implemented by `ForkSafeRuntime` and `BasicRuntime`. Sync facades on the
trace exporter (`send` / `shutdown` / `build`) bound their runtime parameter on it.
`TraceExporter` is generic over the runtime — `TraceExporter<C, R: SharedRuntime>`
— so callers pick `ForkSafeRuntime`, `BasicRuntime`, or `LocalRuntime` explicitly. The
FFI pins `R = ForkSafeRuntime` for ABI stability.
# Why ?
Some callers already have a tokio runtime they want to reuse instead of letting
libdatadog create its own. The previous `SharedRuntime` only supported the
fork-safe owned case. Splitting into distinct types makes the lifecycle and
fork-safety contract explicit and lets callers pick the model that matches their
environment.
The wasm32 target previously shared a file with the native implementation behind
`#[cfg]` walls; it is now a dedicated module with a clean separation.
# How ?
- Introduce a `SharedRuntime` trait (`new`, `spawn_worker`, `shutdown_async`).
- Add a native-only `BlockingRuntime: SharedRuntime` sub-trait with `block_on`.
- Move the existing owned-runtime logic into `ForkSafeRuntime`; fork hooks and sync
`shutdown` are inherent methods (not part of the trait).
- Add `BasicRuntime::with_worker_threads` (library-built) and
`BasicRuntime::from_handle(Arc<Runtime>)` (caller-provided).
- Extract the wasm32 `spawn_local` path from `fork_safe.rs` into its own
`local.rs` module as `LocalRuntime`.
- Make `TraceExporter<C, R>` and `TraceExporterBuilder<R>` generic over the runtime;
sync entry points additionally require `R: BlockingRuntime`. `Default` for the
builder is impl'd only for `R = ForkSafeRuntime` on native so
`TraceExporterBuilder::default()` resolves unambiguously.
- `restart_on_fork = true` is silently ignored (with a `warn!`) on `BasicRuntime`
and `LocalRuntime` since they do not implement a fork protocol.
- Update FFI to pin `R = ForkSafeRuntime` in its `TraceExporter` type alias.
# Additional Notes
- Breaking change for Rust callers of `libdd_shared_runtime`: `SharedRuntime` is
now a trait; use `ForkSafeRuntime::with_worker_threads(1)` (or the trait method
`SharedRuntime::new()` with the trait in scope) instead of `SharedRuntime::new()`.
- FFI handle type changes from `SharedRuntime` to `ForkSafeRuntime`.
Co-authored-by: jules.wiriath <jules.wiriath@datadoghq.com>
let payload = Regex::new(r#""metric":"trace_api.bytes","tags":\["src_library:libdatadog"\],"sketch_b64":".+","common":true,"interval":\d+,"type":"distribution""#).unwrap();
398
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
398
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
399
399
let server = MockServer::start();
400
400
letmut telemetry_srv = server.mock(|when, then| {
401
401
when.method(POST).body_matches(payload);
@@ -426,7 +426,7 @@ mod tests {
426
426
#[test]
427
427
fnrequests_test(){
428
428
let payload = Regex::new(r#""metric":"trace_api.requests","points":\[\[\d+,1\.0\]\],"tags":\["src_library:libdatadog"\],"common":true,"type":"count""#).unwrap();
429
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
429
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
430
430
let server = MockServer::start();
431
431
letmut telemetry_srv = server.mock(|when, then| {
432
432
when.method(POST).body_matches(payload);
@@ -457,7 +457,7 @@ mod tests {
457
457
#[test]
458
458
fnresponses_per_code_test(){
459
459
let payload = Regex::new(r#""metric":"trace_api.responses","points":\[\[\d+,1\.0\]\],"tags":\["status_code:200","src_library:libdatadog"\],"common":true,"type":"count"#).unwrap();
460
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
460
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
461
461
let server = MockServer::start();
462
462
letmut telemetry_srv = server.mock(|when, then| {
463
463
when.method(POST).body_matches(payload);
@@ -488,7 +488,7 @@ mod tests {
488
488
#[test]
489
489
fnerrors_timeout_test(){
490
490
let payload = Regex::new(r#""metric":"trace_api.errors","points":\[\[\d+,1\.0\]\],"tags":\["src_library:libdatadog","type:timeout"\],"common":true,"type":"count"#).unwrap();
491
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
491
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
492
492
let server = MockServer::start();
493
493
letmut telemetry_srv = server.mock(|when, then| {
494
494
when.method(POST).body_matches(payload);
@@ -519,7 +519,7 @@ mod tests {
519
519
#[test]
520
520
fnerrors_network_test(){
521
521
let payload = Regex::new(r#""metric":"trace_api.errors","points":\[\[\d+,1\.0\]\],"tags":\["src_library:libdatadog","type:network"\],"common":true,"type":"count"#).unwrap();
522
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
522
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
523
523
let server = MockServer::start();
524
524
letmut telemetry_srv = server.mock(|when, then| {
525
525
when.method(POST).body_matches(payload);
@@ -550,7 +550,7 @@ mod tests {
550
550
#[test]
551
551
fnerrors_status_code_test(){
552
552
let payload = Regex::new(r#""metric":"trace_api.errors","points":\[\[\d+,1\.0\]\],"tags":\["src_library:libdatadog","type:status_code"\],"common":true,"type":"count"#).unwrap();
553
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
553
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
554
554
let server = MockServer::start();
555
555
letmut telemetry_srv = server.mock(|when, then| {
556
556
when.method(POST).body_matches(payload);
@@ -581,7 +581,7 @@ mod tests {
581
581
#[test]
582
582
fnchunks_sent_test(){
583
583
let payload = Regex::new(r#""metric":"trace_chunks_sent","points":\[\[\d+,1\.0\]\],"tags":\["src_library:libdatadog"\],"common":true,"type":"count"#).unwrap();
584
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
584
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
585
585
let server = MockServer::start();
586
586
letmut telemetry_srv = server.mock(|when, then| {
587
587
when.method(POST).body_matches(payload);
@@ -612,7 +612,7 @@ mod tests {
612
612
#[test]
613
613
fnchunks_dropped_send_failure_test(){
614
614
let payload = Regex::new(r#""metric":"trace_chunks_dropped","points":\[\[\d+,1\.0\]\],"tags":\["src_library:libdatadog","reason:send_failure"\],"common":true,"type":"count"#).unwrap();
615
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
615
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
616
616
let server = MockServer::start();
617
617
letmut telemetry_srv = server.mock(|when, then| {
618
618
when.method(POST).body_matches(payload);
@@ -644,7 +644,7 @@ mod tests {
644
644
fnsend_client_side_stats_drops_test(){
645
645
let payload_p0 = Regex::new(r#""metric":"trace_chunks_dropped","points":\[\[\d+,3\.0\]\],"tags":\["src_library:libdatadog","reason:p0_drop"\],"common":true,"type":"count"#).unwrap();
646
646
let payload_trace_filter = Regex::new(r#""metric":"trace_chunks_dropped","points":\[\[\d+,5\.0\]\],"tags":\["src_library:libdatadog","reason:trace_filters"\],"common":true,"type":"count"#).unwrap();
647
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
647
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
648
648
let server = MockServer::start();
649
649
letmut telemetry_srv = server.mock(|when, then| {
650
650
when.method(POST)
@@ -673,7 +673,7 @@ mod tests {
673
673
#[test]
674
674
fnchunks_dropped_serialization_error_test(){
675
675
let payload = Regex::new(r#""metric":"trace_chunks_dropped","points":\[\[\d+,1\.0\]\],"tags":\["src_library:libdatadog","reason:serialization_error"\],"common":true,"type":"count"#).unwrap();
676
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
676
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
677
677
let server = MockServer::start();
678
678
letmut telemetry_srv = server.mock(|when, then| {
679
679
when.method(POST).body_matches(payload);
@@ -822,7 +822,7 @@ mod tests {
822
822
#[cfg_attr(miri, ignore)]
823
823
#[test]
824
824
fnruntime_id_test(){
825
-
let shared_runtime = SharedRuntime::new().expect("Failed to create runtime");
825
+
let shared_runtime = ForkSafeRuntime::new().expect("Failed to create runtime");
0 commit comments