Index: chromeos/process_proxy/process_output_watcher.cc |
diff --git a/chromeos/process_proxy/process_output_watcher.cc b/chromeos/process_proxy/process_output_watcher.cc |
index 17c1c9459a77dea6d4cb816493e2a2c4050f643b..92868c0e931a2622b2458c6d608f9913b8bb251a 100644 |
--- a/chromeos/process_proxy/process_output_watcher.cc |
+++ b/chromeos/process_proxy/process_output_watcher.cc |
@@ -4,35 +4,20 @@ |
#include "chromeos/process_proxy/process_output_watcher.h" |
-#include <sys/ioctl.h> |
-#include <sys/select.h> |
-#include <unistd.h> |
- |
#include <algorithm> |
#include <cstdio> |
#include <cstring> |
- |
+#include "base/bind.h" |
+#include "base/location.h" |
#include "base/logging.h" |
#include "base/posix/eintr_wrapper.h" |
+#include "base/single_thread_task_runner.h" |
#include "base/third_party/icu/icu_utf.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/time/time.h" |
namespace { |
-void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) { |
- FD_ZERO(set); |
- if (out_fd != -1) |
- FD_SET(out_fd, set); |
- FD_SET(stop_fd, set); |
-} |
- |
-void CloseFd(int* fd) { |
- if (*fd >= 0) { |
- if (IGNORE_EINTR(close(*fd)) != 0) |
- DPLOG(WARNING) << "close fd " << *fd << " failed."; |
- } |
- *fd = -1; |
-} |
- |
// Gets byte size for a UTF8 character given it's leading byte. The character |
// size is encoded as number of leading '1' bits in the character's leading |
// byte. If the most significant bit is '0', the character is a valid ASCII |
@@ -55,87 +40,77 @@ size_t UTF8SizeFromLeadingByte(uint8 leading_byte) { |
namespace chromeos { |
-// static |
-bool ProcessOutputWatcher::VerifyFileDescriptor(int fd) { |
- return (fd >= 0) && (fd < FD_SETSIZE); |
-} |
- |
ProcessOutputWatcher::ProcessOutputWatcher( |
int out_fd, |
- int stop_fd, |
const ProcessOutputCallback& callback) |
: read_buffer_size_(0), |
- out_fd_(out_fd), |
- stop_fd_(stop_fd), |
- on_read_callback_(callback) { |
- CHECK(VerifyFileDescriptor(out_fd_)); |
- CHECK(VerifyFileDescriptor(stop_fd_)); |
- max_fd_ = std::max(out_fd_, stop_fd_); |
+ process_output_file_(out_fd), |
+ on_read_callback_(callback), |
+ weak_factory_(this) { |
+ CHECK_GE(out_fd, 0); |
// We want to be sure we will be able to add 0 at the end of the input, so -1. |
read_buffer_capacity_ = arraysize(read_buffer_) - 1; |
} |
+ProcessOutputWatcher::~ProcessOutputWatcher() {} |
+ |
void ProcessOutputWatcher::Start() { |
WatchProcessOutput(); |
- OnStop(); |
-} |
- |
-ProcessOutputWatcher::~ProcessOutputWatcher() { |
- CloseFd(&out_fd_); |
- CloseFd(&stop_fd_); |
} |
-void ProcessOutputWatcher::WatchProcessOutput() { |
- while (true) { |
- // This has to be reset with every watch cycle. |
- fd_set rfds; |
- DCHECK_GE(stop_fd_, 0); |
- InitReadFdSet(out_fd_, stop_fd_, &rfds); |
- |
- int select_result = |
- HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL)); |
+void ProcessOutputWatcher::OnFileCanReadWithoutBlocking(int fd) { |
+ DCHECK_EQ(process_output_file_.GetPlatformFile(), fd); |
- if (select_result < 0) { |
- DPLOG(WARNING) << "select failed"; |
- return; |
- } |
+ output_file_watcher_.StopWatchingFileDescriptor(); |
+ ReadFromFd(fd); |
+} |
- // Check if we were stopped. |
- if (FD_ISSET(stop_fd_, &rfds)) { |
- return; |
- } |
+void ProcessOutputWatcher::OnFileCanWriteWithoutBlocking(int fd) { |
+ NOTREACHED(); |
+} |
- if (out_fd_ != -1 && FD_ISSET(out_fd_, &rfds)) { |
- ReadFromFd(PROCESS_OUTPUT_TYPE_OUT, &out_fd_); |
- } |
- } |
+void ProcessOutputWatcher::WatchProcessOutput() { |
+ base::MessageLoopForIO::current()->WatchFileDescriptor( |
+ process_output_file_.GetPlatformFile(), false, |
+ base::MessageLoopForIO::WATCH_READ, &output_file_watcher_, this); |
} |
-void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) { |
+void ProcessOutputWatcher::ReadFromFd(int fd) { |
// We don't want to necessary read pipe until it is empty so we don't starve |
// other streams in case data is written faster than we read it. If there is |
// more than read_buffer_size_ bytes in pipe, it will be read in the next |
// iteration. |
DCHECK_GT(read_buffer_capacity_, read_buffer_size_); |
ssize_t bytes_read = |
- HANDLE_EINTR(read(*fd, |
- &read_buffer_[read_buffer_size_], |
+ HANDLE_EINTR(read(fd, &read_buffer_[read_buffer_size_], |
read_buffer_capacity_ - read_buffer_size_)); |
+ |
+ if (bytes_read > 0) { |
+ ReportOutput(PROCESS_OUTPUT_TYPE_OUT, bytes_read); |
+ |
+ // Delay next read to make the process less likely to flood IPC channel |
+ // when output is reported to terminal extension via terminalPrivate API |
+ // (which is the only client of this code). |
+ // TODO(tbarzic): Properly fix this!! Provide a mechanism for clients to |
+ // ack reported output and continue watching the process when ack is |
+ // received. https://crbug.com/398901 |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
+ FROM_HERE, base::Bind(&ProcessOutputWatcher::WatchProcessOutput, |
+ weak_factory_.GetWeakPtr()), |
+ base::TimeDelta::FromMilliseconds(10)); |
+ |
+ return; |
+ } |
+ |
if (bytes_read < 0) |
DPLOG(WARNING) << "read from buffer failed"; |
- if (bytes_read > 0) |
- ReportOutput(type, bytes_read); |
- |
// If there is nothing on the output the watched process has exited (slave end |
// of pty is closed). |
- if (bytes_read <= 0) { |
- // Slave pseudo terminal has been closed, we won't need master fd anymore. |
- CloseFd(fd); |
+ on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, ""); |
- // We have lost contact with the process, so report it. |
- on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, ""); |
- } |
+ // Cancel pending |WatchProcessOutput| calls. |
+ weak_factory_.InvalidateWeakPtrs(); |
} |
size_t ProcessOutputWatcher::OutputSizeWithoutIncompleteUTF8() { |
@@ -191,8 +166,4 @@ void ProcessOutputWatcher::ReportOutput(ProcessOutputType type, |
read_buffer_size_ -= output_to_report; |
} |
-void ProcessOutputWatcher::OnStop() { |
- delete this; |
-} |
- |
} // namespace chromeos |