266 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright 2006, Brian Swetland <swetland@frotz.net>
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #define TRACE_TAG FDEVENT
 | |
| 
 | |
| #include "sysdeps.h"
 | |
| 
 | |
| #include <inttypes.h>
 | |
| 
 | |
| #include <android-base/logging.h>
 | |
| #include <android-base/stringprintf.h>
 | |
| #include <android-base/threads.h>
 | |
| 
 | |
| #include "adb_utils.h"
 | |
| #include "fdevent.h"
 | |
| #include "fdevent_epoll.h"
 | |
| 
 | |
| #if !defined(__linux__)
 | |
| #include "fdevent_poll.h"
 | |
| #endif
 | |
| 
 | |
| using namespace std::chrono_literals;
 | |
| using std::chrono::duration_cast;
 | |
| 
 | |
| void invoke_fde(struct fdevent* fde, unsigned events) {
 | |
|     if (auto f = std::get_if<fd_func>(&fde->func)) {
 | |
|         (*f)(fde->fd.get(), events, fde->arg);
 | |
|     } else if (auto f = std::get_if<fd_func2>(&fde->func)) {
 | |
|         (*f)(fde, events, fde->arg);
 | |
|     } else {
 | |
|         __builtin_unreachable();
 | |
|     }
 | |
| }
 | |
| 
 | |
| std::string dump_fde(const fdevent* fde) {
 | |
|     std::string state;
 | |
|     if (fde->state & FDE_READ) {
 | |
|         state += "R";
 | |
|     }
 | |
|     if (fde->state & FDE_WRITE) {
 | |
|         state += "W";
 | |
|     }
 | |
|     if (fde->state & FDE_ERROR) {
 | |
|         state += "E";
 | |
|     }
 | |
|     return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
 | |
|                                        state.c_str());
 | |
| }
 | |
| 
 | |
| fdevent* fdevent_context::Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg) {
 | |
|     CheckMainThread();
 | |
|     CHECK_GE(fd.get(), 0);
 | |
| 
 | |
|     int fd_num = fd.get();
 | |
| 
 | |
|     auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fdevent{});
 | |
|     CHECK(inserted);
 | |
| 
 | |
|     fdevent* fde = &it->second;
 | |
|     fde->id = fdevent_id_++;
 | |
|     fde->state = 0;
 | |
|     fde->fd = std::move(fd);
 | |
|     fde->func = func;
 | |
|     fde->arg = arg;
 | |
|     if (!set_file_block_mode(fde->fd, false)) {
 | |
|         // Here is not proper to handle the error. If it fails here, some error is
 | |
|         // likely to be detected by poll(), then we can let the callback function
 | |
|         // to handle it.
 | |
|         LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
 | |
|     }
 | |
| 
 | |
|     this->Register(fde);
 | |
|     return fde;
 | |
| }
 | |
| 
 | |
| unique_fd fdevent_context::Destroy(fdevent* fde) {
 | |
|     CheckMainThread();
 | |
|     if (!fde) {
 | |
|         return {};
 | |
|     }
 | |
| 
 | |
|     this->Unregister(fde);
 | |
| 
 | |
|     unique_fd fd = std::move(fde->fd);
 | |
| 
 | |
|     auto erased = this->installed_fdevents_.erase(fd.get());
 | |
|     CHECK_EQ(1UL, erased);
 | |
| 
 | |
|     return fd;
 | |
| }
 | |
| 
 | |
| void fdevent_context::Add(fdevent* fde, unsigned events) {
 | |
|     CHECK(!(events & FDE_TIMEOUT));
 | |
|     Set(fde, fde->state | events);
 | |
| }
 | |
| 
 | |
| void fdevent_context::Del(fdevent* fde, unsigned events) {
 | |
|     CHECK(!(events & FDE_TIMEOUT));
 | |
|     Set(fde, fde->state & ~events);
 | |
| }
 | |
| 
 | |
| void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
 | |
|     CheckMainThread();
 | |
|     fde->timeout = timeout;
 | |
|     fde->last_active = std::chrono::steady_clock::now();
 | |
| }
 | |
| 
 | |
| std::optional<std::chrono::milliseconds> fdevent_context::CalculatePollDuration() {
 | |
|     std::optional<std::chrono::milliseconds> result = std::nullopt;
 | |
|     auto now = std::chrono::steady_clock::now();
 | |
|     CheckMainThread();
 | |
| 
 | |
|     for (const auto& [fd, fde] : this->installed_fdevents_) {
 | |
|         UNUSED(fd);
 | |
|         auto timeout_opt = fde.timeout;
 | |
|         if (timeout_opt) {
 | |
|             auto deadline = fde.last_active + *timeout_opt;
 | |
|             auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
 | |
|             if (time_left < 0ms) {
 | |
|                 time_left = 0ms;
 | |
|             }
 | |
| 
 | |
|             if (!result) {
 | |
|                 result = time_left;
 | |
|             } else {
 | |
|                 result = std::min(*result, time_left);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| void fdevent_context::HandleEvents(const std::vector<fdevent_event>& events) {
 | |
|     for (const auto& event : events) {
 | |
|         invoke_fde(event.fde, event.events);
 | |
|     }
 | |
|     FlushRunQueue();
 | |
| }
 | |
| 
 | |
| void fdevent_context::FlushRunQueue() {
 | |
|     // We need to be careful around reentrancy here, since a function we call can queue up another
 | |
|     // function.
 | |
|     while (true) {
 | |
|         std::function<void()> fn;
 | |
|         {
 | |
|             std::lock_guard<std::mutex> lock(this->run_queue_mutex_);
 | |
|             if (this->run_queue_.empty()) {
 | |
|                 break;
 | |
|             }
 | |
|             fn = std::move(this->run_queue_.front());
 | |
|             this->run_queue_.pop_front();
 | |
|         }
 | |
|         fn();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void fdevent_context::CheckMainThread() {
 | |
|     if (main_thread_id_) {
 | |
|         CHECK_EQ(*main_thread_id_, android::base::GetThreadId());
 | |
|     }
 | |
| }
 | |
| 
 | |
| void fdevent_context::Run(std::function<void()> fn) {
 | |
|     {
 | |
|         std::lock_guard<std::mutex> lock(run_queue_mutex_);
 | |
|         run_queue_.push_back(std::move(fn));
 | |
|     }
 | |
| 
 | |
|     Interrupt();
 | |
| }
 | |
| 
 | |
| void fdevent_context::TerminateLoop() {
 | |
|     terminate_loop_ = true;
 | |
|     Interrupt();
 | |
| }
 | |
| 
 | |
| static std::unique_ptr<fdevent_context> fdevent_create_context() {
 | |
| #if defined(__linux__)
 | |
|     return std::make_unique<fdevent_context_epoll>();
 | |
| #else
 | |
|     return std::make_unique<fdevent_context_poll>();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static auto& g_ambient_fdevent_context() {
 | |
|     static auto context = fdevent_create_context().release();
 | |
|     return context;
 | |
| }
 | |
| 
 | |
| static fdevent_context* fdevent_get_ambient() {
 | |
|     return g_ambient_fdevent_context();
 | |
| }
 | |
| 
 | |
| fdevent* fdevent_create(int fd, fd_func func, void* arg) {
 | |
|     unique_fd ufd(fd);
 | |
|     return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
 | |
| }
 | |
| 
 | |
| fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
 | |
|     unique_fd ufd(fd);
 | |
|     return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
 | |
| }
 | |
| 
 | |
| unique_fd fdevent_release(fdevent* fde) {
 | |
|     return fdevent_get_ambient()->Destroy(fde);
 | |
| }
 | |
| 
 | |
| void fdevent_destroy(fdevent* fde) {
 | |
|     fdevent_get_ambient()->Destroy(fde);
 | |
| }
 | |
| 
 | |
| void fdevent_set(fdevent* fde, unsigned events) {
 | |
|     fdevent_get_ambient()->Set(fde, events);
 | |
| }
 | |
| 
 | |
| void fdevent_add(fdevent* fde, unsigned events) {
 | |
|     fdevent_get_ambient()->Add(fde, events);
 | |
| }
 | |
| 
 | |
| void fdevent_del(fdevent* fde, unsigned events) {
 | |
|     fdevent_get_ambient()->Del(fde, events);
 | |
| }
 | |
| 
 | |
| void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
 | |
|     fdevent_get_ambient()->SetTimeout(fde, timeout);
 | |
| }
 | |
| 
 | |
| void fdevent_run_on_main_thread(std::function<void()> fn) {
 | |
|     fdevent_get_ambient()->Run(std::move(fn));
 | |
| }
 | |
| 
 | |
| void fdevent_loop() {
 | |
|     fdevent_get_ambient()->Loop();
 | |
| }
 | |
| 
 | |
| void check_main_thread() {
 | |
|     fdevent_get_ambient()->CheckMainThread();
 | |
| }
 | |
| 
 | |
| void fdevent_terminate_loop() {
 | |
|     fdevent_get_ambient()->TerminateLoop();
 | |
| }
 | |
| 
 | |
| size_t fdevent_installed_count() {
 | |
|     return fdevent_get_ambient()->InstalledCount();
 | |
| }
 | |
| 
 | |
| void fdevent_reset() {
 | |
|     auto old = std::exchange(g_ambient_fdevent_context(), fdevent_create_context().release());
 | |
|     delete old;
 | |
| }
 |