1
1
#include <ruby.h>
2
+ #include <ruby/thread.h>
2
3
#include "stack_recorder.h"
3
4
#include "libddprof_helpers.h"
4
5
@@ -12,10 +13,17 @@ static ID ruby_time_from_id; // id of :ruby_time_from in Ruby
12
13
13
14
static VALUE stack_recorder_class = Qnil ;
14
15
16
+ struct call_serialize_without_gvl_arguments {
17
+ ddprof_ffi_Profile * profile ;
18
+ ddprof_ffi_SerializeResult result ;
19
+ bool serialize_ran ;
20
+ };
21
+
15
22
static VALUE _native_new (VALUE klass );
16
23
static void stack_recorder_typed_data_free (void * data );
17
24
static VALUE _native_serialize (VALUE self , VALUE recorder_instance );
18
25
static VALUE ruby_time_from (ddprof_ffi_Timespec ddprof_time );
26
+ static void * call_serialize_without_gvl (void * call_args );
19
27
20
28
void stack_recorder_init (VALUE profiling_module ) {
21
29
stack_recorder_class = rb_define_class_under (profiling_module , "StackRecorder" , rb_cObject );
@@ -65,7 +73,22 @@ static VALUE _native_serialize(VALUE self, VALUE recorder_instance) {
65
73
ddprof_ffi_Profile * profile ;
66
74
TypedData_Get_Struct (recorder_instance , ddprof_ffi_Profile , & stack_recorder_typed_data , profile );
67
75
68
- ddprof_ffi_SerializeResult serialized_profile = ddprof_ffi_Profile_serialize (profile );
76
+ // We'll release the Global VM Lock while we're calling serialize, so that the Ruby VM can continue to work while this
77
+ // is pending
78
+ struct call_serialize_without_gvl_arguments args = {.profile = profile , .serialize_ran = false};
79
+
80
+ // We use rb_thread_call_without_gvl2 for similar reasons as in http_transport.c: we don't want pending interrupts
81
+ // that cause exceptions to be raised to be processed as otherwise we can leak the serialized profile.
82
+ rb_thread_call_without_gvl2 (call_serialize_without_gvl , & args , /* No interruption supported */ NULL , NULL );
83
+
84
+ // This weird corner case can happen if rb_thread_call_without_gvl2 returns immediately due to an interrupt
85
+ // without ever calling call_serialize_without_gvl. In this situation, we don't have anything to clean up, we can
86
+ // just return.
87
+ if (!args .serialize_ran ) {
88
+ return rb_ary_new_from_args (2 , error_symbol , rb_str_new_cstr ("Interrupted before call_serialize_without_gvl ran" ));
89
+ }
90
+
91
+ ddprof_ffi_SerializeResult serialized_profile = args .result ;
69
92
70
93
if (serialized_profile .tag == DDPROF_FFI_SERIALIZE_RESULT_ERR ) {
71
94
VALUE err_details = ruby_string_from_vec_u8 (serialized_profile .err );
@@ -105,3 +128,12 @@ void record_sample(VALUE recorder_instance, ddprof_ffi_Sample sample) {
105
128
106
129
ddprof_ffi_Profile_add (profile , sample );
107
130
}
131
+
132
+ static void * call_serialize_without_gvl (void * call_args ) {
133
+ struct call_serialize_without_gvl_arguments * args = (struct call_serialize_without_gvl_arguments * ) call_args ;
134
+
135
+ args -> result = ddprof_ffi_Profile_serialize (args -> profile );
136
+ args -> serialize_ran = true;
137
+
138
+ return NULL ; // Unused
139
+ }
0 commit comments