>
> P.S. + 1 example:
>
> <?php
>
> declare(strict_types=1);
>
> use Async\Scope;
> use function Async\currentScope;
>
> function fetchUrl(string $url): string {
> $ctx = stream_context_create(['http' => ['timeout' => 5]]);
> return file_get_contents($url, false, $ctx);
> }
>
> function fetchAllUrls(array $urls): array
> {
> $futures = [];
>
> foreach ($urls as $url) {
> $futures[$url] = (spawn fetchUrl($url))->getFuture();
> }
>
> await currentScope();
>
> $results = [];
>
> foreach ($futures as $url => $future) {
> $results[$url] = $future->getResult();
> }
>
> return $results;
> }
>
> $urls = [
> 'https://example.com <https://example.com/>',
> 'https://php.net <https://php.net/>',
> 'https://openai.com <https://openai.com/>'
> ];
>
> $results = await spawn fetchAllUrls($urls);
> print_r($results);
>
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 “runBlocking deadlock” on google, and see
the vast amount of results, blogposts and questions regarding its dangers:
https://letmegooglethat.com/?q=kotlin+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… You are
abusing the coroutine machinery! Do not use runBlocking from an asynchronous
context, it is meant to enter an asynchronous context!” (a newbie abused an
async {}/ await currentScope())
-
https://medium.com/better-programming/how-i-fell-in-kotlins-runblocking-deadlock-trap-and-how-you-can-avoid-it-db9e7c4909f1
- How I Fell in Kotlin’s 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/kotlinx.coroutines/run-blocking.html)
says "Calling runBlocking from a suspend function is redundant. For example,
the following code is incorrect:”
suspend fun loadConfiguration() {
// DO NOT DO THIS:
val data = 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 “async {}”
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 = fetchConfigurationData(); // suspending function
}
}
With current rfc syntax:
function loadConfiguration() {
// DO NOT DO THIS:
$data = 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’s 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’s 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’s an “easy” way of spawning fibers (using the
global/current context), you discourage people from using the “less easy” way
of spawning fibers through custom contexts, which will inevitably lead to
deadlocks.
I strongly believe that golang’s 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.