-
Notifications
You must be signed in to change notification settings - Fork 138
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
Object stream randomly drops items #146
Comments
diff --git a/src/anyio/streams/memory.py b/src/anyio/streams/memory.py
index 9b8e8ee..693caba 100644
--- a/src/anyio/streams/memory.py
+++ b/src/anyio/streams/memory.py
@@ -70,6 +70,8 @@ class MemoryObjectReceiveStream(Generic[T_Item], ObjectReceiveStream[T_Item]):
try:
await receive_event.wait()
except BaseException:
+ if container:
+ self._state.buffer.append(container[0])
self._state.waiting_receivers.pop(receive_event, None)
raise The basic problem: when |
Thanks for the report, the accurate analysis and solution! The only change I would make is to use |
I don't think returning the object to the internal buffer like that quite
fixes the bug, because the stream is supposed to deliver objects in FIFO
order, right? Consider if task A grabs an item from the buffer, then task B
grabs an item from the buffer, then task A "puts back" the item it grabbed.
Now the stream has re-ordered the objects.
…On Wed, Aug 12, 2020 at 11:20 PM Alex Grönholm ***@***.***> wrote:
Thanks for the report, the accurate analysis and solution! The only change
I would make is to use .appendleft() instead.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#146 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAEU42EBXTGWH5H2NHBAETTSAOA2HANCNFSM4P5P2OAA>
.
--
Nathaniel J. Smith -- https://vorpus.org <http://vorpus.org>
|
I'm not sure how to fix it then, but I will look at trio's implementation for inspiration. |
I don't understand how trio circumvents the same problem. If a task is waiting to receive from a memory channel and gets cancelled, and then a sender task gets scheduled, won't it try to reschedule that cancelled task with the value to be sent? |
@agronholm when |
I see. But since AnyIO does not have the same low level facilities at its disposal, I will have to think of something else. |
Maybe you could detect when you've been simultaneously awoken by a cancellation + receiving an object to return, and "undo" the cancellation so the operation completes successfully? |
@njsmith I thought about that, but I wasn't sure that it's correct to ignore the cancellation. I guess if your 'simultaneously awoken by two things' interpretation is correct, then simply picking one of them is legit. Here's an implementation: diff --git a/src/anyio/streams/memory.py b/src/anyio/streams/memory.py
index 9b8e8ee..6dc08f1 100644
--- a/src/anyio/streams/memory.py
+++ b/src/anyio/streams/memory.py
@@ -69,7 +69,11 @@ class MemoryObjectReceiveStream(Generic[T_Item], ObjectReceiveStream[T_Item]):
try:
await receive_event.wait()
- except BaseException:
+ except BaseException as e:
+ if type(e) == anyio.get_cancelled_exc_class() and container:
+ # We were simultaneously awoken by a cancellation and receiving an
+ # item. We choose the latter to ensure that items are not dropped.
+ return container[0]
self._state.waiting_receivers.pop(receive_event, None)
raise It seems to work on the asyncio and Trio backends, but not Curio. |
Curio is raising |
That really rubs me the wrong way. I've opened a PR with an alternative fix. |
This issue concerns
master@23803be
Memory object streams appear to randomly drop items in the following scenario (across all backends):
Whereas the equivalent Trio code works as expected (every item is received and printed):
The text was updated successfully, but these errors were encountered: