From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS4713 221.184.0.0/13 X-Spam-Status: No, score=-3.0 required=3.0 tests=BAYES_00,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,SPF_PASS, T_DKIM_INVALID shortcircuit=no autolearn=ham autolearn_force=no version=3.4.0 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id 92D2F1FADE for ; Tue, 12 Sep 2017 13:44:28 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 043B81208D8; Tue, 12 Sep 2017 22:44:26 +0900 (JST) Received: from o1678916x28.outbound-mail.sendgrid.net (o1678916x28.outbound-mail.sendgrid.net [167.89.16.28]) by neon.ruby-lang.org (Postfix) with ESMTPS id DA5C21208D3 for ; Tue, 12 Sep 2017 22:44:22 +0900 (JST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=sendgrid.me; h=from:to:references:subject:mime-version:content-type:content-transfer-encoding:list-id; s=smtpapi; bh=6XpdVoE0SvmPKpVJn7C+JUp4Rlc=; b=GMWwh8n30adDIytz/t 2IFrwrDKBtYQTCjZ+k4Bs4ohTx3O/Zw9F+mUpnesOWIJNspatYigE93dnoXzpkhh 4kiVqyZ83NVgTJT/yDrlNiKrP1ZOYeFmDRyG/qTzLRtIN0JQ4hbRWQ71NMGF0Ar6 AQPtoYWlO2Jr8hBDKyBt5v8qU= Received: by filter0014p3mdw1.sendgrid.net with SMTP id filter0014p3mdw1-32641-59B7E4B3-2B 2017-09-12 13:44:19.63336464 +0000 UTC Received: from herokuapp.com (ec2-54-234-68-254.compute-1.amazonaws.com [54.234.68.254]) by ismtpd0006p1iad1.sendgrid.net (SG) with ESMTP id HiEbFH-IR3Wrz2CBLmCX_Q Tue, 12 Sep 2017 13:44:19.513 +0000 (UTC) Date: Tue, 12 Sep 2017 13:44:20 +0000 (UTC) From: git@chuckremes.com To: ruby-core@ruby-lang.org Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 57843 X-Redmine-Project: ruby-trunk X-Redmine-Issue-Id: 13821 X-Redmine-Issue-Author: cremes X-Redmine-Sender: cremes 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: ync6xU2WACa70kv/Ymy4QrNMhiuLXJG8OTL2vJD1yS7NG/0ct+6kXuhQ0ygCk4GoihtnA1RK5ue9N5 72hSFhBx3bog1ThKMcUHbMk4Af4EhojZe9IsaBKXjI3A1/tjkxkNL2A1oNLHxuG+r09+EzqG5TzctN hwDkor0dmCPxOgYC3MnQWRgGEkrbxp07rtmUa7kVHrCDoEanWqnJPSmwpA== X-ML-Name: ruby-core X-Mail-Count: 82761 Subject: [ruby-core:82761] [Ruby trunk Feature#13821] Allow fibers to be resumed across threads 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 #13821 has been updated by cremes (Chuck Remes). I understand how this request could allow for race conditions between Fibers. Right now we are relying on the fact that they can only run on a single thread to enforce this particular semantic. I also agree that this is useful in certain situations. But it is still quite useful to allow for Fibers to migrate between threads. Perhaps we could allow for both possibilities with a minor change to the Fiber API. ``` class Fiber def initialize(migrate: false) ... end def [](index) ... end def []=(index, value) ... end end ``` By default we would retain the existing behavior where the Fiber is "locked" to its originating Thread. But if you call `Fiber.new(migrate: true)` then the Fiber is free to float among multiple threads. When doing so, the programmer is *explicitly* agreeing to no longer rely upon the original semantics. If they yield a fiber inside of a synchronized section then they understand it will likely break if resumed on another thread. Likewise, they do not rely upon Thread#[] and related methods to set/get fiber locals. That Thread API for fiber locals is broken anyway... the #[] and #[]= methods on Thread should set/get thread locals as they did originally. There should be Fiber#[] and Fiber#[]= methods on the Fiber class. Conflating the two separate concepts all into the Thread class is no good. With Ruby 3 on the way this is the perfect time to fix problems like that. I'll open a separate ticket to suggest that as an improvement to the Thread and Fiber classes. ---------------------------------------- Feature #13821: Allow fibers to be resumed across threads https://bugs.ruby-lang.org/issues/13821#change-66617 * Author: cremes (Chuck Remes) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- Given a Fiber created in ThreadA, Ruby 2.4.1 (and earlier releases) raise a FiberError if the fiber is resumed in ThreadB or any other thread other than the one that created the original Fiber. Sample code attached to demonstrate problem. If Fibers are truly encapsulating all of the data for the continuation, we should be allowed to move them between Threads and resume their operation. Why? One use-case is to support the async-await asynchronous programming model. In that model, a method marked async runs *synchronously* until the #await method is encountered. At that point the method is suspended and control is returned to the caller. When the #await method completes (asynchronously) then it may resume the suspended method and continue. The only way to capture this program state, suspend and resume, is via a Fiber. example: ``` class Wait include AsyncAwait def dofirst async do puts 'Synchronously print dofirst.' result = await { dosecond } puts 'dosecond is complete' result end end def dosecond async do puts 'Synchronously print dosecond from async task.' slept = await { sleep 3 } puts 'Sleep complete' slept end end def run task = dofirst puts 'Received task' p AsyncAwait::Task.await(task) end end Wait.new.run ``` ``` # Expected output: # Synchronous print dofirst. # Received task # Synchronously print dosecond from async task. # Sleep complete # dosecond is complete # 3 ``` Right now the best way to accomplish suspension of the #dofirst and #dosecond commands and allow them to run asynchronously is by passing those blocks to *another thread* (other than the callers thread) so they can be encapsulated in a new Fiber and then yielded. When it's time to resume after #await completes, that other thread must lookup the fiber and resume it. This is lots of extra code and logic to make sure that fibers are only resumed on the threads that created them. Allowing Fibers to migrate between threads would eliminate this problem. ---Files-------------------------------- fiber_across_threads.rb (377 Bytes) wait.rb (728 Bytes) -- https://bugs.ruby-lang.org/