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 
24 struct IOEvent {
25   IOEventLoop* loop;
26   event* e;
27   std::function<bool()> callback;
28   bool enabled;
29 
IOEventIOEvent30   IOEvent(IOEventLoop* loop, const std::function<bool()>& callback)
31       : loop(loop), e(nullptr), callback(callback), enabled(false) {}
32 
~IOEventIOEvent33   ~IOEvent() {
34     if (e != nullptr) {
35       event_free(e);
36     }
37   }
38 };
39 
IOEventLoop()40 IOEventLoop::IOEventLoop() : ebase_(nullptr), has_error_(false) {}
41 
~IOEventLoop()42 IOEventLoop::~IOEventLoop() {
43   events_.clear();
44   if (ebase_ != nullptr) {
45     event_base_free(ebase_);
46   }
47 }
48 
EnsureInit()49 bool IOEventLoop::EnsureInit() {
50   if (ebase_ == nullptr) {
51     ebase_ = event_base_new();
52     if (ebase_ == nullptr) {
53       LOG(ERROR) << "failed to call event_base_new()";
54       return false;
55     }
56   }
57   return true;
58 }
59 
EventCallbackFn(int,short,void * arg)60 void IOEventLoop::EventCallbackFn(int, short, void* arg) {
61   IOEvent* e = static_cast<IOEvent*>(arg);
62   if (!e->callback()) {
63     e->loop->has_error_ = true;
64     e->loop->ExitLoop();
65   }
66 }
67 
MakeFdNonBlocking(int fd)68 static bool MakeFdNonBlocking(int fd) {
69   int flags = fcntl(fd, F_GETFL, 0);
70   if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
71     PLOG(ERROR) << "fcntl() failed";
72     return false;
73   }
74   return true;
75 }
76 
AddReadEvent(int fd,const std::function<bool ()> & callback)77 IOEventRef IOEventLoop::AddReadEvent(int fd,
78                                      const std::function<bool()>& callback) {
79   if (!MakeFdNonBlocking(fd)) {
80     return nullptr;
81   }
82   return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback);
83 }
84 
AddWriteEvent(int fd,const std::function<bool ()> & callback)85 IOEventRef IOEventLoop::AddWriteEvent(int fd,
86                                       const std::function<bool()>& callback) {
87   if (!MakeFdNonBlocking(fd)) {
88     return nullptr;
89   }
90   return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback);
91 }
92 
AddSignalEvent(int sig,const std::function<bool ()> & callback)93 bool IOEventLoop::AddSignalEvent(int sig,
94                                  const std::function<bool()>& callback) {
95   return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback) != nullptr;
96 }
97 
AddSignalEvents(std::vector<int> sigs,const std::function<bool ()> & callback)98 bool IOEventLoop::AddSignalEvents(std::vector<int> sigs,
99                                   const std::function<bool()>& callback) {
100   for (auto sig : sigs) {
101     if (!AddSignalEvent(sig, callback)) {
102       return false;
103     }
104   }
105   return true;
106 }
107 
AddPeriodicEvent(timeval duration,const std::function<bool ()> & callback)108 bool IOEventLoop::AddPeriodicEvent(timeval duration,
109                                    const std::function<bool()>& callback) {
110   return AddEvent(-1, EV_PERSIST, &duration, callback) != nullptr;
111 }
112 
AddEvent(int fd_or_sig,short events,timeval * timeout,const std::function<bool ()> & callback)113 IOEventRef IOEventLoop::AddEvent(int fd_or_sig, short events, timeval* timeout,
114                                  const std::function<bool()>& callback) {
115   if (!EnsureInit()) {
116     return nullptr;
117   }
118   std::unique_ptr<IOEvent> e(new IOEvent(this, callback));
119   e->e = event_new(ebase_, fd_or_sig, events, EventCallbackFn, e.get());
120   if (e->e == nullptr) {
121     LOG(ERROR) << "event_new() failed";
122     return nullptr;
123   }
124   if (event_add(e->e, timeout) != 0) {
125     LOG(ERROR) << "event_add() failed";
126     return nullptr;
127   }
128   e->enabled = true;
129   events_.push_back(std::move(e));
130   return events_.back().get();
131 }
132 
RunLoop()133 bool IOEventLoop::RunLoop() {
134   if (event_base_dispatch(ebase_) == -1) {
135     LOG(ERROR) << "event_base_dispatch() failed";
136     return false;
137   }
138   if (has_error_) {
139     return false;
140   }
141   return true;
142 }
143 
ExitLoop()144 bool IOEventLoop::ExitLoop() {
145   if (event_base_loopbreak(ebase_) == -1) {
146     LOG(ERROR) << "event_base_loopbreak() failed";
147     return false;
148   }
149   return true;
150 }
151 
DisableEvent(IOEventRef ref)152 bool IOEventLoop::DisableEvent(IOEventRef ref) {
153   if (ref->enabled) {
154     if (event_del(ref->e) != 0) {
155       LOG(ERROR) << "event_del() failed";
156       return false;
157     }
158     ref->enabled = false;
159   }
160   return true;
161 }
162 
EnableEvent(IOEventRef ref)163 bool IOEventLoop::EnableEvent(IOEventRef ref) {
164   if (!ref->enabled) {
165     if (event_add(ref->e, nullptr) != 0) {
166       LOG(ERROR) << "event_add() failed";
167       return false;
168     }
169     ref->enabled = true;
170   }
171   return true;
172 }
173 
DelEvent(IOEventRef ref)174 bool IOEventLoop::DelEvent(IOEventRef ref) {
175   DisableEvent(ref);
176   IOEventLoop* loop = ref->loop;
177   for (auto it = loop->events_.begin(); it != loop->events_.end(); ++it) {
178     if (it->get() == ref) {
179       loop->events_.erase(it);
180       break;
181     }
182   }
183   return true;
184 }
185