| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
| /* | |
| * Copyright (C) 2019, Google Inc. | |
| * | |
| * event_loop.cpp - cam - Event loop | |
| */ | |
| #include "event_loop.h" | |
| #include <assert.h> | |
| #include <event2/event.h> | |
| #include <event2/thread.h> | |
| #include <iostream> | |
| EventLoop *EventLoop::instance_ = nullptr; | |
| EventLoop::EventLoop() | |
| { | |
| assert(!instance_); | |
| evthread_use_pthreads(); | |
| base_ = event_base_new(); | |
| instance_ = this; | |
| } | |
| EventLoop::~EventLoop() | |
| { | |
| instance_ = nullptr; | |
| events_.clear(); | |
| event_base_free(base_); | |
| libevent_global_shutdown(); | |
| } | |
| EventLoop *EventLoop::instance() | |
| { | |
| return instance_; | |
| } | |
| int EventLoop::exec() | |
| { | |
| exitCode_ = -1; | |
| event_base_loop(base_, EVLOOP_NO_EXIT_ON_EMPTY); | |
| return exitCode_; | |
| } | |
| void EventLoop::exit(int code) | |
| { | |
| exitCode_ = code; | |
| event_base_loopbreak(base_); | |
| } | |
| void EventLoop::callLater(const std::function<void()> &func) | |
| { | |
| { | |
| std::unique_lock<std::mutex> locker(lock_); | |
| calls_.push_back(func); | |
| } | |
| event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr); | |
| } | |
| void EventLoop::addFdEvent(int fd, EventType type, | |
| const std::function<void()> &callback) | |
| { | |
| std::unique_ptr<Event> event = std::make_unique<Event>(callback); | |
| short events = (type & Read ? EV_READ : 0) | |
| | (type & Write ? EV_WRITE : 0) | |
| | EV_PERSIST; | |
| event->event_ = event_new(base_, fd, events, &EventLoop::Event::dispatch, | |
| event.get()); | |
| if (!event->event_) { | |
| std::cerr << "Failed to create event for fd " << fd << std::endl; | |
| return; | |
| } | |
| int ret = event_add(event->event_, nullptr); | |
| if (ret < 0) { | |
| std::cerr << "Failed to add event for fd " << fd << std::endl; | |
| return; | |
| } | |
| events_.push_back(std::move(event)); | |
| } | |
| void EventLoop::addTimerEvent(const std::chrono::microseconds period, | |
| const std::function<void()> &callback) | |
| { | |
| std::unique_ptr<Event> event = std::make_unique<Event>(callback); | |
| event->event_ = event_new(base_, -1, EV_PERSIST, &EventLoop::Event::dispatch, | |
| event.get()); | |
| if (!event->event_) { | |
| std::cerr << "Failed to create timer event" << std::endl; | |
| return; | |
| } | |
| struct timeval tv; | |
| tv.tv_sec = period.count() / 1000000ULL; | |
| tv.tv_usec = period.count() % 1000000ULL; | |
| int ret = event_add(event->event_, &tv); | |
| if (ret < 0) { | |
| std::cerr << "Failed to add timer event" << std::endl; | |
| return; | |
| } | |
| events_.push_back(std::move(event)); | |
| } | |
| void EventLoop::dispatchCallback([[maybe_unused]] evutil_socket_t fd, | |
| [[maybe_unused]] short flags, void *param) | |
| { | |
| EventLoop *loop = static_cast<EventLoop *>(param); | |
| loop->dispatchCall(); | |
| } | |
| void EventLoop::dispatchCall() | |
| { | |
| std::function<void()> call; | |
| { | |
| std::unique_lock<std::mutex> locker(lock_); | |
| if (calls_.empty()) | |
| return; | |
| call = calls_.front(); | |
| calls_.pop_front(); | |
| } | |
| call(); | |
| } | |
| EventLoop::Event::Event(const std::function<void()> &callback) | |
| : callback_(callback), event_(nullptr) | |
| { | |
| } | |
| EventLoop::Event::~Event() | |
| { | |
| event_del(event_); | |
| event_free(event_); | |
| } | |
| void EventLoop::Event::dispatch([[maybe_unused]] int fd, | |
| [[maybe_unused]] short events, void *arg) | |
| { | |
| Event *event = static_cast<Event *>(arg); | |
| event->callback_(); | |
| } |