On Fri, Mar 12, 2021 at 10:54 PM Aaron Piotrowski <aa...@trowski.com> wrote:
>
>
> > On Mar 12, 2021, at 4:36 PM, Christoph M. Becker <cmbecke...@gmx.de> wrote:
> >
> > On 12.03.2021 at 23:04, Michael Wallner wrote:
> >
> >> Thank you, and everyone involved, for your effort.
> >>
> >> On 08/03/2021 20.40, Aaron Piotrowski wrote:
> >>> Greetings everyone!
> >>>
> >>> The vote has started on the fiber RFC: https://wiki.php.net/rfc/fibers 
> >>> <https://wiki.php.net/rfc/fibers>
> >>>
> >>> Voting will run through March 22nd.
> >>
> >> I voted /no/, because of the dependency on Boost.
> >> If my assumptions are wrong, I may reconsider my vote.
> >
> > Only asm files are used[1], and these can be bundled, so there is no
> > dependency on boost or C++ in general.
> >
> > [1] <https://github.com/amphp/ext-fiber/tree/master/boost>
> >
> > --
> > Christoph M. Becker
> >
>
> Hi Mike, Christoph, and Derick,
>
> To add a bit more information:
>
> These asm files are part of the low-level boost.context lib, found here: 
> https://github.com/boostorg/context
>
> This library has infrequent releases. Some of the files for old architectures 
> have not changed in several years. Keeping these files up-to-date will not be 
> a burden (and I plan to assume this responsibility).
>
> The Boost license is extremely permissive and approved by the OSI, so there 
> is no problem bundling with PHP.
>
> Hopefully that provides some clarification and you’ll reconsider your vote 
> Mike.
>
> Cheers!
> Aaron Piotrowski
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>

Apologies, this is a long one!

This RFC strikes me as being very dangerous. Implicitly allowing code
which is synchronous by design to be executed asynchronously seems
sure to lead to very subtle, unpredictable, confusing and dangerous
bugs.

The issue is that executing code asynchronously introduces race
conditions and, in code which is synchronous by design, those race
conditions will lead to bugs which will never occur during QA or
development and will be incredibly hard to identify once they do occur
in production.

These are the kind of bugs where user sessions get mixed up, the wrong
package is installed by a package manager, or an ecommerce website
captures the wrong amount of money from a customer's credit card. And
developers will have no realistic way of tracking down why these
things are happening in real world, multi developer, multi vendor PHP
applications.

For example, the PHP ecosystem is full of code like this:

    private function capturePayment()
    {
        $paymentRequest = preparePaymentRequest($this->currentOrder);
        $this->paymentGateway->capturePayment($paymentRequest);
        
$this->currentOrder->setTransactionId($paymentRequest->getTransactionId());
    }

In the above code snippet, the developer has relied on the fact that
the mutable value $this->currentOrder will be the same before and
after the call to $this->capturePayment(), because capturePayment() is
synchronous. In all likelihood, the authoring PHP developer is not
even familiar with asynchronicity, and the assumption that
capturePayment() is synchronous is therefore not even a conscious one.
Note that, if capturePayment is executed asynchronously, eventually we
will see payments attributed to the wrong orders.

Note that, if fibers land, there is no know whether the code above is
correct by looking at it in isolation. You can only know that by
knowing the environment in which the code is executed, as well as
understanding *all of the internals of capturePayment()*, and its
dependencies, many of which will probably managed by other developers
and will involve a bunch of injected services, many of which will do
IO and therefore are candidates for using fibers, directly or
indirectly, such as logging, HTTP, and database.

The worst characteristics to me are the following:
- These race conditions will cause problems so rarely that developers
will not even realise that their programs contain dangerous bugs. Code
which is not safe to use with fibers will appear to be functional
because the happy path will work fine, and so developers will assume
it's okay.
- Changes very, very far away from your code can cause your code to
become unexpectedly asynchronous. Imagine you are using the DynamoDB
monolog logger in your application, and that library indirectly uses
Guzzle, which gets fiber support. Any place you are logging anything
now becomes asynchronous, as well as any code which invokes that code.
If you are using some kind of event dispatch pattern then pretty much
everything in your program becomes asynchronous.
- As a library author, there is no way of knowing how your code is
going to be used, and what injected dependencies might use fibers
internally.

The counter arguments:
- "The above code snippet is suboptimal, it shouldn't assume that
mutable state remains unchanged following some IO, and the order
should be passed in as an arg." -- Whether or not this is true is
irrelevant -- developers have relied on the invariants of the PHP
language when writing their code, such as synchronous code being
synchronous, and this is how lots of code is written in the wild. If
this feature lands, that won't change -- very few userland developers
will understand this feature, including library authors.
- "Fibers should only be enabled in async first programs." and "We
could flag that certain libraries are fiber compatible or
incompatible." If code appears to work in a fiber environment, people
will just use it in a fiber environment, without understanding all of
the security issues and horrible bugs they're introducing. This
problem should rather be solved at the language level by making async
explicit.

Consider the following:

    private async function capturePayment()
    {
        $paymentRequest = preparePaymentRequest($this->currentOrder);
        await $this->paymentGateway->capturePayment($paymentRequest);
        
$this->currentOrder->setTransactionId($paymentRequest->getTransactionId());
    }

Looking at the above code, I can see the race condition. It becomes
explicit. So I change the code and communicate explicitly via the type
system that this function is asynchronous and that my code expects
PaymentGateway::capturePayment() to be asynchronous.

    private async function capturePayment()
    {
        $order = $this->currentOrder;
        $paymentRequest = preparePaymentRequest($order);
        await $this->paymentGateway->capturePayment($paymentRequest);
        $order->setTransactionId($paymentRequest->getTransactionId());
    }

Regarding PHP's type system not supporting generics, and the
associated advantages of this RFC over promises; I agree that this is
a problem with promises. But the lack of generics in PHP already means
we have no language-level type safety on data structures, which is
certainly worse than not having type safety on promises. Psalm et al
have provided reasonable solutions to this issue.

Sorry for the criticism of the idea -- I've used React and Amp in
production on a product which has been in prod for many years -- and
I'd be happy to see first class asynchronous support within the
language, but I think this RFC would make the language worse, not
better. Technically, I'm sure this is great, and I do appreciate that
a lot of work has probably been put into this.

Thanks

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to