Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:101816 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 34774 invoked from network); 9 Feb 2018 11:48:22 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 9 Feb 2018 11:48:22 -0000 Authentication-Results: pb1.pair.com header.from=me@kelunik.com; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=me@kelunik.com; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain kelunik.com from 81.169.146.218 cause and error) X-PHP-List-Original-Sender: me@kelunik.com X-Host-Fingerprint: 81.169.146.218 mo4-p00-ob.smtp.rzone.de Received: from [81.169.146.218] ([81.169.146.218:22321] helo=mo4-p00-ob.smtp.rzone.de) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 87/F4-21403-28A8D7A5 for ; Fri, 09 Feb 2018 06:48:20 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1518176895; s=strato-dkim-0002; d=kelunik.com; h=Content-Type:Cc:To:Subject:Message-ID:Date:From:References: In-Reply-To:X-RZG-CLASS-ID:X-RZG-AUTH; bh=oSSHgCMaVB04/n+WoultsGexV66XC8vff1G+CRrL2Y0=; b=K3Xmw06VhHDH8FB/3nWHjiiZxaS7b/Z5NQVUGpF+b+Uoog94XnNEg+Qom0+UZ2S4Dy +ZeMsUXnI5UKhOA1t8+lFwiVPgMqngog3FgJYC11INEzockHKH31mnWy5Uf36KuWSbml 1Y/4GPRnHEuhvAgNNWNblAs8bURkcHZz/r93ouAQrjyAPp8s+SIY8vSvC2lYKjEKHUf3 3pfbagJo7er0DB6nkybhYwe5A373MXlYKJhG9mGkpKkRc50n/LWEEE6gHjz0tU2hDmu+ sWgu6X7BCDVL+dvczA8yi0XtnveX6FyKzxsfQCU5nWCphOt84OMmpSfzx4YcHdWn7fPd kRkA== X-RZG-AUTH: :IWkkfkWkbvHsXQGmRYmUo9mlsGbEv0XHBzMIJSS+jKTzde5mDb8Db2nURiuycA== X-RZG-CLASS-ID: mo00 Received: by mail-yw0-f175.google.com with SMTP id j128so4884442ywg.7 for ; Fri, 09 Feb 2018 03:48:15 -0800 (PST) X-Gm-Message-State: APf1xPBFAnN1MmjyUMhYdOemukDq3xSjkSzU4v7QMp0N/CaI+qwg64IJ X5dzylXfq1ytT5aXMEtXGwrZAQYgr6eFVNkEGks= X-Google-Smtp-Source: AH8x226P6ZmvEowlqi7YwGAP5Rotn2Hoihw31EkP8HcE1g/MoLaw2Z/+y4sFqTb3lValS3aVz/IpGyMDxYU/qqu45sc= X-Received: by 10.13.251.129 with SMTP id l123mr1634917ywf.130.1518176894540; Fri, 09 Feb 2018 03:48:14 -0800 (PST) MIME-Version: 1.0 Received: by 10.37.182.201 with HTTP; Fri, 9 Feb 2018 03:48:14 -0800 (PST) In-Reply-To: References: <76EBF900-3BE8-4B60-9422-880A9754FCC4@lvht.net> <0DDFDACA-6322-461C-9D57-215E8C7D9CD0@trowski.com> Date: Fri, 9 Feb 2018 12:48:14 +0100 X-Gmail-Original-Message-ID: Message-ID: To: Haitao Lv Cc: Aaron Piotrowski , PHP Internals , Dmitry Stogov Content-Type: multipart/alternative; boundary="94eb2c07e3bc8168f90564c61a28" Subject: Re: [PHP-DEV] [RFC] Fiber support (again) From: me@kelunik.com (Niklas Keller) --94eb2c07e3bc8168f90564c61a28 Content-Type: text/plain; charset="UTF-8" > > > The language should offer a sane API, not the absolute minimum required > to > > work for these things. > > The Ruby's Fiber do offer a live? method but does not have a getStatus > method. > The Lua's coroutine only offer a status method. > > So do we really need to offer three additional helper method? Or what is > your > advice about these API? What's the downside? > > >>> - What about throwing exceptions into a fiber? > >> > >> Currently does not support throw exception into the fiber. User land > code > >> could check > >> the value of Fiber::yield and throw exception themselves. The Ruby's > Fiber > >> and Lua's > >> coroutine also does not support such api as well. > > > > > > And throw the exception where? That means async code with fibers can't > > really handle errors? > > Actually you can transfer any thing to Fiber by the resume method. And you > can > check the return value of Fiber::yield to handle error. > > Fiber is designed as a primitive, low level, and lightweight feature. User > land > code seldom not need to use them directly in your normal code. > So the following is not a big problem, > > $a = Fiber::yield(...); > if ($a === false) { > throw new Exception(...); > } > > And both the Ruby and Lua does not offer such API as well. > We have started building a PoC library on top of Fibers, see https://github.com/amphp/green-thread/blob/7bd3470e7986169372d5e9c39500f3652091b512/src/functions.php . We'd like to avoid the additional `await()` function and rather directly couple Fibers with promises in the PHP core. Using `await` (the keyword) instead of `Fiber::yield()` feels way better and avoids the need for any wrapping libraries for what should be a core feature. `async` can be added to create a new Fiber and Fiber can implement Promise. > > > >> > >>> > >>> - Using Fiber::resume() to initialize the fiber and resume feels > >> awkward. Separate methods again would be better here, perhaps > >> Fiber::init(...$args) and Fiber::resume($send). > >> > >> All Fiber created with a suspended status. So make resume to do both the > >> init and resume > >> do make sense. > >> > > > > It does't make sense to me. Reading the example in the README and > > understanding why the first resume() takes two arguments instead of one > > took me quite some minutes. > > This Ruby's Fiber and Lua's coroutine using one resume API to init and > resume > the coroutine. I do not think a dedicate is really required. > > The generator cannot be init by it's send method. And if you want to > implement > coroutine feature(without stack) by it, you have to write code > > function run() { > if ($this->beforeFirstYield) { > $this->beforeFirstYield = false; > return $this->coroutine->current(); > } else { > $retval = $this->coroutine->send($this->sendValue); > $this->sendValue = null; > return $retval; > } > } > > It is verbose. > That's why we directly prime coroutines on creation, see https://github.com/amphp/amp/blob/4a742beb59615f36ed998e2dc210e36576e44c44/lib/Coroutine.php#L36-L52 > See https://nikic.github.io/2012/12/22/Cooperative- > multitasking-using-coroutines-in-PHP.html > > > > >> Please see Ruby Fiber API https://ruby-doc.org/core-2.2.0/Fiber.html. > >> > >>> > >>> - What happens if the sub1() function in the RFC is invoked outside of > a > >> fiber? > >> > >> You will get a Fatal Error like > >> Fatal error: Uncaught Error: Cannot call Fiber::yield out of Fiber > >> > >>> - I think a keyword here would be beneficial, even if it has a minor BC > >> impact. Fibers could then be written like generators. `await` or `emit` > as > >> a keyword perhaps? This would be a less verbose API, feel less magical > (a > >> static method call that actually pauses execution feels out of place), > and > >> would allow Fibers to be returned from methods, named functions, etc > with > >> less boilerplate. > >> > >> Wishing this to be accepted by the community in the PHP 7.3, so no > keyword > >> is accepted. > >> And if the community cannot accept, the Fiber can be still distributed > as > >> a standalone > >> extension. So we cannot depend on a new keyword. > > > > > > Right, if it's a standalone extension it can't use a new keyword, but as > a > > language feature it totally can. > > In my opinion, using a keyword or call a method is just a coding style > problem. > Introducing a new keyword does not offer any benefit by makes a minor BC. > It makes code way easier to write IMO. > Both Ruby's Fiber and Lua's coroutine does not required a dedicate keyword. > > > Looking at the current README, there are two issues that must be > completely > > solved IMO before accepting this: > > > >> Each Fiber has a separate 4k stack. You can use the fiber.stack_size ini > > option to change the default stack size. You can also use the second > > argument of Fiber::__construct to set the stack size on fly. > > > > Resizing of the stack should happen automatically, just like generators > > resize automatically. > > This size is the init stack size. It means when a Fiber created, it will > get a > dedicate stack of 4k size. When the fiber use all the stack space, zend vm > will > allocate additional space for feature call frame, automatically. > > The default size is 4k means that every fiber requires at least 4k memory > to use > as there own stack. But user can change this by php.ini and the construct > argument > to reduce the memory footprint. That's good to hear! Any reason why these need to be configureable? They're not for generators either. IMO a sane default would probably be enough. > > > > >> Fiber::yield cannot be used in internal callback > > > > This also seems problematic and will make fibers quite less useful, > > especially as these yields can happen anywhere down the stack. > > > > This do be a problem. But not so much big problem. > > You cannot use Fiber::yield like > > $f = new Fiber(function () { > array_map(function ($i) { > Fiber::yield($i); > }, [1, 2, 3]); > }); > $f->resume(); > $f->resume(); > > Because when zend execute the array_map, it will push a new frame onto the > c stack. > When zend execute Fiber::yield, it only backup it's php stack, and it's c > stack > will be overwrites. > > However, you can use Fiber::yield like > > $f = new Fiber(function () { > Fiber::yield(1); // will cause by resume, by an internal callback > }); > > ExtEventLoop::onRead($fd, function () { // this is an internal call > $f->resume(); > }); The issue here is not using it directly within array_map, but rather somewhere down the stack, which a user can never know. This might result in subtle BC breaks. It means that a Fiber::yield() can never be added to code without breaking BC. I guess we need to rewrite all functions that accept user-defined callbacks for it to work. IMO PHP's main() function should be a Fiber, too, which makes it impossible to use Fiber::yield() outside a Fiber and enables top-level await(). Regards, Niklas --94eb2c07e3bc8168f90564c61a28--