>
> > 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

Reply via email to