Skip to content

Commit 1d2dc3d

Browse files
Add logging + rb_protect fix: show stale state pointer when exception bypasses cleanup
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6ab97cf commit 1d2dc3d

1 file changed

Lines changed: 26 additions & 2 deletions

File tree

scheduler.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ rb_fiber_scheduler_blocking_operation_execute(rb_fiber_scheduler_blocking_operat
211211
}
212212

213213
// Now we're executing - call the function
214+
fprintf(stderr, "[blocking_operation] execute: op=%p state=%p\n", (void*)blocking_operation, (void*)blocking_operation->state);
214215
blocking_operation->state->result = blocking_operation->function(blocking_operation->data);
215216
blocking_operation->state->saved_errno = errno;
216217

@@ -1088,6 +1089,13 @@ rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname)
10881089
* Thread.new { blocking_operation.call }.join
10891090
* end
10901091
*/
1092+
static VALUE
1093+
scheduler_blocking_operation_wait_call(VALUE _args)
1094+
{
1095+
VALUE *args = (VALUE *)_args;
1096+
return rb_funcall(args[0], id_blocking_operation_wait, 1, args[1]);
1097+
}
1098+
10911099
VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void* (*function)(void *), void *data, rb_unblock_function_t *unblock_function, void *data2, int flags, struct rb_fiber_scheduler_blocking_operation_state *state)
10921100
{
10931101
// Check if scheduler supports blocking_operation_wait before creating the object
@@ -1098,10 +1106,22 @@ VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void* (*functi
10981106
// Create a new BlockingOperation with the blocking operation
10991107
VALUE blocking_operation = rb_fiber_scheduler_blocking_operation_new(function, data, unblock_function, data2, flags, state);
11001108

1101-
VALUE result = rb_funcall(scheduler, id_blocking_operation_wait, 1, blocking_operation);
1109+
rb_fiber_scheduler_blocking_operation_t *operation = get_blocking_operation(blocking_operation);
1110+
fprintf(stderr, "[blocking_operation] wait: op=%p state=%p\n", (void*)operation, (void*)state);
1111+
1112+
int tag = 0;
1113+
VALUE call_args[2] = {scheduler, blocking_operation};
1114+
VALUE result = rb_protect(scheduler_blocking_operation_wait_call, (VALUE)call_args, &tag);
1115+
1116+
if (tag) {
1117+
// Exception from blocking_operation_wait: cleanup still runs below,
1118+
// but log this so we can detect when a stale state pointer is later accessed.
1119+
fprintf(stderr, "[blocking_operation] wait exception: op=%p state=%p (now dangling!)\n", (void*)operation, (void*)state);
1120+
}
11021121

1122+
// Get fresh operation pointer after rb_protect (GC may have run)
1123+
operation = get_blocking_operation(blocking_operation);
11031124
// Get the operation data to check if it was executed
1104-
rb_fiber_scheduler_blocking_operation_t *operation = get_blocking_operation(blocking_operation);
11051125
rb_atomic_t current_status = RUBY_ATOMIC_LOAD(operation->status);
11061126

11071127
// Invalidate the operation now that we're done with it
@@ -1114,6 +1134,9 @@ VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void* (*functi
11141134
// Ensure that the blocking operation remains visible until this point:
11151135
RB_GC_GUARD(blocking_operation);
11161136

1137+
// Re-raise any exception from the scheduler after cleanup.
1138+
if (tag) rb_jump_tag(tag);
1139+
11171140
// If the blocking operation was never executed, return Qundef to signal the caller to use rb_nogvl instead
11181141
if (current_status == RB_FIBER_SCHEDULER_BLOCKING_OPERATION_STATUS_QUEUED) {
11191142
return Qundef;
@@ -1212,6 +1235,7 @@ rb_fiber_scheduler_blocking_operation_cancel(rb_fiber_scheduler_blocking_operati
12121235

12131236
case RB_FIBER_SCHEDULER_BLOCKING_OPERATION_STATUS_EXECUTING:
12141237
// Work is running - mark cancelled AND call unblock function
1238+
fprintf(stderr, "[blocking_operation] cancel(executing): op=%p state=%p\n", (void*)blocking_operation, (void*)blocking_operation->state);
12151239
if (RUBY_ATOMIC_CAS(blocking_operation->status, current_state, RB_FIBER_SCHEDULER_BLOCKING_OPERATION_STATUS_CANCELLED) != current_state) {
12161240
// State changed between load and CAS - operation may have completed:
12171241
return 0;

0 commit comments

Comments
 (0)