cam: event_loop: Execute events in the libevent loop

Cam uses libevent to deal with threads and idle loop while still
implementing its own event queue. This creates issues when the event
loop is terminated as it might get stuck in the idle loop if exit() is
called while the thread is busy with dispatchCalls().

Solve this by removing the custom event execution and instead injecting
the calls as events to the base event loop.

Reported-by: Sebastian Fricke <sebastian.fricke@posteo.net>
Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Niklas Söderlund
2021-01-30 00:52:54 +01:00
parent 297c9c3fa0
commit 5b60b689c1
2 changed files with 27 additions and 30 deletions

View File

@@ -38,25 +38,13 @@ EventLoop *EventLoop::instance()
int EventLoop::exec()
{
exitCode_ = -1;
exit_.store(false, std::memory_order_release);
while (!exit_.load(std::memory_order_acquire)) {
dispatchCalls();
event_base_loop(base_, EVLOOP_NO_EXIT_ON_EMPTY);
}
event_base_loop(base_, EVLOOP_NO_EXIT_ON_EMPTY);
return exitCode_;
}
void EventLoop::exit(int code)
{
exitCode_ = code;
exit_.store(true, std::memory_order_release);
interrupt();
}
void EventLoop::interrupt()
{
event_base_loopbreak(base_);
}
@@ -67,20 +55,28 @@ void EventLoop::callLater(const std::function<void()> &func)
calls_.push_back(func);
}
interrupt();
event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr);
}
void EventLoop::dispatchCalls()
void EventLoop::dispatchCallback([[maybe_unused]] evutil_socket_t fd,
[[maybe_unused]] short flags, void *param)
{
std::unique_lock<std::mutex> locker(lock_);
for (auto iter = calls_.begin(); iter != calls_.end(); ) {
std::function<void()> call = std::move(*iter);
iter = calls_.erase(iter);
locker.unlock();
call();
locker.lock();
}
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();
}

View File

@@ -7,11 +7,12 @@
#ifndef __CAM_EVENT_LOOP_H__
#define __CAM_EVENT_LOOP_H__
#include <atomic>
#include <functional>
#include <list>
#include <mutex>
#include <event2/util.h>
struct event_base;
class EventLoop
@@ -31,14 +32,14 @@ private:
static EventLoop *instance_;
struct event_base *base_;
std::atomic<bool> exit_;
int exitCode_;
std::list<std::function<void()>> calls_;
std::mutex lock_;
void interrupt();
void dispatchCalls();
static void dispatchCallback(evutil_socket_t fd, short flags,
void *param);
void dispatchCall();
};
#endif /* __CAM_EVENT_LOOP_H__ */