Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:113638 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 61132 invoked from network); 20 Mar 2021 08:01:35 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 20 Mar 2021 08:01:35 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 7FC991804C0 for ; Sat, 20 Mar 2021 00:56:17 -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-f49.google.com (mail-pj1-f49.google.com [209.85.216.49]) (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 ; Sat, 20 Mar 2021 00:56:16 -0700 (PDT) Received: by mail-pj1-f49.google.com with SMTP id x7-20020a17090a2b07b02900c0ea793940so7823284pjc.2 for ; Sat, 20 Mar 2021 00:56:16 -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; bh=KUNdqZP33p/tpiCis/Lu88lCGxwhxFgbLecXzqsFE+c=; b=p+KD8u8ofQC342AMSY52VRfOYOLzfVMS6pLnDwmengucu12BchWufNnq98DPwQX3m/ T0iS60qXrAJGcvYqclamtn+vql4NuIpncgmmdEmLOv8YmILECZxyTXDOlTKsNRS83Ziy u9DlhpYqVREGg227LQCbNCX3CP5+6e7hUeX0YLfFCn5feHAKdsc9RcOG096FEyI6w63s TRAYj3WsIK3JiVPMw8m854P3A5i8W5METW35hj8n64f7jLn/Uo89pcAKEt/0zrsx851O ZInNYPANdeU5F92A57f5Krn6e2OXmZB2+LZXe0KPv/zqBn8Ly6ISpXsaeYlnMesF+tZn fwSA== 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; bh=KUNdqZP33p/tpiCis/Lu88lCGxwhxFgbLecXzqsFE+c=; b=gumbQW0Ffmx0FbEOIjXZxX0ZmmukbNqW7RbLZD7VpsafsLttT44AuBrx0YosSO52ED baldOaCNPa+syQbQfbnhXG6mtKE0/cQkqhXkYtCVC/f0hGSf9r9FzGEn81/oHryu+CcU HcxJ0ahUtoIbAsmiDXCoBzncFY7cAy5CqLMbNEKfSHYZxHvDamlXDS/NNzb1A2CgbWje hLnG2fS09/XzyFzxiwfciP/QWId28uCb5Eio2k1YOxgyRggcJ5lc/GDNbuTVsHzdcTrl 3Yuqvr6aRjidWdjes/ffnw/rTpGApZ2+tgi820WBCvfKATFR+uBRaNKuk+QtzcIYCoNH H4rQ== X-Gm-Message-State: AOAM531jDrcoCdklYxS3hNovb9vLOWjFshvBt+LIQygRYbpTBa8E3/Zr 6OLpwUCS/XzKICes6vqfoMxgNqCqunhPvuKDyaQ= X-Google-Smtp-Source: ABdhPJwQ1wwqD4MBJSKPYN3cmkur7EiQBsFAAfzt8nN084mxMU8uGB4N8zIn1823/ci1QgSe39F4NQMVAEx9QUETXVY= X-Received: by 2002:a17:90b:38f:: with SMTP id ga15mr2410166pjb.149.1616226973873; Sat, 20 Mar 2021 00:56:13 -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: Sat, 20 Mar 2021 07:56:02 +0000 Message-ID: To: Rowan Tommins Cc: PHP internals Content-Type: text/plain; charset="UTF-8" Subject: Re: [PHP-DEV] [VOTE] Fibers From: joshdifabio@gmail.com (Josh Di Fabio) HI Rowan, Thanks for sharing your thoughts. On Fri, Mar 19, 2021 at 7:55 PM Rowan Tommins wrote: > > On 18/03/2021 09:20, Josh Di Fabio wrote: > > "If you want to enable fibers in your application, you must be > > confident about the implementation details of all of the code in your > > application, including that of your dependencies, which are written > > and maintained by other developers." > > > > I don't have anything to add to my previous point in that I disagree > > that this is practical. > > > While I agree that this is extremely difficult, and slows adoption of > asynchronous technologies, I think the challenge is not identifying > asynchronous code, it's identifying shared state. I agree. If we take it back to first principles, so to speak, then the problems all involve shared (mutable) state. Thanks for highlighting that. Perhaps focusing on shared mutable state might be helpful in identifying more popular and even more effective solutions to the kind of problems I'm concerned about. I.e. perhaps solutions which target/reduce/identify mutability via language features or static analysis might be more elegant, more effective, and almost certainly more popular (if added via new language features) than solutions which rather make it harder to utilise fibers. If developers could identify that their program contains only a few hundred state mutations (and they could see where they are), then I think reviewing and understanding those mutations asks something a bit more realistic of developers than reviewing and understanding the internals of their entire codebase. Having said that, it does seem like it would be very challenging to really encourage strict immutability at the language level in PHP given we lack most of the requisite building blocks, such as; ergonomic higher order functions, immutable data structures, generic data structures, local constants, etc. I suppose static analysis is a more realistic place to attack these problems, although that approach has various drawbacks. > In your example, you show code that was written to use shared state > unwittingly calling code that was written to be asynchronous: > > private function capturePayment() > { > $paymentRequest = preparePaymentRequest($this->currentOrder); > $this->paymentGateway->capturePayment($paymentRequest); > $this->currentOrder->setTransactionId($paymentRequest->getTransactionId()); > } > > However, the same problem exists the other way around - code written to > be asynchronous unwittingly calling code written to use shared state: > > private async function capturePayment() > { > $paymentRequest = $this->someDependency->preparePaymentRequest(); > await $this->paymentGateway->capturePayment($paymentRequest); > $this->someDependency->setTransactionId($paymentRequest->getTransactionId()); > } > > This all looks fine - but what if someDependency is actually calling > into a library which stores the current order in a static variable? Now > you have exactly the same race condition for the opposite reason. And > the solution is the same: carefully vet all your dependencies. > > > I can think of a few ways of solving this: > > 1) Require all the code to be synchronous. This is easy in PHP, even if > Fibers are supported: just don't run in an asynchronous framework. > > 2) Require most of the code to be synchronous. This is the common > approach of labelling functions as "async" or converting return values > to Generators or Promises. The big disadvantage is that it requires > rewriting a lot of code that doesn't care one way or the other if it's > called synchronously. > > 3) Require all the code to be free of shared state. This is ultimately > the only way you'll get the full advantage of asynchronous code. > > 4) Require most of the code to be free of shared state. Having some > primitives in the language to mark out code that definitely *can't* be > asynchronous would probably be useful. Perhaps you could mark a function > as "no-interrupt"; this could then be used as a wrapper when calling > into a library you know or suspect of using state in unsafe ways. Niklas has suggested something similar as far as I understand. Some other people have suggested that vendors could somehow declare at the package level that they are ~"fiber safe" (perhaps in composer.json). > > > 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. > > > Although this would avoid keeping both synchronous and asynchronous > versions of the same function, it would require adding that keyword to > every single function call, just in case somewhere inside it wants to > take advantage of your asynchronous environment. > > From my limited understanding, goroutines are a completely different > concept. Saying "go someFunction()" in Go immediately starts a new > thread-like thing, and doesn't return anything. The runtime manages the > scheduling of the thread, but if you want to get any data out of the > goroutine, you have to pass it explicitly, e.g. via a channel. > > > Regards, > > -- > Rowan Tommins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > Thanks for taking the time to share your thoughts. I found them very insightful and helpful. As a side note, GitHub published a blog post a couple of days ago which provides an interesting real world example of this kind of issue if people are interested. https://github.blog/2021-03-18-how-we-found-and-fixed-a-rare-race-condition-in-our-session-handling/ Cheers