Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126835 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id D70FF1A00BC for ; Wed, 19 Mar 2025 08:26:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1742372648; bh=DD22EYZxv710k1iGaMaYZTh7+9xtmcAiem+h5IJFlXo=; h=From:Subject:Date:References:To:In-Reply-To:From; b=g6uTfNGun2LK5QNJPVSx8Y8pmMbbr5vBe1VcHp/FQhCeTDZxQfsnSpg+qKnKzDRI0 Pu++iqBqTrYCE5x3xOJpIFVI6Cx4Q7PpwuBlpGgZknlHy4dQLXsB91I5l7UCZV5g8g OgA/OUmY0SYpnaXDm6FM2QiZi93od+Qgv5ogywqH2/l0v6IdnMWt3a1cOYiK0uSfpM jF19GiZN1XpltHLNhxEmy1BfkQlBqFrTeMCItOFzTHaQFnPhHh3FdahnWkQksOKHZ/ XJ8/cUPOhryfBWUZc60iKFmKP0pbmRSWqYUyAAeXwqsvKPt6DcET49Ej8kzaxIwdTP O2YA0o4zUrW9A== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 296CA180053 for ; Wed, 19 Mar 2025 08:24:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-ed1-f52.google.com (mail-ed1-f52.google.com [209.85.208.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 19 Mar 2025 08:24:06 +0000 (UTC) Received: by mail-ed1-f52.google.com with SMTP id 4fb4d7f45d1cf-5e033c2f106so9265437a12.3 for ; Wed, 19 Mar 2025 01:26:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1742372796; x=1742977596; darn=lists.php.net; h=message-id:in-reply-to:to:references:date:subject:mime-version:from :from:to:cc:subject:date:message-id:reply-to; bh=ZPYcdncHKNJyOp+kyuIqJmcGO6/pHQ2+XrMYejdLEKI=; b=d5FQD+YseEwvlggRjvOSS+HcIKoaad2AxFEKOSbV0SqZ+6f0oTxGFGWaArLf+4S5RO h024qC9F10VH1vtRZZqAifBxC+RiB8AqcXU4GhFjwuLV5LI/ZXahebbwl7WXx8jGYa7F Qei3NajhI3WQacc7R6XZUCqdIXYHAxOsJ7YJX7Rna3zNVR4fIX40muCB9F0bOLt+BUtt L5KX+MC530FhtIZz6poqHcyMN4jBa/LbZs3wBMs2afwbJIRGsdUxR/kcy4rEhMYqRJOX E/5Zy1vIe3RENbSS2dJR70FlUf1XqJf5padkeNcpZRYE2Q6zMfi0DkLrAfEgSydbTIi0 gCDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742372796; x=1742977596; h=message-id:in-reply-to:to:references:date:subject:mime-version:from :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ZPYcdncHKNJyOp+kyuIqJmcGO6/pHQ2+XrMYejdLEKI=; b=rkRrdIB+aDp0j+ul0P2h0j7U0hetDPbeYXMbY0h5ZtJ5TgU6WTL/ES/Ake8uWrarPu Qd8vbA0tBz9blEn36rFqWThGg9msbwPpGbxE9Na5SmdmsnkT+A3Z+R3dJ+R25KojonD4 l8yyhlg6uQ/92A+tf2iYO+aTanLySv1/iOPswTqdbTCZbNaz1xR3xIywT3/creL4xrvp T/wCzDWlMSOTjngmSDpeD6q8bKvLpcLsjW0qGHCVDEB9QwLqTbGdUpgCEvQZsmSZ8mw9 28qygoXXvrIzUqm03R4Ooh5mCK5GJgCJnGJc23gSywR3u20wuG7W+kpq4tIlFCiC90KQ krnw== X-Gm-Message-State: AOJu0YyYgoqbnY2zkxy6VnONxuLzyXVFx2OQJqxLuv4s9sDy3F/2fV48 5rYRcsSB5CXLvHqzguTA2vzCCBnpm66reDC+Q6EtYwuCA8VJ0qqcN22w8w== X-Gm-Gg: ASbGncsP82ed3u+FPh5MrSJjKs3CVTEYHrmorgxq9PO56QSMDsP3iHs7NnhoQNJKMli vMbUD5qHJpGwIF+jsGBv5uAsZu9Niwbte1zQFhlaJaqpsf1PNkqyy7+e2ThFIJfWtrF3FiWQT5J KGlEHh8w2kef1StYXEZew1O7ww2/aAZnDjoyFkJwwoBxRyjMckZflv4GNfW87Jzw8zSxcVZ1pnj HOU/af83uXAI0BU9GBY4FPTsqXGWoJOQ05ZtzR7Y0zroqwrMz09zV1FIz980u1Oac8tDR4+FdnW u9ksmQED7wLv0xfiuwkOxOXugG0JHsyjm5Z7Np0TujFOoKvuE/0dINtdOmaS0BVooaR2bAL1Amk lBosVhdIWmuFT2Emsx/yl/Y7SQJ9xbeaZRKWmWUUuMWKSMy0M+p/45g== X-Google-Smtp-Source: AGHT+IFVut4AH309dg39jgnSlU/H1MDC5Uj1/FO74O3WuG/MMNYGue+YE22m5mpn76P+CyOxBSZ6wg== X-Received: by 2002:a05:6402:13d3:b0:5e0:8c55:50d with SMTP id 4fb4d7f45d1cf-5eb80d29dc8mr1693555a12.14.1742372796238; Wed, 19 Mar 2025 01:26:36 -0700 (PDT) Received: from smtpclient.apple (luna-5a179190a417ccc30000f.net.as198747.daniil.it. [2a0e:97c0:38f:0:3ccc:714a:919:71a5]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5e816afdff2sm8786672a12.67.2025.03.19.01.26.35 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 19 Mar 2025 01:26:35 -0700 (PDT) Content-Type: multipart/alternative; boundary="Apple-Mail=_02417645-2871-46C3-B5ED-8C3D419E87E5" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.400.131.1.6\)) Subject: Re: [PHP-DEV] PHP True Async RFC - Stage 2 Date: Wed, 19 Mar 2025 09:26:23 +0100 References: <72bd5401-53a9-409f-ad45-687333401961@rwec.co.uk> To: internals@lists.php.net In-Reply-To: Message-ID: X-Mailer: Apple Mail (2.3826.400.131.1.6) From: daniil.gentili@gmail.com (Daniil Gentili) --Apple-Mail=_02417645-2871-46C3-B5ED-8C3D419E87E5 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 >=20 > P.S. + 1 example: >=20 > =20 > declare(strict_types=3D1); >=20 > use Async\Scope; > use function Async\currentScope; >=20 > function fetchUrl(string $url): string { > $ctx =3D stream_context_create(['http' =3D> ['timeout' =3D> 5]]); > return file_get_contents($url, false, $ctx); > } >=20 > function fetchAllUrls(array $urls): array > { > $futures =3D []; > =20 > foreach ($urls as $url) { > $futures[$url] =3D (spawn fetchUrl($url))->getFuture(); > } > =20 > await currentScope(); > =20 > $results =3D []; > =20 > foreach ($futures as $url =3D> $future) { > $results[$url] =3D $future->getResult(); > } > =20 > return $results; > } >=20 > $urls =3D [ > 'https://example.com ', > 'https://php.net ', > 'https://openai.com ' > ]; >=20 > $results =3D await spawn fetchAllUrls($urls); > print_r($results); >=20 I still strongly believe the RFC should not include the footgun that is = the await on the current scope, and this example you sent shows exactly = why: you gather an array of futures, and instead of awaiting the array, = you await the scope (potentially causing a deadlock if client libraries = do not use a self-managed scope manually, using whatever extra syntax is = required to do that), and then manually extract the values for some = reason. This is still using the kotlin approach equivalent to its runBlocking = function, with all the footguns that come with it. I would like to invite you to google =E2=80=9CrunBlocking deadlock=E2=80=9D= on google, and see the vast amount of results, blogposts and questions = regarding its dangers: = https://letmegooglethat.com/?q=3Dkotlin+runblocking+deadlock A few examples: - https://discuss.kotlinlang.org/t/coroutines-and-deadlocks/18060/2 - = "You launched a runBlocking inside the default dispatcher for each = core=E2=80=A6 You are abusing the coroutine machinery! Do not use = runBlocking from an asynchronous context, it is meant to enter an = asynchronous context!=E2=80=9D (a newbie abused an async {}/ await = currentScope()) - = https://medium.com/better-programming/how-i-fell-in-kotlins-runblocking-de= adlock-trap-and-how-you-can-avoid-it-db9e7c4909f1 - How I Fell in = Kotlin=E2=80=99s RunBlocking Deadlock Trap, and How You Can Avoid It = (async {}/await currentScope() blocks on internal kotlin runtime fibers, = causing a deadlock in some conditions) Even the official kotlin documentation = (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kot= linx.coroutines/run-blocking.html) says "Calling runBlocking from a = suspend function is redundant. For example, the following code is = incorrect:=E2=80=9D suspend fun loadConfiguration() { // DO NOT DO THIS: val data =3D runBlocking { // <- redundant and blocks the thread, do = not do that fetchConfigurationData() // suspending function } } Which is fully equivalent to the following PHP (note that I use the = =E2=80=9Casync {}=E2=80=9D nursery still used by some people in the = discussion, but fully equivalent logic could be written using await = currentScope): function loadConfiguration() { // DO NOT DO THIS: async { // <- redundant and blocks the thread, do not do that $data =3D fetchConfigurationData(); // suspending function } } With current rfc syntax: function loadConfiguration() { // DO NOT DO THIS: $data =3D fetchConfigurationData(); // suspending function await currentScope(); // <- redundant and blocks the thread, do not = do that } When even the official language documentation is telling you in ALL CAPS = to not use something, you automatically know it=E2=80=99s a major = footgun which has already been abused by newbies. As I reiterated before, making a scope awaitable is a footgun waiting to = happen, and while at least now there=E2=80=99s an escape hatch in the = form of custom scopes, forcing libraries to use them is a very bad idea = IMO, as other people said in this thread, if there=E2=80=99s an = =E2=80=9Ceasy=E2=80=9D way of spawning fibers (using the global/current = context), you discourage people from using the =E2=80=9Cless easy=E2=80=9D= way of spawning fibers through custom contexts, which will inevitably = lead to deadlocks. I strongly believe that golang=E2=80=99s scopeless approach (which is = the current approach already used by async php) is the best approach, = and there should be no ways for users to mess with the internals of = libraries that accidentally spawn a fiber in the current scope instead = of a custom one. Regards, Daniil Gentili. --Apple-Mail=_02417645-2871-46C3-B5ED-8C3D419E87E5 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8

P.S. + 1 = example:

<?php

declare(strict_types=3D1);

use Async\Scope;
use function Async\currentScope;

function fetchUrl(string $url): string {
$ctx =3D stream_context_create(['http' =3D> ['timeout' =3D> 5]]);
return file_get_contents($url, false, $ctx);
}

function fetchAllUrls(array $urls): array
{
$futures =3D [];

= foreach ($urls as $url) {
$futures[$url] =3D (spawn fetchUrl($url))->getFuture();
}
=
await = currentScope();
=
$results =3D [];
=
foreach ($futures as $url =3D> $future) {
$results[$url] =3D $future->getResult();
}

= return $results;
}

$urls =3D [
'https://example.com',
= 'https://php.net',
'https://openai.com'
];

<= span style=3D"color:rgb(102,0,0)">$results =3D await spawn = fetchAllUrls($urls);
print_r($results);

<= /div>

I still strongly believe the RFC = should not include the footgun that is the await on the current scope, = and this example you sent shows exactly why: you gather an array of = futures, and instead of awaiting the array, you await the scope = (potentially causing a deadlock if client libraries do not use a = self-managed scope manually, using whatever extra syntax is required to = do that), and then manually extract the values for some = reason.

This is still using the kotlin approach = equivalent to its runBlocking function, with all the footguns that come = with it.

I would like to invite you to = google =E2=80=9CrunBlocking deadlock=E2=80=9D on google, and see the = vast amount of results, blogposts and questions regarding its dangers: = https://letmegooglethat.com/?q=3Dkotlin+runblocking+deadlock
A few examples:
- = https://discuss.kotlinlang.org/t/coroutines-and-deadlocks/18060/2 - "You = launched a runBlocking inside the default dispatcher for each core=E2=80=A6= You are abusing the coroutine machinery! Do not use runBlocking from an = asynchronous context, it is meant to enter an asynchronous context!=E2=80=9D= (a newbie abused an async {}/ await currentScope())
- = https://medium.com/better-programming/how-i-fell-in-kotlins-runblocking-de= adlock-trap-and-how-you-can-avoid-it-db9e7c4909f1 - How I Fell in = Kotlin=E2=80=99s RunBlocking Deadlock Trap, and How You Can Avoid It = (async {}/await currentScope() blocks on internal kotlin runtime fibers, = causing a deadlock in some conditions)

Even the = official kotlin documentation = (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kot= linx.coroutines/run-blocking.html) says "Calling runBlocking from a = suspend function is redundant. For example, the following code is = incorrect:=E2=80=9D

suspend fun = loadConfiguration() {
    // DO NOT DO = THIS:
    val data =3D runBlocking { // <- = redundant and blocks the thread, do not do that
    =     fetchConfigurationData() // suspending = function
    = }
}

Which is fully equivalent to the = following PHP (note that I use the =E2=80=9Casync {}=E2=80=9D nursery = still used by some people in the discussion, but fully equivalent logic = could be written using await = currentScope):

function loadConfiguration() = {
    // DO NOT DO THIS:
    async { // = <- redundant and blocks the thread, do not do that
  =       $data =3D fetchConfigurationData(); // suspending = function
    = }
}


With current rfc = syntax:

function loadConfiguration() = {
    // DO NOT DO THIS:
    $data =3D = fetchConfigurationData(); // suspending function
    = await currentScope(); // <- redundant and blocks the thread, do not = do that
}


When = even the official language documentation is telling you in ALL CAPS to = not use something, you automatically know it=E2=80=99s a major footgun = which has already been abused by newbies.

As I = reiterated before, making a scope awaitable is a footgun waiting to = happen, and while at least now there=E2=80=99s an escape hatch in the = form of custom scopes, forcing libraries to use them is a very bad idea = IMO, as other people said in this thread, if there=E2=80=99s an = =E2=80=9Ceasy=E2=80=9D way of spawning fibers (using the global/current = context), you discourage people from using the =E2=80=9Cless easy=E2=80=9D= way of spawning fibers through custom contexts, which will inevitably = lead to deadlocks.

I strongly believe that = golang=E2=80=99s scopeless approach (which is the current approach = already used by async php) is the best approach, and there should be no = ways for users to mess with the internals of libraries that accidentally = spawn a fiber in the current scope instead of a custom = one.

Regards,
Daniil = Gentili.
= --Apple-Mail=_02417645-2871-46C3-B5ED-8C3D419E87E5--