Make PhutilChannel::waitForAny more reasonable about detecting unselectable channels
Summary:
Ref T4189. Currently, channel getWriteSockets() methods return only blocked write sockets, and waitForAny() degrades to a busy wait if any channels are missing read and write sockets, assuming the channel is not a socket-based channel.
This means that if you have an unblocked, writable, socket-based channel and a blocked, readable socket-based channel, we always degrade to busy wait immediately.
An example of this occurs during git push:
- We have one R/W channel through SSH to the git push on the user's computer.
- We have one R/W channel through ExecFuture to the git receive-pack on the remote host.
- When the client finishes writing, we close the "R" half of the SSH channel. This leaves it writable, socket-based, and unblocked for most of the push.
Instead, make getWriteSockets() methods return all write sockets, even if they're not blocked, and then ignore them if isWriteBufferEmpty() says we don't need to wait for them.
This is generally cleaner, and fixes several issues:
- The ssh-exec process no longer sits in a busy wait between the SSH read channel closing and the operation finishing.
- The stdin/stdout/stderr log in the SSH stuff can be simplified (next diff).
Test Plan: Ran git push of the Wine repository, saw nothing we own spend an unreasonable amount of time doing CPU stuff. Added a bunch of logging to confirm things are working reasonably under the hood.
Reviewers: btrahan, zeeg
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4189
Differential Revision: https://secure.phabricator.com/D7769