-
-
Notifications
You must be signed in to change notification settings - Fork 345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can we make abort functions more composable? #896
Comments
Interesting question! So first let's think about what kind of scope we're hoping for... obviously we can't support this for all blocking operations. It only seems to make sense for "primitive" blocking operations (those that only call The original idea of I do wonder if it's worth trying to extend
Or, maybe we should go in the other direction, and try to make What exactly are the things that make
I'll add the |
A almost cycling example of the above gist is here. I was able to fix all the minor errors in the code and try to cycle it but it still fails catastrophically with an internal trio error.
|
The approach of using trio.Event instead of trio.hazmat.Task for the key of self._waiters works fine (but probably does not have the desired cancelling behavior that you guys want). |
@ktarplee Oh, I see the bug, it's very silly. When we reschedule a task in the I'm not sure what this says about APIs. Maybe it's an argument for the fancier |
I have a working trio.Event based implementation of this same idea and that is what I had to do however I deleted the waiter after the Do I need to collect all tasks to reschedule, then delete them from waiters, then reschedule each? |
Something like... awake = []
for other_task, other_wanted in self._waiters.items():
if other_wanted in self._available:
self._available -= other_wanted
awake.append(other_task)
for other_task in awake:
del self._waiters[other_task]
for other_task in awake:
trio.hazmat.reschedule(other_task) |
@ktarplee The lazy solution is: for other_task, other_wanted in list(self._waiters.items()):
if other_wanted in self._available:
self._available -= other_wanted
del self._waiters[other_task]
trio.hazmat.reschedule(other_task) (Notice the added
So here's the problem with using with trio.move_on_after(10): # I'm only willing to wait 10 seconds for this
async with resource_pool.claim(...):
... And further suppose that the timeout expires while (You might think: well, maybe I can catch that |
If I try to concretize my original idea here, I get something like:
is equivalent to
except that if If people think this is a good idea, I don't think it would be difficult to implement, but it sounds like we may want to do something with broader scope instead. |
In chat today, @njsmith gave a neat example of how to build a resource-aware job scheduler using
trio.Event
. He mentioned that doing the bookkeeping in the task that's doing the wakeup, rather than in the task that's being woken up, seems broadly essential to avoiding race conditions (which I agree with).I think there's a rough edge with respect to how cancellation handling fits into this, though:
wait_task_rescheduled
, yourabort_fn
runs synchronously with the cancellation being delivered. So if the waking task sees that you're still registered, you won't be cancelled before you're woken up; and if you do get cancelled, the waking task won't see you as still eligible to be woken up. This is great. It means the most natural way to handle cancellation is also relatively race-proof.trio.Event.wait()
, the only way you can do cleanup on cancellation is by catching theCancelled
exception after it's injected. Moreover, it's possible that a task gets cancelled, but that before it next runs to deliver the cancellation, it also is woken up due to the resource it was waiting for becoming available. Now we're back to writing bookkeeping logic inside the woken task. This is less great.It seems to me that we might be well served by having a way of effectively composing abort functions. That is, when I say
await event.wait()
, I should have the option of designating some code that will run synchronously with a cancellation ofevent.wait()
.Doing this in a way that lets the outer logic block the cancellation (i.e., exposing the full
abort_fn
power) is very tricky. Doing it in a way that only supports supplying additional code to run when the cancellation succeeds is probably easier though. Do folks have thoughts on whether this is desirable, independent of how it might best be implemented?The text was updated successfully, but these errors were encountered: