| Josh Gao | 95068bb | 2019-07-08 15:23:17 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright 2006, Brian Swetland <swetland@frotz.net> | 
|  | 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 | */ | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 16 |  | 
| Yabin Cui | aed3c61 | 2015-09-22 15:52:57 -0700 | [diff] [blame] | 17 | #define TRACE_TAG FDEVENT | 
| JP Abgrall | 408fa57 | 2011-03-16 15:57:42 -0700 | [diff] [blame] | 18 |  | 
| Dan Albert | 3313426 | 2015-03-19 15:21:08 -0700 | [diff] [blame] | 19 | #include "sysdeps.h" | 
| Dan Albert | 3313426 | 2015-03-19 15:21:08 -0700 | [diff] [blame] | 20 |  | 
| Josh Gao | ded557f | 2018-06-18 14:55:27 -0700 | [diff] [blame] | 21 | #include <inttypes.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 22 |  | 
| Josh Gao | 2c95bf7 | 2019-07-08 18:05:16 -0700 | [diff] [blame] | 23 | #include <android-base/logging.h> | 
| Elliott Hughes | 4f71319 | 2015-12-04 22:00:26 -0800 | [diff] [blame] | 24 | #include <android-base/stringprintf.h> | 
| Josh Gao | 2c95bf7 | 2019-07-08 18:05:16 -0700 | [diff] [blame] | 25 | #include <android-base/threads.h> | 
| Yabin Cui | a108016 | 2015-09-04 16:19:56 -0700 | [diff] [blame] | 26 |  | 
| Josh Gao | 33944a2 | 2019-07-08 18:16:19 -0700 | [diff] [blame] | 27 | #include "adb_utils.h" | 
| Josh Gao | 95068bb | 2019-07-08 15:23:17 -0700 | [diff] [blame] | 28 | #include "fdevent.h" | 
| Josh Gao | b43ad44 | 2019-07-11 13:47:44 -0700 | [diff] [blame] | 29 | #include "fdevent_epoll.h" | 
| Josh Gao | 95068bb | 2019-07-08 15:23:17 -0700 | [diff] [blame] | 30 | #include "fdevent_poll.h" | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 31 |  | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 32 | using namespace std::chrono_literals; | 
|  | 33 | using std::chrono::duration_cast; | 
|  | 34 |  | 
|  | 35 | void invoke_fde(struct fdevent* fde, unsigned events) { | 
|  | 36 | if (auto f = std::get_if<fd_func>(&fde->func)) { | 
|  | 37 | (*f)(fde->fd.get(), events, fde->arg); | 
|  | 38 | } else if (auto f = std::get_if<fd_func2>(&fde->func)) { | 
|  | 39 | (*f)(fde, events, fde->arg); | 
|  | 40 | } else { | 
|  | 41 | __builtin_unreachable(); | 
|  | 42 | } | 
|  | 43 | } | 
|  | 44 |  | 
| Josh Gao | 95068bb | 2019-07-08 15:23:17 -0700 | [diff] [blame] | 45 | std::string dump_fde(const fdevent* fde) { | 
| Yabin Cui | a108016 | 2015-09-04 16:19:56 -0700 | [diff] [blame] | 46 | std::string state; | 
| Yabin Cui | a108016 | 2015-09-04 16:19:56 -0700 | [diff] [blame] | 47 | if (fde->state & FDE_READ) { | 
|  | 48 | state += "R"; | 
|  | 49 | } | 
|  | 50 | if (fde->state & FDE_WRITE) { | 
|  | 51 | state += "W"; | 
|  | 52 | } | 
|  | 53 | if (fde->state & FDE_ERROR) { | 
|  | 54 | state += "E"; | 
|  | 55 | } | 
| Josh Gao | ded557f | 2018-06-18 14:55:27 -0700 | [diff] [blame] | 56 | return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(), | 
|  | 57 | state.c_str()); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 58 | } | 
|  | 59 |  | 
| Josh Gao | 33944a2 | 2019-07-08 18:16:19 -0700 | [diff] [blame] | 60 | fdevent* fdevent_context::Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg) { | 
|  | 61 | CheckMainThread(); | 
|  | 62 | CHECK_GE(fd.get(), 0); | 
|  | 63 |  | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 64 | int fd_num = fd.get(); | 
|  | 65 |  | 
| Josh Gao | 33944a2 | 2019-07-08 18:16:19 -0700 | [diff] [blame] | 66 | fdevent* fde = new fdevent(); | 
|  | 67 | fde->id = fdevent_id_++; | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 68 | fde->state = 0; | 
| Josh Gao | 33944a2 | 2019-07-08 18:16:19 -0700 | [diff] [blame] | 69 | fde->fd = std::move(fd); | 
|  | 70 | fde->func = func; | 
|  | 71 | fde->arg = arg; | 
|  | 72 | if (!set_file_block_mode(fde->fd, false)) { | 
|  | 73 | // Here is not proper to handle the error. If it fails here, some error is | 
|  | 74 | // likely to be detected by poll(), then we can let the callback function | 
|  | 75 | // to handle it. | 
|  | 76 | LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get(); | 
|  | 77 | } | 
|  | 78 |  | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 79 | auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde); | 
|  | 80 | CHECK(inserted); | 
|  | 81 | UNUSED(it); | 
|  | 82 |  | 
| Josh Gao | 33944a2 | 2019-07-08 18:16:19 -0700 | [diff] [blame] | 83 | this->Register(fde); | 
|  | 84 | return fde; | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | unique_fd fdevent_context::Destroy(fdevent* fde) { | 
|  | 88 | CheckMainThread(); | 
|  | 89 | if (!fde) { | 
|  | 90 | return {}; | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | this->Unregister(fde); | 
|  | 94 |  | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 95 | auto erased = this->installed_fdevents_.erase(fde->fd.get()); | 
|  | 96 | CHECK_EQ(1UL, erased); | 
|  | 97 |  | 
| Josh Gao | 33944a2 | 2019-07-08 18:16:19 -0700 | [diff] [blame] | 98 | unique_fd result = std::move(fde->fd); | 
|  | 99 | delete fde; | 
|  | 100 | return result; | 
|  | 101 | } | 
|  | 102 |  | 
| Josh Gao | 35b2936 | 2019-07-08 18:17:41 -0700 | [diff] [blame] | 103 | void fdevent_context::Add(fdevent* fde, unsigned events) { | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 104 | CHECK(!(events & FDE_TIMEOUT)); | 
|  | 105 | Set(fde, fde->state | events); | 
| Josh Gao | 35b2936 | 2019-07-08 18:17:41 -0700 | [diff] [blame] | 106 | } | 
|  | 107 |  | 
|  | 108 | void fdevent_context::Del(fdevent* fde, unsigned events) { | 
|  | 109 | CHECK(!(events & FDE_TIMEOUT)); | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 110 | Set(fde, fde->state & ~events); | 
| Josh Gao | 35b2936 | 2019-07-08 18:17:41 -0700 | [diff] [blame] | 111 | } | 
|  | 112 |  | 
|  | 113 | void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) { | 
|  | 114 | CheckMainThread(); | 
|  | 115 | fde->timeout = timeout; | 
|  | 116 | fde->last_active = std::chrono::steady_clock::now(); | 
|  | 117 | } | 
|  | 118 |  | 
| Josh Gao | e22cb9f | 2019-07-11 16:35:37 -0700 | [diff] [blame] | 119 | std::optional<std::chrono::milliseconds> fdevent_context::CalculatePollDuration() { | 
|  | 120 | std::optional<std::chrono::milliseconds> result = std::nullopt; | 
|  | 121 | auto now = std::chrono::steady_clock::now(); | 
|  | 122 | CheckMainThread(); | 
|  | 123 |  | 
|  | 124 | for (const auto& [fd, fde] : this->installed_fdevents_) { | 
|  | 125 | UNUSED(fd); | 
|  | 126 | auto timeout_opt = fde->timeout; | 
|  | 127 | if (timeout_opt) { | 
|  | 128 | auto deadline = fde->last_active + *timeout_opt; | 
|  | 129 | auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now); | 
|  | 130 | if (time_left < 0ms) { | 
|  | 131 | time_left = 0ms; | 
|  | 132 | } | 
|  | 133 |  | 
|  | 134 | if (!result) { | 
|  | 135 | result = time_left; | 
|  | 136 | } else { | 
|  | 137 | result = std::min(*result, time_left); | 
|  | 138 | } | 
|  | 139 | } | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | return result; | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | void fdevent_context::HandleEvents(const std::vector<fdevent_event>& events) { | 
|  | 146 | for (const auto& event : events) { | 
|  | 147 | invoke_fde(event.fde, event.events); | 
|  | 148 | } | 
|  | 149 | FlushRunQueue(); | 
|  | 150 | } | 
|  | 151 |  | 
|  | 152 | void fdevent_context::FlushRunQueue() { | 
|  | 153 | // We need to be careful around reentrancy here, since a function we call can queue up another | 
|  | 154 | // function. | 
|  | 155 | while (true) { | 
|  | 156 | std::function<void()> fn; | 
|  | 157 | { | 
|  | 158 | std::lock_guard<std::mutex> lock(this->run_queue_mutex_); | 
|  | 159 | if (this->run_queue_.empty()) { | 
|  | 160 | break; | 
|  | 161 | } | 
|  | 162 | fn = std::move(this->run_queue_.front()); | 
|  | 163 | this->run_queue_.pop_front(); | 
|  | 164 | } | 
|  | 165 | fn(); | 
|  | 166 | } | 
|  | 167 | } | 
|  | 168 |  | 
| Josh Gao | 2c95bf7 | 2019-07-08 18:05:16 -0700 | [diff] [blame] | 169 | void fdevent_context::CheckMainThread() { | 
|  | 170 | if (main_thread_id_) { | 
|  | 171 | CHECK_EQ(*main_thread_id_, android::base::GetThreadId()); | 
|  | 172 | } | 
|  | 173 | } | 
|  | 174 |  | 
| Josh Gao | 95eef6b | 2019-07-08 17:37:23 -0700 | [diff] [blame] | 175 | void fdevent_context::Run(std::function<void()> fn) { | 
|  | 176 | { | 
|  | 177 | std::lock_guard<std::mutex> lock(run_queue_mutex_); | 
|  | 178 | run_queue_.push_back(std::move(fn)); | 
|  | 179 | } | 
|  | 180 |  | 
|  | 181 | Interrupt(); | 
|  | 182 | } | 
|  | 183 |  | 
| Josh Gao | ebaa348 | 2019-07-08 18:08:06 -0700 | [diff] [blame] | 184 | void fdevent_context::TerminateLoop() { | 
|  | 185 | terminate_loop_ = true; | 
|  | 186 | Interrupt(); | 
|  | 187 | } | 
|  | 188 |  | 
| Josh Gao | b43ad44 | 2019-07-11 13:47:44 -0700 | [diff] [blame] | 189 | static std::unique_ptr<fdevent_context> fdevent_create_context() { | 
|  | 190 | #if defined(__linux__) | 
|  | 191 | return std::make_unique<fdevent_context_epoll>(); | 
|  | 192 | #else | 
|  | 193 | return std::make_unique<fdevent_context_poll>(); | 
|  | 194 | #endif | 
|  | 195 | } | 
|  | 196 |  | 
| Josh Gao | 7adca93 | 2019-07-08 17:31:47 -0700 | [diff] [blame] | 197 | static auto& g_ambient_fdevent_context = | 
| Josh Gao | b43ad44 | 2019-07-11 13:47:44 -0700 | [diff] [blame] | 198 | *new std::unique_ptr<fdevent_context>(fdevent_create_context()); | 
| Josh Gao | c2cf121 | 2019-06-28 16:34:36 -0700 | [diff] [blame] | 199 |  | 
|  | 200 | static fdevent_context* fdevent_get_ambient() { | 
| Josh Gao | 7adca93 | 2019-07-08 17:31:47 -0700 | [diff] [blame] | 201 | return g_ambient_fdevent_context.get(); | 
| Josh Gao | c2cf121 | 2019-06-28 16:34:36 -0700 | [diff] [blame] | 202 | } | 
|  | 203 |  | 
| Josh Gao | c2cf121 | 2019-06-28 16:34:36 -0700 | [diff] [blame] | 204 | fdevent* fdevent_create(int fd, fd_func func, void* arg) { | 
|  | 205 | unique_fd ufd(fd); | 
|  | 206 | return fdevent_get_ambient()->Create(std::move(ufd), func, arg); | 
|  | 207 | } | 
|  | 208 |  | 
|  | 209 | fdevent* fdevent_create(int fd, fd_func2 func, void* arg) { | 
|  | 210 | unique_fd ufd(fd); | 
|  | 211 | return fdevent_get_ambient()->Create(std::move(ufd), func, arg); | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 | unique_fd fdevent_release(fdevent* fde) { | 
|  | 215 | return fdevent_get_ambient()->Destroy(fde); | 
|  | 216 | } | 
|  | 217 |  | 
|  | 218 | void fdevent_destroy(fdevent* fde) { | 
|  | 219 | fdevent_get_ambient()->Destroy(fde); | 
|  | 220 | } | 
|  | 221 |  | 
|  | 222 | void fdevent_set(fdevent* fde, unsigned events) { | 
|  | 223 | fdevent_get_ambient()->Set(fde, events); | 
|  | 224 | } | 
|  | 225 |  | 
|  | 226 | void fdevent_add(fdevent* fde, unsigned events) { | 
|  | 227 | fdevent_get_ambient()->Add(fde, events); | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | void fdevent_del(fdevent* fde, unsigned events) { | 
|  | 231 | fdevent_get_ambient()->Del(fde, events); | 
|  | 232 | } | 
|  | 233 |  | 
|  | 234 | void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) { | 
|  | 235 | fdevent_get_ambient()->SetTimeout(fde, timeout); | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | void fdevent_run_on_main_thread(std::function<void()> fn) { | 
|  | 239 | fdevent_get_ambient()->Run(std::move(fn)); | 
|  | 240 | } | 
|  | 241 |  | 
|  | 242 | void fdevent_loop() { | 
|  | 243 | fdevent_get_ambient()->Loop(); | 
|  | 244 | } | 
|  | 245 |  | 
|  | 246 | void check_main_thread() { | 
|  | 247 | fdevent_get_ambient()->CheckMainThread(); | 
|  | 248 | } | 
|  | 249 |  | 
| Josh Gao | 022d447 | 2016-02-10 14:49:00 -0800 | [diff] [blame] | 250 | void fdevent_terminate_loop() { | 
| Josh Gao | c2cf121 | 2019-06-28 16:34:36 -0700 | [diff] [blame] | 251 | fdevent_get_ambient()->TerminateLoop(); | 
| Josh Gao | 022d447 | 2016-02-10 14:49:00 -0800 | [diff] [blame] | 252 | } | 
|  | 253 |  | 
| Yabin Cui | c1b1f6f | 2015-09-15 16:27:09 -0700 | [diff] [blame] | 254 | size_t fdevent_installed_count() { | 
| Josh Gao | c2cf121 | 2019-06-28 16:34:36 -0700 | [diff] [blame] | 255 | return fdevent_get_ambient()->InstalledCount(); | 
| Yabin Cui | c1b1f6f | 2015-09-15 16:27:09 -0700 | [diff] [blame] | 256 | } | 
|  | 257 |  | 
|  | 258 | void fdevent_reset() { | 
| Josh Gao | b43ad44 | 2019-07-11 13:47:44 -0700 | [diff] [blame] | 259 | g_ambient_fdevent_context = fdevent_create_context(); | 
| Yabin Cui | c1b1f6f | 2015-09-15 16:27:09 -0700 | [diff] [blame] | 260 | } |