Project

General

Profile

Bug #20414

Updated by ioquatix (Samuel Williams) 9 months ago

The following program will fail with `FiberError`, and is difficult to properly clean up: 

 ```ruby 
 root_fiber = Fiber.current 

 f1 = Fiber.new do 
   
	 root_fiber.transfer 
 end 

 f2 = Fiber.new do 
   
	 f1.resume 
 end 

 f2.transfer 

 f2.raise("error") # => `raise': attempt to transfer to a resuming fiber (FiberError) 
 ``` 

 This program deliberately set's up a scenario where `f2` is resuming `f1`. Trying to raise an exception on `f2` is impossible, because the only way control flow can return to it, is when `f1` yields or exits. 

 We can avoid this problem, by raising the exception on f1, and we can do this automatically using the following logic: 

 ```c 
 static VALUE 
 fiber_raise(rb_fiber_t *fiber, VALUE exception) 
 { 
     // Add this recursive step: 
     if (fiber->resuming_fiber) { 
         return fiber_raise(fiber->resuming_fiber, exception); 
     } 

     // Existing code ... 
     else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) { 
         return fiber_transfer_kw(fiber, -1, &exception, RB_NO_KEYWORDS); 
     } 
     else { 
         return fiber_resume_kw(fiber, -1, &exception, RB_NO_KEYWORDS); 
     } 
 } 
 ``` 

 This makes `Fiber#raise` much more robust and useful for the purpose of stopping fibers, without knowing exactly what they are doing.

Back