1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/async_socket_io_handler.h"
6
7#include <fcntl.h>
8
9#include "base/posix/eintr_wrapper.h"
10
11namespace base {
12
13AsyncSocketIoHandler::AsyncSocketIoHandler()
14    : socket_(base::SyncSocket::kInvalidHandle),
15      pending_buffer_(NULL),
16      pending_buffer_len_(0),
17      is_watching_(false) {
18}
19
20AsyncSocketIoHandler::~AsyncSocketIoHandler() {
21  DCHECK(CalledOnValidThread());
22}
23
24void AsyncSocketIoHandler::OnFileCanReadWithoutBlocking(int socket) {
25  DCHECK(CalledOnValidThread());
26  DCHECK_EQ(socket, socket_);
27  DCHECK(!read_complete_.is_null());
28
29  if (pending_buffer_) {
30    int bytes_read = HANDLE_EINTR(read(socket_, pending_buffer_,
31                                       pending_buffer_len_));
32    DCHECK_GE(bytes_read, 0);
33    pending_buffer_ = NULL;
34    pending_buffer_len_ = 0;
35    read_complete_.Run(bytes_read > 0 ? bytes_read : 0);
36  } else {
37    // We're getting notifications that we can read from the socket while
38    // we're not waiting for data.  In order to not starve the message loop,
39    // let's stop watching the fd and restart the watch when Read() is called.
40    is_watching_ = false;
41    socket_watcher_.StopWatchingFileDescriptor();
42  }
43}
44
45bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) {
46  DCHECK(CalledOnValidThread());
47  DCHECK(!read_complete_.is_null());
48  DCHECK(!pending_buffer_);
49
50  EnsureWatchingSocket();
51
52  int bytes_read = HANDLE_EINTR(read(socket_, buffer, buffer_len));
53  if (bytes_read < 0) {
54    if (errno == EAGAIN) {
55      pending_buffer_ = buffer;
56      pending_buffer_len_ = buffer_len;
57    } else {
58      NOTREACHED() << "read(): " << errno;
59      return false;
60    }
61  } else {
62    read_complete_.Run(bytes_read);
63  }
64  return true;
65}
66
67bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket,
68                                      const ReadCompleteCallback& callback) {
69  DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle);
70
71  DetachFromThread();
72
73  socket_ = socket;
74  read_complete_ = callback;
75
76  // SyncSocket is blocking by default, so let's convert it to non-blocking.
77  int value = fcntl(socket, F_GETFL);
78  if (!(value & O_NONBLOCK)) {
79    // Set the socket to be non-blocking so we can do async reads.
80    if (fcntl(socket, F_SETFL, O_NONBLOCK) == -1) {
81      NOTREACHED();
82      return false;
83    }
84  }
85
86  return true;
87}
88
89void AsyncSocketIoHandler::EnsureWatchingSocket() {
90  DCHECK(CalledOnValidThread());
91  if (!is_watching_ && socket_ != base::SyncSocket::kInvalidHandle) {
92    is_watching_ = base::MessageLoopForIO::current()->WatchFileDescriptor(
93        socket_, true, base::MessageLoopForIO::WATCH_READ,
94        &socket_watcher_, this);
95  }
96}
97
98}  // namespace base.
99