| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/async_socket_io_handler.h" |
| |
| #include <fcntl.h> |
| |
| #include "base/posix/eintr_wrapper.h" |
| |
| namespace base { |
| |
| AsyncSocketIoHandler::AsyncSocketIoHandler() |
| : socket_(base::SyncSocket::kInvalidHandle), |
| pending_buffer_(NULL), |
| pending_buffer_len_(0), |
| is_watching_(false) { |
| } |
| |
| AsyncSocketIoHandler::~AsyncSocketIoHandler() { |
| DCHECK(CalledOnValidThread()); |
| } |
| |
| void AsyncSocketIoHandler::OnFileCanReadWithoutBlocking(int socket) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK_EQ(socket, socket_); |
| DCHECK(!read_complete_.is_null()); |
| |
| if (pending_buffer_) { |
| int bytes_read = HANDLE_EINTR(read(socket_, pending_buffer_, |
| pending_buffer_len_)); |
| DCHECK_GE(bytes_read, 0); |
| pending_buffer_ = NULL; |
| pending_buffer_len_ = 0; |
| read_complete_.Run(bytes_read > 0 ? bytes_read : 0); |
| } else { |
| // We're getting notifications that we can read from the socket while |
| // we're not waiting for data. In order to not starve the message loop, |
| // let's stop watching the fd and restart the watch when Read() is called. |
| is_watching_ = false; |
| socket_watcher_.StopWatchingFileDescriptor(); |
| } |
| } |
| |
| bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(!read_complete_.is_null()); |
| DCHECK(!pending_buffer_); |
| |
| EnsureWatchingSocket(); |
| |
| int bytes_read = HANDLE_EINTR(read(socket_, buffer, buffer_len)); |
| if (bytes_read < 0) { |
| if (errno == EAGAIN) { |
| pending_buffer_ = buffer; |
| pending_buffer_len_ = buffer_len; |
| } else { |
| NOTREACHED() << "read(): " << errno; |
| return false; |
| } |
| } else { |
| read_complete_.Run(bytes_read); |
| } |
| return true; |
| } |
| |
| bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket, |
| const ReadCompleteCallback& callback) { |
| DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle); |
| |
| DetachFromThread(); |
| |
| socket_ = socket; |
| read_complete_ = callback; |
| |
| // SyncSocket is blocking by default, so let's convert it to non-blocking. |
| int value = fcntl(socket, F_GETFL); |
| if (!(value & O_NONBLOCK)) { |
| // Set the socket to be non-blocking so we can do async reads. |
| if (fcntl(socket, F_SETFL, O_NONBLOCK) == -1) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void AsyncSocketIoHandler::EnsureWatchingSocket() { |
| DCHECK(CalledOnValidThread()); |
| if (!is_watching_ && socket_ != base::SyncSocket::kInvalidHandle) { |
| is_watching_ = base::MessageLoopForIO::current()->WatchFileDescriptor( |
| socket_, true, base::MessageLoopForIO::WATCH_READ, |
| &socket_watcher_, this); |
| } |
| } |
| |
| } // namespace base. |