// 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.