Skip to content

Commit fd7bbe6

Browse files
committed
Implement current_fiber_for()
This will be used to access the OTEL context after open-telemetry/opentelemetry-ruby#1807 .
1 parent 29a6bd8 commit fd7bbe6

4 files changed

Lines changed: 59 additions & 0 deletions

File tree

ext/datadog_profiling_native_extension/extconf.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ def skip_building_extension!(reason)
164164
# On older Rubies, "pop" was not a primitive operation
165165
$defs << "-DNO_PRIMITIVE_POP" if RUBY_VERSION < "3.2"
166166

167+
# We could support this for older Rubies, but since this only gets used by the OTEL context extraction, and that
168+
# use-case is only for 3.1+, we didn't bother supporting it farther back yet.
169+
$defs << "-DNO_CURRENT_FIBER_FOR" if RUBY_VERSION < "3.1"
170+
167171
# On older Rubies, there was no tid member in the internal thread structure
168172
$defs << "-DNO_THREAD_TID" if RUBY_VERSION < "3.1"
169173

ext/datadog_profiling_native_extension/private_vm_api_access.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,3 +803,50 @@ static inline int ddtrace_imemo_type(VALUE imemo) {
803803

804804
// Is the VM smack in the middle of raising an exception?
805805
bool is_raised_flag_set(VALUE thread) { return thread_struct_from_object(thread)->ec->raised_flag > 0; }
806+
807+
#ifndef NO_CURRENT_FIBER_FOR
808+
// The following three declarations are all
809+
// taken from upstream cont.c at commit d97884a58be32e829fd03a80cd521f4733d65c79 (February 2025, master branch)
810+
// (See the Ruby project copyright and license above)
811+
// to enable building `current_fiber_for`.
812+
//
813+
// We needed to copy them because they aren't otherwise exposed in any VM APIs or headers.
814+
// @ivoanjo: I manually checked the Ruby 3.1, 3.2, 3.3 and 3.4 branches + master, and the parts we care about in these
815+
// structures have not changed in many years (in fact, last change I spotted was for 2.7).
816+
enum context_type {
817+
CONTINUATION_CONTEXT = 0,
818+
FIBER_CONTEXT = 1
819+
};
820+
821+
typedef struct rb_context_struct { // This declaration is incomplete -- only contains up to `self` which is the part we care about
822+
enum context_type type;
823+
int argc;
824+
int kw_splat;
825+
VALUE self;
826+
} rb_context_t;
827+
828+
struct rb_fiber_struct { // This declaration is incomplete -- only contains the first entry which is the part we care about
829+
rb_context_t cont;
830+
};
831+
832+
VALUE current_fiber_for(VALUE thread) {
833+
VALUE self = thread_struct_from_object(thread)->ec->fiber_ptr->cont.self;
834+
return self == 0 ? Qnil : self;
835+
}
836+
837+
void self_test_current_fiber_for(void) {
838+
VALUE expected_current_fiber = current_fiber_for(rb_thread_current());
839+
VALUE actual_current_fiber = rb_fiber_current();
840+
841+
if (expected_current_fiber == Qnil) {
842+
// On purpose above we tried reading before calling `rb_fiber_current()` so the fiber may have not existed yet.
843+
// But now it should be there.
844+
expected_current_fiber = current_fiber_for(rb_thread_current());
845+
}
846+
847+
if (expected_current_fiber != actual_current_fiber) rb_raise(rb_eRuntimeError, "current_fiber_for() self-test failed");
848+
}
849+
#else
850+
VALUE current_fiber_for(VALUE thread) { rb_raise(rb_eRuntimeError, "Not implemented for Ruby < 3.1"); }
851+
void self_test_current_fiber_for(void) { } // Nothing to do
852+
#endif

ext/datadog_profiling_native_extension/private_vm_api_access.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ bool is_thread_alive(VALUE thread);
4444
VALUE thread_name_for(VALUE thread);
4545

4646
int ddtrace_rb_profile_frames(VALUE thread, int start, int limit, frame_info *stack_buffer);
47+
4748
// Returns true if the current thread belongs to the main Ractor or if Ruby has no Ractor support
4849
bool ddtrace_rb_ractor_main_p(void);
4950

@@ -70,3 +71,9 @@ const char *imemo_kind(VALUE imemo);
7071
{ if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, RTYPEDDATA_TYPE(rb_thread_current())))) raise_unexpected_type(value, ADD_QUOTES(value), "Thread", __FILE__, __LINE__, __func__); }
7172

7273
bool is_raised_flag_set(VALUE thread);
74+
75+
// Can be nil if `rb_fiber_current()` or similar has not been called (gets allocated lazily)
76+
// Only implemented for Ruby 3.1+
77+
VALUE current_fiber_for(VALUE thread);
78+
79+
void self_test_current_fiber_for(void);

ext/datadog_profiling_native_extension/profiling.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
8686

8787
static VALUE native_working_p(DDTRACE_UNUSED VALUE _self) {
8888
self_test_clock_id();
89+
self_test_current_fiber_for();
8990
self_test_mn_enabled();
9091

9192
return Qtrue;

0 commit comments

Comments
 (0)