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

tcp_forwarder: make sendall() blocking #1277

Merged
merged 1 commit into from
Nov 18, 2024
Merged

Conversation

irdkwmnsb
Copy link
Contributor

Consider side A to be the client (program ran on a host machine), B to be the server (ios device) and P to be python process.

When we have B streaming the data to A over P and A doesn't want to read the packets from it's socket - the kernel will keep them in a buffer. And when it fills up, next time P receives a packet from B it will block on line 122 because the buffer of socket P->A is full. And pymobiledevice3 hangs altogether and doesn't receive new connections.

While this change breaks some use cases I think it is generally better to not block io operations if the kernel buffer is full.

@doronz88
Copy link
Owner

Doesn't this just pass the blocking part into the sender endpoint instead of the receiver?

@doronz88
Copy link
Owner

Also, this is from python's documentation:

Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from bytes until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.

As I understand this, the sendall() is already blocking. How does this make any difference?

@irdkwmnsb
Copy link
Contributor Author

Also, this is from python's documentation:

Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from bytes until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.

As I understand this, the sendall() is already blocking. How does this make any difference?

As per the documentation many details are system-dependent.
On linux and Darwin the use case I described definitely blocks the operation and no more sockets are being opened or data is being sent until the client socket is being read from.

@irdkwmnsb
Copy link
Contributor Author

Also, this is from python's documentation:

Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from bytes until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.

As I understand this, the sendall() is already blocking. How does this make any difference?

There is no behavioural difference in send and sendall when it comes to blocking and waiting for kernel response.
The only difference is that sendall will try to send data until all data is sent. Since POSIX's write gives no guarantee to how many bytes will be definitely sent to the socket, sendall will repeatedly send data until all is written.
See https://stackoverflow.com/a/34252690

@irdkwmnsb
Copy link
Contributor Author

Doesn't this just pass the blocking part into the sender endpoint instead of the receiver?

And no, the connection will be closed upon a read/write error which will occur if the buffer is full and no data is being read.

@doronz88
Copy link
Owner

doronz88 commented Nov 17, 2024

Sorry for the misunderstanding. Just to clarify and understand correctly, you say that adding the sorrounding .setblocking(True) makes sure if that if the other endpoint doesn't trigger recv(), it will raise an error instead of waiting indifinetly?

@irdkwmnsb
Copy link
Contributor Author

Sorry for the misunderstanding. Just to clarify and understand correctly, you say that adding the sorrounding .setblocking(True) makes sure if that if the other endpoint doesn't trigger recv(), it will raise an error instead of waiting indifinetly?

If you try to send while blocking option is set for a specific file descriptor - the program will not return into user space if the other end is not reading data. In other words - if you add setBlocking(false) before sendAll and the other end is not reading data - it will trigger EWOULDBLOCK oserror, since the sockets are being switched into non-blocking mode right after their initialization in _handle_server_connection

Copy link
Owner

@doronz88 doronz88 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I see. Thanks for clarifying the matter

@doronz88 doronz88 merged commit 11cc5e1 into doronz88:master Nov 18, 2024
17 checks passed
@doronz88 doronz88 changed the title Remove blocking IO tcp_forwarder: make sendall() blocking Nov 18, 2024
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

Successfully merging this pull request may close these issues.

2 participants