> Let me summarize the current state for today:
>
> I am abandoning startScheduler and the idea of preserving backward compatibility with await_all
> or anything else in that category. The scheduler will be initialized implicitly, and this does not
> concern user-land. Consequently, the spawn function() code will work everywhere and always.
>
Very glad to hear this, this is the correct approach for concurrency, one that will not break all
existing libraries and give them the freedom to handle their own resource cleanup.
I’ve also seen your latest email about kotlin-like contexts, and they also make more sense than an
await_all block (which can only cause deadlocks): note how a kotlin coroutine context may only be
cancelled (cancelling all inner coroutines with CancelledExceptions), never awaited.
>
> >
> > I can give you several examples where such logic is used in Amphp libraries, and it will
> > break if they are invoked within an async block.
> >
>
> Got it, it looks like I misunderstood the post due to my focus. So, essentially, you're
> talking not so much about wait_all itself, but rather about the parent-child vs. free model.
>
> This question is what concerns me the most right now.
>
> If you have real examples of how this can cause problems, I would really appreciate it if you
> could share them. Code is the best criterion of truth.
>
Sure:
- https://github.com/amphp/websocket/blob/2.x/src/PeriodicHeartbeatQueue.php
- https://github.com/amphp/redis/blob/2.x/src/Sync/RedisMutex.php
This is the main example where it is most evident that background fibers are needed: logic which
requires periodic background pings to be sent in order to keep a connection alive, a mutex held or
something similar.
Constructing a PeriodicHeartbeatQueueinside of a wait_all block invoking a a someClass::connect(),
storing it in a property and destroying it outside of it in someClass::close or
someClass::__destruct, would cause a deadlock (the EventLoop::repeat doesn’t technically spawn a
fiber immediately, it spawns one every $interval, but it behaves as though a single background fiber
is spawned with a sleep($interval), so essentially it’s a standalone thread of execution,
collected only on __destruct).
https://github.com/danog/MadelineProto/tree/v8/src/Loop/Connection
contains multiple examples of tasks of the same kind in my own library (ping loops to keep
connections alive, read loops to handle updates (which contain vital information needed to keep the
client running correctly) in the background, etc...), all started on __construct when initialising
the library, and stopped in __destruct when they are not needed anymore.
A coroutine context/scope a-la kotlin is fine, but it should absolutely not have anything to await
all coroutines in the scope, or else it can cause deadlocks with the very common logic listed above.
Regards,
Daniil Gentili.