[RFC PATCH v3 07/21] apps: common: event_loop: Make it possible to exit with exception
Jacopo Mondi
jacopo.mondi at ideasonboard.com
Tue Feb 4 14:34:55 CET 2025
Hi Barnabás
On Thu, Jan 30, 2025 at 11:50:36AM +0000, Barnabás Pőcze wrote:
> Instead of taking a single integer in `EventLoop:exit()`, change it
> so that an `std::exception_ptr` can also be passed. And in `EventLoop::exec()`
> rethrow the stored exception (if any).
>
> Furthermore, catch exceptions from deferred calls, and stop the event
> loop with the caught exception.
>
> Signed-off-by: Barnabás Pőcze <pobrn at protonmail.com>
> ---
> src/apps/common/event_loop.cpp | 35 +++++++++++++++++++++++++++++-----
> src/apps/common/event_loop.h | 6 ++++--
> 2 files changed, 34 insertions(+), 7 deletions(-)
>
> diff --git a/src/apps/common/event_loop.cpp b/src/apps/common/event_loop.cpp
> index 0d7a4a024..13c2d6057 100644
> --- a/src/apps/common/event_loop.cpp
> +++ b/src/apps/common/event_loop.cpp
> @@ -12,6 +12,7 @@
> #include <event2/thread.h>
> #include <algorithm>
> #include <iostream>
> +#include <utility>
>
> EventLoop *EventLoop::instance_ = nullptr;
>
> @@ -38,7 +39,14 @@ EventLoop::EventLoop()
> self->calls_.pop_front();
> }
>
> - call();
> + try {
> + call();
> + }
> + catch (...) {
we don't have a preferred syntax for try {} catch () {}
but I feel like it should probably be
try {
} catch () {
}
to match how we handle if() branches ?
> + ::event_active(self->callsTrigger_, 0, 0);
> + self->exit(std::current_exception());
> + break;
> + }
> }
> }, this);
> assert(callsTrigger_);
> @@ -63,14 +71,31 @@ EventLoop *EventLoop::instance()
>
> int EventLoop::exec()
> {
> - exitCode_ = -1;
> + {
> + std::lock_guard locker(lock_);
> + result_ = 0;
> + }
> +
> event_base_loop(base_, EVLOOP_NO_EXIT_ON_EMPTY);
> - return exitCode_;
> +
> + auto result = [&] {
> + std::lock_guard locker(lock_);
> + return std::exchange(result_, 0);
> + }();
> +
> + if (auto *exc = std::get_if<std::exception_ptr>(&result))
> + std::rethrow_exception(std::move(*exc));
> +
> + return std::get<int>(result);
All of this is morbidly fascinating as only C++ can be.
I understand this whole patch is only needed to support the following
hunk in 19/21
- if (queueRequest(request))
- loop_->exit(-EINVAL);
+ queueRequest(request);
iow to remove the need for explicit loop interruptions but to
propagate the exceptions thrown by gtest ASSERT_EQ() and friends in
the even loop and exit there.
All of this is neat but I wonder if the complexity of this patch is
necessary just to support that single use case. Maybe it's complex for
me but not for others, I'll ask their opinion.
> }
>
> -void EventLoop::exit(int code)
> +void EventLoop::exit(std::variant<int, std::exception_ptr> result)
> {
> - exitCode_ = code;
> + {
> + std::lock_guard locker(lock_);
> + result_ = std::move(result);
> + }
> +
> event_base_loopbreak(base_);
> }
>
> diff --git a/src/apps/common/event_loop.h b/src/apps/common/event_loop.h
> index 6d7d0497a..380c3483a 100644
> --- a/src/apps/common/event_loop.h
> +++ b/src/apps/common/event_loop.h
> @@ -9,11 +9,13 @@
>
> #include <chrono>
> #include <deque>
> +#include <exception>
> #include <functional>
> #include <list>
> #include <memory>
> #include <mutex>
> #include <optional>
> +#include <variant>
>
> #include <libcamera/base/class.h>
>
> @@ -35,7 +37,7 @@ public:
> static EventLoop *instance();
>
> int exec();
> - void exit(int code = 0);
> + void exit(std::variant<int, std::exception_ptr> result = 0);
>
> void callLater(std::function<void()> &&func, std::optional<std::uintptr_t> cookie = {});
> void cancelLater(std::optional<std::uintptr_t> cookie = {});
> @@ -63,7 +65,7 @@ private:
> static EventLoop *instance_;
>
> struct event_base *base_;
> - int exitCode_;
> + std::variant<int, std::exception_ptr> result_ = 0;
>
> std::deque<std::pair<std::function<void()>, std::optional<std::uintptr_t>>> calls_;
> ::event *callsTrigger_ = nullptr;
> --
> 2.48.1
>
>
More information about the libcamera-devel
mailing list