Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:113572 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 86630 invoked from network); 16 Mar 2021 18:50:36 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 16 Mar 2021 18:50:36 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id ED91D1804B8 for ; Tue, 16 Mar 2021 11:44:25 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-pj1-f46.google.com (mail-pj1-f46.google.com [209.85.216.46]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 16 Mar 2021 11:44:25 -0700 (PDT) Received: by mail-pj1-f46.google.com with SMTP id ga23-20020a17090b0397b02900c0b81bbcd4so1860463pjb.0 for ; Tue, 16 Mar 2021 11:44:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=fExMbiWxZaFCN3ouATX4fQsR3DA2AyMKkzCyLn1GtyM=; b=ODgIePTJbcX0pVq5aysPxQ1cxtDqwvftAOCgCM7dUkRha7cNlXgwd1ul6l07owkCVW gNjv9fVlohB3A8URJdiJ1eLgCcJMOfaFFLbOQP6piBL5R2mFNxg0CzT8AS0L+ndSaATU wBiPLPkZyTf3dnld3tc/DW+Kzhur78dPdY7UKKpxhtVQ+F5+ZXw0w/o8r3diqRc2wGWt WYWxRIHPWYqn8sUeXBXIJIN5bB3JbQXxMvA+qzLZeDLA1f8KlGrfM/0kncTBGQseN3ZI v1gjFS7YWuzGssnU5kNWmtwksrpTPGWW0jDYy0cq5Yy2R9+jAQJoqcxpyHXnxeX8zil0 7IzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=fExMbiWxZaFCN3ouATX4fQsR3DA2AyMKkzCyLn1GtyM=; b=OCvPt/0ZfI/mlcpTwX6AJ8WZyPv5EOvHCTWLfEkdTM5VYYcwzcG/JILHZKw5VdYWxr GGiqH1eJ7yCcbseqHc6/VM9NDtHo4HjDZF6444fOTpw8lkthCrJKjuArdLs2salGck1E XDTJF6aFy3JTct0H9FJttkUWn/iqpjrNMSluQEwSbpu1New6ftGuWmAbiF0Wn6ibxPJl nbw5tOC4B0JDSgGYPzEmdmaXXvcMWHc6XqE+wMUDAvrVyV11UKC8Kj3t60cy2FUqq4Pt kJB/RXNQPn+MwY00YaeI+cP0aXJJx/oYBSbXdQjDhnimbK2u44jWNi3jeBZnxUuxVTtm WDsA== X-Gm-Message-State: AOAM531YPiEGb0qTUZUd5mqQH9Zozcpai7K5hF3AaS/5z5VL4iI27wK/ 4pu/aGsLc7TvIThxXztjzxnFrbo1t1LCxqLZb38= X-Google-Smtp-Source: ABdhPJyAN1X/YddkFoiE8hvhrCe6nNu7pwlcSa9fiIj9TsO53RNJ6UN/aeXQosI9B9zmG2i2R1jWLy7N64WUuAldAv0= X-Received: by 2002:a17:90b:691:: with SMTP id m17mr415860pjz.191.1615920264255; Tue, 16 Mar 2021 11:44:24 -0700 (PDT) MIME-Version: 1.0 References: <70951423-5e77-c150-6dce-dd3c62f8dc8b@php.net> <0b994a60-7970-605b-1657-d6ee732690e5@gmx.de> <5C73A1DE-E563-4F69-B8C7-6506F81D7345@trowski.com> In-Reply-To: Date: Tue, 16 Mar 2021 18:44:12 +0000 Message-ID: To: Niklas Keller Cc: Aaron Piotrowski , "Christoph M. Becker" , Michael Wallner , php internals , Derick Rethans Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] [VOTE] Fibers From: joshdifabio@gmail.com (Josh Di Fabio) On Tue, Mar 16, 2021 at 6:20 PM Niklas Keller wrote: > > Hey Josh, > >> >> 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. > > > Thank you for sharing your thoughts, I'll respond inline for better conte= xt, but shortened some paragraphs for better readability. Many thanks for the detailed reply. I've replied inline. >> >> [...] >> >> For example, the PHP ecosystem is full of code like this: >> >> private function capturePayment() >> { >> $paymentRequest =3D preparePaymentRequest($this->currentOrder); >> $this->paymentGateway->capturePayment($paymentRequest); >> $this->currentOrder->setTransactionId($paymentRequest->getTransa= ctionId()); >> } >> >> 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() [...] > > > This is a very valid concern to have. However, this code won't simply bre= ak if executed asynchronously. > It only breaks if the same method (or other methods making use of the sam= e state) is executed concurrently on that object. I understand this, but of course this will be common in programs where fibers are used, and the nature of this proposal means that fibers will potentially be highly insidious and quite unpredictable in their reach. >> >> [...] >> >> - These race conditions will cause problems so rarely that developers >> will not even realise that their programs contain dangerous bugs. [...] >> - Changes very, very far away from your code can cause your code to >> become unexpectedly 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." [...] >> - "Fibers should only be enabled in async first programs." and "We >> could flag that certain libraries are fiber compatible or >> incompatible." [...] >> >> Consider the following: >> >> private async function capturePayment() >> { >> $paymentRequest =3D preparePaymentRequest($this->currentOrder); >> await $this->paymentGateway->capturePayment($paymentRequest); >> $this->currentOrder->setTransactionId($paymentRequest->getTransa= ctionId()); >> } >> >> 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. > > > If you can see the race condition here, you can probably also see the rac= e condition in the original snippet above. The difference is that in the second case, the developer has *explicitly opted into the underlying call being async*. I.e. they expect it, and can design with it in mind. > The overall issue you're describing is thread safety and mutable state. I= t exists with fibers, but a very similar issue exists with explicit async /= await. > Just because something supports asynchronous I/O, doesn't mean it also su= pports concurrent operations on its state. > > It might be a good idea to introduce some guarding concept in a follow-up= RFC, so objects can easily protect against being used concurrently e.g. > > ``` > private synchronized function capturePayment() > { > $paymentRequest =3D preparePaymentRequest($this->currentOrder); > $this->paymentGateway->capturePayment($paymentRequest); > $this->currentOrder->setTransactionId($paymentRequest->getTransactio= nId()); > } > ``` > > "synchonized" isn't the right keyword here, but I can't think of a better= name right now. > >> >> 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. > > > Being able to use the type system is a concern the RFC solves, but that's= rather just nice-to-have. The relevant problem is the call stack pollution= or "What color is your function" problem. It simply doesn't make sense for= applications and libraries to offer an async and non-async interface for e= verything. Due to the "What color is your function" problem, there can't be= a gradual migration to non-blocking I/O, which will mostly result in non-b= locking I/O simply not being supported by the majority of libraries, making= it basically non-viable in PHP. Perhaps we could rather make fibers *opt in* at the *callsite* (similar to goroutine calls) in order to prevent functions unexpectedly being executed asynchronously due to faraway changes. This would be safe and predictable while also avoiding the "What color is your function" problem. For example private function capturePayment(): void { $paymentRequest =3D preparePaymentRequest($this->currentOrder); // allow, but don't require, the underlying call to use fibers. Callers higher up the stack would also have to opt in async $this->paymentGateway->capturePayment($paymentRequest); $this->currentOrder->setTransactionId($paymentRequest->getTransacti= onId()); } With this approach, APIs we'd avoid the "what colour is your function" problem without sacrificing the safety that we really need in e.g. ecommerce. > > Best, > Niklas Thanks again for engaging.