IOEventLoop.cpp revision a383c11bc5d56a3d245f5a915d3a8122db46b833
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "IOEventLoop.h"
18
19#include <event2/event.h>
20#include <fcntl.h>
21
22#include <android-base/logging.h>
23
24struct IOEvent {
25  IOEventLoop* loop;
26  event* e;
27  std::function<bool()> callback;
28  bool enabled;
29
30  IOEvent(IOEventLoop* loop, const std::function<bool()>& callback)
31      : loop(loop), e(nullptr), callback(callback), enabled(false) {}
32
33  ~IOEvent() {
34    if (e != nullptr) {
35      event_free(e);
36    }
37  }
38};
39
40IOEventLoop::IOEventLoop() : ebase_(nullptr), has_error_(false) {}
41
42IOEventLoop::~IOEventLoop() {
43  if (ebase_ != nullptr) {
44    event_base_free(ebase_);
45  }
46}
47
48bool IOEventLoop::EnsureInit() {
49  if (ebase_ == nullptr) {
50    ebase_ = event_base_new();
51    if (ebase_ == nullptr) {
52      LOG(ERROR) << "failed to call event_base_new()";
53      return false;
54    }
55  }
56  return true;
57}
58
59void IOEventLoop::EventCallbackFn(int, short, void* arg) {
60  IOEvent* e = static_cast<IOEvent*>(arg);
61  if (!e->callback()) {
62    e->loop->has_error_ = true;
63    e->loop->ExitLoop();
64  }
65}
66
67static bool MakeFdNonBlocking(int fd) {
68  int flags = fcntl(fd, F_GETFL, 0);
69  if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
70    PLOG(ERROR) << "fcntl() failed";
71    return false;
72  }
73  return true;
74}
75
76IOEventRef IOEventLoop::AddReadEvent(int fd,
77                                     const std::function<bool()>& callback) {
78  if (!MakeFdNonBlocking(fd)) {
79    return nullptr;
80  }
81  return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback);
82}
83
84IOEventRef IOEventLoop::AddWriteEvent(int fd,
85                                      const std::function<bool()>& callback) {
86  if (!MakeFdNonBlocking(fd)) {
87    return nullptr;
88  }
89  return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback);
90}
91
92bool IOEventLoop::AddSignalEvent(int sig,
93                                 const std::function<bool()>& callback) {
94  return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback) != nullptr;
95}
96
97bool IOEventLoop::AddSignalEvents(std::vector<int> sigs,
98                                  const std::function<bool()>& callback) {
99  for (auto sig : sigs) {
100    if (!AddSignalEvent(sig, callback)) {
101      return false;
102    }
103  }
104  return true;
105}
106
107bool IOEventLoop::AddPeriodicEvent(timeval duration,
108                                   const std::function<bool()>& callback) {
109  return AddEvent(-1, EV_PERSIST, &duration, callback) != nullptr;
110}
111
112IOEventRef IOEventLoop::AddEvent(int fd_or_sig, short events, timeval* timeout,
113                                 const std::function<bool()>& callback) {
114  if (!EnsureInit()) {
115    return nullptr;
116  }
117  std::unique_ptr<IOEvent> e(new IOEvent(this, callback));
118  e->e = event_new(ebase_, fd_or_sig, events, EventCallbackFn, e.get());
119  if (e->e == nullptr) {
120    LOG(ERROR) << "event_new() failed";
121    return nullptr;
122  }
123  if (event_add(e->e, timeout) != 0) {
124    LOG(ERROR) << "event_add() failed";
125    return nullptr;
126  }
127  e->enabled = true;
128  events_.push_back(std::move(e));
129  return events_.back().get();
130}
131
132bool IOEventLoop::RunLoop() {
133  if (event_base_dispatch(ebase_) == -1) {
134    LOG(ERROR) << "event_base_dispatch() failed";
135    return false;
136  }
137  if (has_error_) {
138    return false;
139  }
140  return true;
141}
142
143bool IOEventLoop::ExitLoop() {
144  if (event_base_loopbreak(ebase_) == -1) {
145    LOG(ERROR) << "event_base_loopbreak() failed";
146    return false;
147  }
148  return true;
149}
150
151bool IOEventLoop::DisableEvent(IOEventRef ref) {
152  if (ref->enabled) {
153    if (event_del(ref->e) != 0) {
154      LOG(ERROR) << "event_del() failed";
155      return false;
156    }
157    ref->enabled = false;
158  }
159  return true;
160}
161
162bool IOEventLoop::EnableEvent(IOEventRef ref) {
163  if (!ref->enabled) {
164    if (event_add(ref->e, nullptr) != 0) {
165      LOG(ERROR) << "event_add() failed";
166      return false;
167    }
168    ref->enabled = true;
169  }
170  return true;
171}
172
173bool IOEventLoop::DelEvent(IOEventRef ref) {
174  DisableEvent(ref);
175  IOEventLoop* loop = ref->loop;
176  for (auto it = loop->events_.begin(); it != loop->events_.end(); ++it) {
177    if (it->get() == ref) {
178      loop->events_.erase(it);
179      break;
180    }
181  }
182  return true;
183}
184