Skip to content
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

send_message() race condition #12

Closed
belm0 opened this issue Sep 3, 2018 · 3 comments
Closed

send_message() race condition #12

belm0 opened this issue Sep 3, 2018 · 3 comments
Assignees

Comments

@belm0
Copy link
Member

belm0 commented Sep 3, 2018

This race condition causes send_message() to return even though message is not sent, and _writer_task() to block even though there is a pending message from wsproto.

  1. send_message() "a" hands data to wsproto, sets _data_pending, blocks on _data_sent
  2. wsproto queues data "a" for bytes_to_send
  3. _writer_task() receives _data_pending event and calls bytes_to_send(), getting data "a"
  4. send_message() "b" hands data to wsproto, sets _data_pending (already set), blocks on _data_sent
  5. wsproto queues data "b" for bytes_to_send
  6. _writer_task() sends message "a" to stream, clears _data_pending, sets _data_sent
  7. send_message() "a" exits
  8. send_message() "b" exits even though message has not been sent
  9. _writer_task() blocks on _data_pending, even though wsproto has message "b" pending

send_message():
https://github.com/HyperionGray/trio-websocket/blob/b787bf1a8a026ef1d9ca995d044bc97d42e7f727/trio_websocket/__init__.py#L192-L195

_writer_task():
https://github.com/HyperionGray/trio-websocket/blob/b787bf1a8a026ef1d9ca995d044bc97d42e7f727/trio_websocket/__init__.py#L299-L306

As part of this fix, it would be great to set up pytest and have an explicit test for this case.

@belm0
Copy link
Member Author

belm0 commented Sep 3, 2018

this needs some review of the API and design

my quick fix was to loop on bytes_to_send():

            while True:
                data = self._wsproto.bytes_to_send()
                if len(data) > 0:
                    await self._stream.send_all(data)
                else:
                    break

However, if there is a high enough rate of send_message() calls, we can never exit the loop and clear _data_sent.

Perhaps send_message() could be left synchronous (i.e. don't use _data_sent), giving up on back pressure. Otherwise, given that wsproto can aggregate messages, I'm not sure how to emit a data sent signal at send_message() granularity. (Granted, I don't know the wsproto API.)

@njsmith
Copy link
Member

njsmith commented Sep 3, 2018

Whoops, I never did file this here, did I? It's mentioned here: python-trio/trio#637

I think the fix is to get rid of the writer task entirely, which would simplify things for other reasons too: #3

belm0 added a commit to belm0/trio-websocket that referenced this issue Sep 3, 2018
@mehaase
Copy link
Contributor

mehaase commented Sep 4, 2018

An earlier version of this library had no writer task, and I thought I was simplifying things by adding one! It should be easy to revert.

belm0 pushed a commit to belm0/trio-websocket that referenced this issue Sep 4, 2018
@mehaase mehaase self-assigned this Sep 4, 2018
@mehaase mehaase closed this as completed in db2c17e Sep 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants