@@ -28,6 +28,7 @@ extern int madvise(caddr_t, size_t, int);
2828#include "eval_intern.h"
2929#include "internal.h"
3030#include "internal/cont.h"
31+ #include "internal/thread.h"
3132#include "internal/error.h"
3233#include "internal/gc.h"
3334#include "internal/proc.h"
@@ -229,18 +230,18 @@ typedef struct rb_context_struct {
229230 struct rb_jit_cont * jit_cont ; // Continuation contexts for JITs
230231} rb_context_t ;
231232
232-
233233/*
234234 * Fiber status:
235- * [Fiber.new] ------> FIBER_CREATED
236- * | [Fiber#resume]
237- * v
238- * +--> FIBER_RESUMED ----+
239- * [Fiber#resume] | | [Fiber.yield] |
240- * | v |
241- * +-- FIBER_SUSPENDED | [Terminate]
242- * |
243- * FIBER_TERMINATED <-+
235+ * [Fiber.new] ------> FIBER_CREATED ----> [Fiber#kill] --> |
236+ * | [Fiber#resume] |
237+ * v |
238+ * +--> FIBER_RESUMED ----> [return] ------> |
239+ * [Fiber#resume] | | [Fiber.yield/transfer] |
240+ * [Fiber#transfer] | v |
241+ * +--- FIBER_SUSPENDED --> [Fiber#kill] --> |
242+ * |
243+ * |
244+ * FIBER_TERMINATED <-------------------+
244245 */
245246enum fiber_status {
246247 FIBER_CREATED ,
@@ -266,6 +267,8 @@ struct rb_fiber_struct {
266267 unsigned int yielding : 1 ;
267268 unsigned int blocking : 1 ;
268269
270+ unsigned int killed : 1 ;
271+
269272 struct coroutine_context context ;
270273 struct fiber_pool_stack stack ;
271274};
@@ -1996,6 +1999,7 @@ fiber_t_alloc(VALUE fiber_value, unsigned int blocking)
19961999 fiber -> cont .self = fiber_value ;
19972000 fiber -> cont .type = FIBER_CONTEXT ;
19982001 fiber -> blocking = blocking ;
2002+ fiber -> killed = 0 ;
19992003 cont_init (& fiber -> cont , th );
20002004
20012005 fiber -> cont .saved_ec .fiber_ptr = fiber ;
@@ -2522,13 +2526,16 @@ rb_fiber_start(rb_fiber_t *fiber)
25222526 if (state == TAG_RAISE ) {
25232527 // noop...
25242528 }
2529+ else if (state == TAG_FATAL && err == RUBY_FATAL_FIBER_KILLED ) {
2530+ need_interrupt = FALSE;
2531+ err = Qfalse ;
2532+ }
25252533 else if (state == TAG_FATAL ) {
25262534 rb_threadptr_pending_interrupt_enque (th , err );
25272535 }
25282536 else {
25292537 err = rb_vm_make_jump_tag_but_local_jump (state , err );
25302538 }
2531- need_interrupt = TRUE;
25322539 }
25332540
25342541 rb_fiber_terminate (fiber , need_interrupt , err );
@@ -2547,6 +2554,7 @@ rb_threadptr_root_fiber_setup(rb_thread_t *th)
25472554 fiber -> cont .saved_ec .fiber_ptr = fiber ;
25482555 fiber -> cont .saved_ec .thread_ptr = th ;
25492556 fiber -> blocking = 1 ;
2557+ fiber -> killed = 0 ;
25502558 fiber_status_set (fiber , FIBER_RESUMED ); /* skip CREATED */
25512559 th -> ec = & fiber -> cont .saved_ec ;
25522560 // When rb_threadptr_root_fiber_setup is called for the first time, rb_rjit_enabled and
@@ -2649,6 +2657,19 @@ fiber_store(rb_fiber_t *next_fiber, rb_thread_t *th)
26492657 fiber_setcontext (next_fiber , fiber );
26502658}
26512659
2660+ static void
2661+ fiber_check_killed (rb_fiber_t * fiber )
2662+ {
2663+ VM_ASSERT (fiber == fiber_current ());
2664+
2665+ if (fiber -> killed ) {
2666+ rb_thread_t * thread = fiber -> cont .saved_ec .thread_ptr ;
2667+
2668+ thread -> ec -> errinfo = RUBY_FATAL_FIBER_KILLED ;
2669+ EC_JUMP_TAG (thread -> ec , RUBY_TAG_FATAL );
2670+ }
2671+ }
2672+
26522673static inline VALUE
26532674fiber_switch (rb_fiber_t * fiber , int argc , const VALUE * argv , int kw_splat , rb_fiber_t * resuming_fiber , bool yielding )
26542675{
@@ -2737,7 +2758,14 @@ fiber_switch(rb_fiber_t *fiber, int argc, const VALUE *argv, int kw_splat, rb_fi
27372758
27382759 current_fiber = th -> ec -> fiber_ptr ;
27392760 value = current_fiber -> cont .value ;
2740- if (current_fiber -> cont .argc == -1 ) rb_exc_raise (value );
2761+
2762+ fiber_check_killed (current_fiber );
2763+
2764+ if (current_fiber -> cont .argc == -1 ) {
2765+ // Fiber#raise will trigger this path.
2766+ rb_exc_raise (value );
2767+ }
2768+
27412769 return value ;
27422770}
27432771
@@ -3175,14 +3203,9 @@ rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
31753203}
31763204
31773205static VALUE
3178- fiber_raise (rb_fiber_t * fiber , int argc , const VALUE * argv )
3206+ fiber_raise (rb_fiber_t * fiber , VALUE exception )
31793207{
3180- VALUE exception = rb_make_exception (argc , argv );
3181-
3182- if (fiber -> resuming_fiber ) {
3183- rb_raise (rb_eFiberError , "attempt to raise a resuming fiber" );
3184- }
3185- else if (FIBER_SUSPENDED_P (fiber ) && !fiber -> yielding ) {
3208+ if (FIBER_SUSPENDED_P (fiber ) && !fiber -> yielding ) {
31863209 return fiber_transfer_kw (fiber , -1 , & exception , RB_NO_KEYWORDS );
31873210 }
31883211 else {
@@ -3193,7 +3216,9 @@ fiber_raise(rb_fiber_t *fiber, int argc, const VALUE *argv)
31933216VALUE
31943217rb_fiber_raise (VALUE fiber , int argc , const VALUE * argv )
31953218{
3196- return fiber_raise (fiber_ptr (fiber ), argc , argv );
3219+ VALUE exception = rb_make_exception (argc , argv );
3220+
3221+ return fiber_raise (fiber_ptr (fiber ), exception );
31973222}
31983223
31993224/*
@@ -3223,6 +3248,39 @@ rb_fiber_m_raise(int argc, VALUE *argv, VALUE self)
32233248 return rb_fiber_raise (self , argc , argv );
32243249}
32253250
3251+ /*
3252+ * call-seq:
3253+ * fiber.kill -> nil
3254+ *
3255+ * Terminates +fiber+ by raising an uncatchable exception, returning
3256+ * the terminated Fiber.
3257+ *
3258+ * If the fiber has not been started, transition directly to the terminated state.
3259+ *
3260+ * If the fiber is already terminated, does nothing.
3261+ */
3262+ static VALUE
3263+ rb_fiber_m_kill (VALUE self )
3264+ {
3265+ rb_fiber_t * fiber = fiber_ptr (self );
3266+
3267+ if (fiber -> killed ) return Qfalse ;
3268+ fiber -> killed = 1 ;
3269+
3270+ if (fiber -> status == FIBER_CREATED ) {
3271+ fiber -> status = FIBER_TERMINATED ;
3272+ }
3273+ else if (fiber -> status != FIBER_TERMINATED ) {
3274+ if (fiber_current () == fiber ) {
3275+ fiber_check_killed (fiber );
3276+ } else {
3277+ fiber_raise (fiber_ptr (self ), Qnil );
3278+ }
3279+ }
3280+
3281+ return self ;
3282+ }
3283+
32263284/*
32273285 * call-seq:
32283286 * Fiber.current -> fiber
@@ -3398,6 +3456,7 @@ Init_Cont(void)
33983456 rb_define_method (rb_cFiber , "storage=" , rb_fiber_storage_set , 1 );
33993457 rb_define_method (rb_cFiber , "resume" , rb_fiber_m_resume , -1 );
34003458 rb_define_method (rb_cFiber , "raise" , rb_fiber_m_raise , -1 );
3459+ rb_define_method (rb_cFiber , "kill" , rb_fiber_m_kill , 0 );
34013460 rb_define_method (rb_cFiber , "backtrace" , rb_fiber_backtrace , -1 );
34023461 rb_define_method (rb_cFiber , "backtrace_locations" , rb_fiber_backtrace_locations , -1 );
34033462 rb_define_method (rb_cFiber , "to_s" , fiber_to_s , 0 );
0 commit comments