From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-2.8 required=3.0 tests=AWL,BAYES_00, DKIM_ADSP_CUSTOM_MED,FORGED_GMAIL_RCVD,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY shortcircuit=no autolearn=no autolearn_force=no version=3.4.2 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id 5826B1F4B4 for ; Thu, 8 Oct 2020 09:34:18 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id B63221209FB; Thu, 8 Oct 2020 18:33:33 +0900 (JST) Received: from xtrwkhkc.outbound-mail.sendgrid.net (xtrwkhkc.outbound-mail.sendgrid.net [167.89.16.28]) by neon.ruby-lang.org (Postfix) with ESMTPS id 301BA1209F8 for ; Thu, 8 Oct 2020 18:33:30 +0900 (JST) Received: by filterdrecv-p3las1-dcbfbb89c-z6sz6 with SMTP id filterdrecv-p3las1-dcbfbb89c-z6sz6-17-5F7EDD0B-C 2020-10-08 09:34:03.233558946 +0000 UTC m=+39490.005492863 Received: from herokuapp.com (unknown) by ismtpd0094p1iad2.sendgrid.net (SG) with ESMTP id WX9j7W0jRyuq3bRGkUhdSw for ; Thu, 08 Oct 2020 09:34:03.093 +0000 (UTC) Date: Thu, 08 Oct 2020 09:34:03 +0000 (UTC) From: eregontp@gmail.com Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 76208 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Bug X-Redmine-Issue-Id: 17221 X-Redmine-Issue-Author: ko1 X-Redmine-Sender: Eregon X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-SG-EID: =?us-ascii?Q?KippOI8ZHtTweq7XfQzW93937kJ4QNWwSBuHnaMEcr2n37Ps+=2F5LMSKmTwKmy9?= =?us-ascii?Q?h0NGx18nwirC0xXRoNmA1WsKlCTSNfP2mzhpTnS?= =?us-ascii?Q?cqc7oaXSIwL7YFhuZ6twNiBiwZ=2FP=2FDfg9=2FIQ7Ft?= =?us-ascii?Q?zvgQlMRd4xHClx14ssQO2V8ZE67vGinE0AdlPrd?= =?us-ascii?Q?5P65GiSYm=2F327kWqjo7wXMpL97Eo4LAQeHDt7t2?= =?us-ascii?Q?M=2FvRA2VSICfyFOGuM=3D?= To: ruby-core@ruby-lang.org X-Entity-ID: b/2+PoftWZ6GuOu3b0IycA== X-ML-Name: ruby-core X-Mail-Count: 100342 Subject: [ruby-core:100342] [Ruby master Bug#17221] Relax the Fiber#transfer's limitation X-BeenThere: ruby-core@ruby-lang.org X-Mailman-Version: 2.1.15 Precedence: list Reply-To: Ruby developers List-Id: Ruby developers List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #17221 has been updated by Eregon (Benoit Daloze). For (2), can we use the error message `attempt to transfer to a yielding fiber (FiberError)` (yielding instead of resuming)? That seems clearer since f2 is not resuming but waiting for a `resume` and inside `Fiber.yield` (= yielding). ---------------------------------------- Bug #17221: Relax the Fiber#transfer's limitation https://bugs.ruby-lang.org/issues/17221#change-87955 * Author: ko1 (Koichi Sasada) * Status: Open * Priority: Normal * Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN ---------------------------------------- Using `Fiber#transfer` with `Fiber#resume` for a same Fiber is limited (once `Fiber#transfer` is called for a fiber, the fiber can not be resumed more). ```ruby require 'fiber' f1 = nil f2 = Fiber.new{ f1.transfer } f1 = Fiber.new{ f2.transfer Fiber.yield 10 Fiber.yield 20 } p f1.resume #=> 10 p f1.resume #=> `resume': cannot resume transferred Fiber (FiberError) ``` This restriction was introduced to protect the resume/yield chain, but we realized that it is too much to protect the chain. Instead of the current restriction, we introduce some other protections. (1) can not transfer to the resuming fiber. ```ruby require 'fiber' root = Fiber.current f1 = f2 = nil f1 = Fiber.new{ f2 = Fiber.new{ root.transfer(10) } f2.resume } p f1.transfer #=> 10 # root <-----+ # | | # v | transfer # f1 -> f2 -+ # resume/yield chain # horizontal direction: resume # vertical direction: transfer p f1.transfer #=> attempt to transfer to a resuming fiber (FiberError) # f1 has it's own resume/yield chain, and f1.transfer breaks the chain # root <-----+ # || (error)| # vv | # f1 -> f2 -+ # resume/yield chain ``` (2) can not transfer to the yielding fiber. ```ruby require 'fiber' f1 = f2 = nil f1 = Fiber.new{ f2 = Fiber.new{ Fiber.yield } f2.resume 10 } p f1.transfer #=> 10 # root # | ^ # | | transfer # v | # f1 --> f2 # resume/yield chain # <-- p f2.transfer #=> attempt to transfer to a resuming fiber (FiberError) # f2 is waiting for the resume, so the transfer is not allowed. # root --+ # | ^ | transfer (error) # | | | # v | v # f1 --> f2 # resume/yield chain # <-- ``` (3) can not resume transferring fiber. ```ruby require 'fiber' f1 = f2 = nil f2 = Fiber.new{ f1.resume #=> attempt to resume the transferring fiber (FiberError) } f1 = Fiber.new{ f2.transfer } f1.transfer # root # | # v # f1 <-+ # | | # v | resume (error) # f2 --+ # f1 seems waiting for transfer from other fibers. ``` (4) can not yield from not-resumed fiber ``` require 'fiber' f2 = Fiber.new do Fiber.yield #=> `yield': attempt to yield on not resumed fiber (FiberError) end f1 = Fiber.new f2.transfer end p f1.transfer # root # | # v # f1 # | # v # <- f2 # yield to where ...? (2.7 switches to root fiber) ``` and remove current restriction. The first example works fine: ```ruby require 'fiber' f1 = nil f2 = Fiber.new{ f1.transfer } f1 = Fiber.new{ f2.transfer Fiber.yield 10 Fiber.yield 20 } p f1.resume #=> 10 p f1.resume #=> 20 # root -> f1 <-+ # | | # v | # f2 --+ ``` The basic idea is respect *programmer's intention*. For (1), resuming fiber should be switched by the `Fiber.yield`. For (2), yielding fiber should be switched by the `Fiber#resume`. For (3), transferring fiber should be switched by the `Fiber#transfer`. Mainly (1) can keep the resume/yield chain. Also (2) and (3) makes the chain and relationships with fibers cleanly. ---- Also at the end of a transferred fiber, it had continued on root fiber. However, if the root fiber resumed a fiber (and that fiber can resumed another fiber), this behavior also breaks the resume/yield chain. So at the end of a transferred fiber, switch to the edge of resume chain from root fiber. For example, root fiber resumed f1 and f1 resumed f2, transferred to f3 and f3 terminated, then continue from the fiber f2 (it was continued from root fiber without this patch). ```ruby require 'fiber' f3 = Fiber.new{ 10 } f2 = Fiber.new{ f3.transfer + 20 } f1 = Fiber.new{ f2.resume } p f1.resume #=> 30 # without this patch: # # root -> f1 -> f2 # ^ | # | exit v # +----------- f3 # with this patch: # # root -> f1 -> f2 <-+ # keep resume/yield chain # | | # v | # f3 --+ exit ``` The patch is: https://github.com/ruby/ruby/pull/3636 ---Files-------------------------------- clipboard-202010080257-u2lbv.png (25.5 KB) -- https://bugs.ruby-lang.org/