@@ -20,6 +20,9 @@ static VALUE _native_from_span(VALUE klass, VALUE span);
2020/* TraceExporter methods */
2121static VALUE _native_exporter_new (int argc , VALUE * argv , VALUE klass );
2222static VALUE _native_send_traces (VALUE self , VALUE traces );
23+ static VALUE _native_before_fork (VALUE self );
24+ static VALUE _native_after_fork_in_parent (VALUE self );
25+ static VALUE _native_after_fork_in_child (VALUE self );
2326
2427/* Response helpers */
2528static VALUE create_ok_response (long trace_count , VALUE payload );
@@ -473,6 +476,46 @@ static VALUE _native_exporter_new(
473476 exporter );
474477}
475478
479+ /* ========================================================================
480+ * Fork safety hooks
481+ *
482+ * These coordinate the tokio runtime lifecycle around process forks
483+ * (Puma, Unicorn, Passenger).
484+ * ======================================================================== */
485+
486+ static VALUE _native_before_fork (VALUE self ) {
487+ ddog_TraceExporter * exporter ;
488+ TypedData_Get_Struct (self , ddog_TraceExporter , & trace_exporter_typed_data , exporter );
489+ if (exporter == NULL ) {
490+ raise_error (rb_eRuntimeError , "TraceExporter has not been initialized or was already freed" );
491+ }
492+ ddog_TraceExporterError * err = ddog_trace_exporter_before_fork (exporter );
493+ check_exporter_error ("Failed to prepare for fork" , err );
494+ return Qnil ;
495+ }
496+
497+ static VALUE _native_after_fork_in_parent (VALUE self ) {
498+ ddog_TraceExporter * exporter ;
499+ TypedData_Get_Struct (self , ddog_TraceExporter , & trace_exporter_typed_data , exporter );
500+ if (exporter == NULL ) {
501+ raise_error (rb_eRuntimeError , "TraceExporter has not been initialized or was already freed" );
502+ }
503+ ddog_TraceExporterError * err = ddog_trace_exporter_after_fork_in_parent (exporter );
504+ check_exporter_error ("Failed to restore after fork in parent" , err );
505+ return Qnil ;
506+ }
507+
508+ static VALUE _native_after_fork_in_child (VALUE self ) {
509+ ddog_TraceExporter * exporter ;
510+ TypedData_Get_Struct (self , ddog_TraceExporter , & trace_exporter_typed_data , exporter );
511+ if (exporter == NULL ) {
512+ raise_error (rb_eRuntimeError , "TraceExporter has not been initialized or was already freed" );
513+ }
514+ ddog_TraceExporterError * err = ddog_trace_exporter_after_fork_in_child (exporter );
515+ check_exporter_error ("Failed to restore after fork in child" , err );
516+ return Qnil ;
517+ }
518+
476519/* ========================================================================
477520 * GVL-release helper for ddog_trace_exporter_send_trace_chunks
478521 *
@@ -736,6 +779,14 @@ void trace_exporter_init(VALUE tracing_module) {
736779 rb_define_method (trace_exporter_class , "_native_send_traces" ,
737780 _native_send_traces , 1 );
738781
782+ /* Instance: fork safety hooks */
783+ rb_define_method (trace_exporter_class , "_native_before_fork" ,
784+ _native_before_fork , 0 );
785+ rb_define_method (trace_exporter_class , "_native_after_fork_in_parent" ,
786+ _native_after_fork_in_parent , 0 );
787+ rb_define_method (trace_exporter_class , "_native_after_fork_in_child" ,
788+ _native_after_fork_in_child , 0 );
789+
739790 /* ----------------------------------------------------------------
740791 * Response class (defined in Ruby, loaded lazily)
741792 *
0 commit comments