From patchwork Wed Jan 23 08:59:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 345 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id F05B260B1B for ; Wed, 23 Jan 2019 09:59:28 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 796A923E for ; Wed, 23 Jan 2019 09:59:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548233968; bh=tOq3daofVJtoyVGMU7+wS2iGWp06621AVcof4maOSQ8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=gCBxu8TofLYIBHHjI4G/g2jqokow+hXe7M822+bxMilEEDzGhpRtBZYe9qieUKVzq CQMc66QmzTJ+tWB0VS/JY+Wk+MWBbWNkSYNCoE2pm695JPueQ88tfHx7vTEILL5ygV +CUsFhpKrsu1xPSB0Ez2t1nriLuUIrxHC++pg5IA= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Jan 2019 10:59:20 +0200 Message-Id: <20190123085923.12524-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> References: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/4] tests: Test event dispatcher interruption by signal X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 23 Jan 2019 08:59:29 -0000 Add a test to verify that the event dispatcher correctly restarts event processing when interrupted by a signal. The test currently fails as this feature isn't implemented. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- test/event-dispatcher.cpp | 73 +++++++++++++++++++++++++++++++++++++++ test/meson.build | 9 ++--- 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 test/event-dispatcher.cpp diff --git a/test/event-dispatcher.cpp b/test/event-dispatcher.cpp new file mode 100644 index 000000000000..06c2657f09d6 --- /dev/null +++ b/test/event-dispatcher.cpp @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event-dispatcher.cpp - Event dispatcher test + */ + +#include +#include +#include + +#include +#include +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class EventDispatcherTest : public Test +{ +protected: + static void sigAlarmHandler(int) + { + cout << "SIGALARM received" << endl; + } + + int init() + { + struct sigaction sa = {}; + sa.sa_handler = &sigAlarmHandler; + sigaction(SIGALRM, &sa, nullptr); + + return 0; + } + + int run() + { + EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); + Timer timer; + + /* Event processing interruption by signal. */ + struct timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + + timer.start(1000); + + struct itimerval itimer = {}; + itimer.it_value.tv_usec = 500000; + setitimer(ITIMER_REAL, &itimer, nullptr); + + dispatcher->processEvents(); + + struct timespec stop; + clock_gettime(CLOCK_MONOTONIC, &stop); + int duration = (stop.tv_sec - start.tv_sec) * 1000; + duration += (stop.tv_nsec - start.tv_nsec) / 1000000; + + if (abs(duration - 1000) > 50) { + cout << "Event processing restart test failed" << endl; + return TestFail; + } + + return TestPass; + } + + void cleanup() + { + } +}; + +TEST_REGISTER(EventDispatcherTest) diff --git a/test/meson.build b/test/meson.build index 8cdcae2c9e87..d515a716207e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -5,10 +5,11 @@ subdir('pipeline') subdir('v4l2_device') public_tests = [ - ['event', 'event.cpp'], - ['list-cameras', 'list-cameras.cpp'], - ['signal', 'signal.cpp'], - ['timer', 'timer.cpp'], + ['event', 'event.cpp'], + ['event-dispatcher', 'event-dispatcher.cpp'], + ['list-cameras', 'list-cameras.cpp'], + ['signal', 'signal.cpp'], + ['timer', 'timer.cpp'], ] internal_tests = [ From patchwork Wed Jan 23 08:59:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 346 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6633060C98 for ; Wed, 23 Jan 2019 09:59:29 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E15C023D for ; Wed, 23 Jan 2019 09:59:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548233969; bh=agpvYX4U9f4P8YjrOd42wLo6vxSKRnt8qWskjDqQIPs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=L+thkplDUnyoK1jHB61ETTBimwQGGWQ72Dyw1gwVLfoP5hYrnc3uMqOW5+iZlS84k ZHGBywjCs4MdrK9hEFz4nhhFYLH0EZnHiwnTmFVmdexBtlIy7IWyjGiz0ts73Lk2Ig 5Kvi9XmCbh9tA8m9wQYyVsatTKseIzQKc2JUmp9c= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Jan 2019 10:59:21 +0200 Message-Id: <20190123085923.12524-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> References: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/4] libcamera: event_dispatcher_poll: Handle interrupted ppoll() calls X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 23 Jan 2019 08:59:29 -0000 The ppoll() call can be interrupted if a signal is delivered. Handle the EINTR error code gracefully by restarting the call. This fixes the event-dispatcher test failure. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- src/libcamera/event_dispatcher_poll.cpp | 58 +++++++++++-------- src/libcamera/include/event_dispatcher_poll.h | 1 + 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp index eefac54ca6da..6e0609c34ddc 100644 --- a/src/libcamera/event_dispatcher_poll.cpp +++ b/src/libcamera/event_dispatcher_poll.cpp @@ -128,32 +128,11 @@ void EventDispatcherPoll::processEvents() for (auto notifier : notifiers_) pollfds.push_back({ notifier.first, notifier.second.events(), 0 }); - /* Compute the timeout. */ - Timer *nextTimer = !timers_.empty() ? timers_.front() : nullptr; - struct timespec timeout; - - if (nextTimer) { - clock_gettime(CLOCK_MONOTONIC, &timeout); - uint64_t now = timeout.tv_sec * 1000000000ULL + timeout.tv_nsec; - - if (nextTimer->deadline() > now) { - uint64_t delta = nextTimer->deadline() - now; - timeout.tv_sec = delta / 1000000000ULL; - timeout.tv_nsec = delta % 1000000000ULL; - } else { - timeout.tv_sec = 0; - timeout.tv_nsec = 0; - } - - LOG(Event, Debug) - << "timeout " << timeout.tv_sec << "." - << std::setfill('0') << std::setw(9) - << timeout.tv_nsec; - } - /* Wait for events and process notifiers and timers. */ - ret = ppoll(pollfds.data(), pollfds.size(), - nextTimer ? &timeout : nullptr, nullptr); + do { + ret = poll(&pollfds); + } while (ret == -1 && errno == EINTR); + if (ret < 0) { ret = -errno; LOG(Event, Warning) << "poll() failed with " << strerror(-ret); @@ -178,6 +157,35 @@ short EventDispatcherPoll::EventNotifierSetPoll::events() const return events; } +int EventDispatcherPoll::poll(std::vector *pollfds) +{ + /* Compute the timeout. */ + Timer *nextTimer = !timers_.empty() ? timers_.front() : nullptr; + struct timespec timeout; + + if (nextTimer) { + clock_gettime(CLOCK_MONOTONIC, &timeout); + uint64_t now = timeout.tv_sec * 1000000000ULL + timeout.tv_nsec; + + if (nextTimer->deadline() > now) { + uint64_t delta = nextTimer->deadline() - now; + timeout.tv_sec = delta / 1000000000ULL; + timeout.tv_nsec = delta % 1000000000ULL; + } else { + timeout.tv_sec = 0; + timeout.tv_nsec = 0; + } + + LOG(Event, Debug) + << "timeout " << timeout.tv_sec << "." + << std::setfill('0') << std::setw(9) + << timeout.tv_nsec; + } + + return ppoll(pollfds->data(), pollfds->size(), + nextTimer ? &timeout : nullptr, nullptr); +} + void EventDispatcherPoll::processNotifiers(const std::vector &pollfds) { static const struct { diff --git a/src/libcamera/include/event_dispatcher_poll.h b/src/libcamera/include/event_dispatcher_poll.h index a41926e11a11..ac3efde082b4 100644 --- a/src/libcamera/include/event_dispatcher_poll.h +++ b/src/libcamera/include/event_dispatcher_poll.h @@ -41,6 +41,7 @@ private: std::map notifiers_; std::list timers_; + int poll(std::vector *pollfds); void processNotifiers(const std::vector &pollfds); void processTimers(); }; From patchwork Wed Jan 23 08:59:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 347 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C811F60C9C for ; Wed, 23 Jan 2019 09:59:29 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5824623E for ; Wed, 23 Jan 2019 09:59:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548233969; bh=jGwEGD2Js39ZX+865l7YI72a5zJN+1GNZuOluD2WQac=; h=From:To:Subject:Date:In-Reply-To:References:From; b=jlwf+XRUlAImna14Y53dQFK8YvhZNkq1sPPWFDvQSOqQM0tVQnw+AoUiiqxp3yV4y Rfc1v6KhELT3pfRWZX9xZZDQ5p240zEFfG3ov/ZfaMmA+0p/MzHLQxAsy0WEMibyA8 v6jHbynKp95y6EDcgPhGklPH4UiwuwUZY+ot2tug= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Jan 2019 10:59:22 +0200 Message-Id: <20190123085923.12524-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> References: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/4] libcamera: event_dispatcher: Add interrupt() function X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 23 Jan 2019 08:59:30 -0000 The new interrupt() function allows interrupting in-progress blocking processEvents() calls. This is useful to stop running event loops. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- include/libcamera/event_dispatcher.h | 2 ++ src/libcamera/event_dispatcher.cpp | 10 ++++++ src/libcamera/event_dispatcher_poll.cpp | 32 ++++++++++++++++++- src/libcamera/include/event_dispatcher_poll.h | 3 ++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/libcamera/event_dispatcher.h b/include/libcamera/event_dispatcher.h index c20518c6866d..cb06bf208e12 100644 --- a/include/libcamera/event_dispatcher.h +++ b/include/libcamera/event_dispatcher.h @@ -26,6 +26,8 @@ public: virtual void unregisterTimer(Timer *timer) = 0; virtual void processEvents() = 0; + + virtual void interrupt() = 0; }; } /* namespace libcamera */ diff --git a/src/libcamera/event_dispatcher.cpp b/src/libcamera/event_dispatcher.cpp index f7c40734095e..b82c59c3f5dc 100644 --- a/src/libcamera/event_dispatcher.cpp +++ b/src/libcamera/event_dispatcher.cpp @@ -104,4 +104,14 @@ EventDispatcher::~EventDispatcher() * it before returning. */ +/** + * \fn EventDispatcher::interrupt() + * \brief Interrupt any running processEvents() call as soon as possible + * + * Calling this function interrupts any blocking processEvents() call in + * progress. The processEvents() function will return as soon as possible, + * after processing pending timers and events. If processEvents() isn't in + * progress, it will be interrupted immediately the next time it gets called. + */ + } /* namespace libcamera */ diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp index 6e0609c34ddc..a2674ab31135 100644 --- a/src/libcamera/event_dispatcher_poll.cpp +++ b/src/libcamera/event_dispatcher_poll.cpp @@ -8,7 +8,10 @@ #include #include #include +#include #include +#include +#include #include #include @@ -43,10 +46,18 @@ static const char *notifierType(EventNotifier::Type type) EventDispatcherPoll::EventDispatcherPoll() { + /* + * Create the event fd. Failures are fatal as we can't implement an + * interruptible dispatcher without the fd. + */ + eventfd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (eventfd_ < 0) + LOG(Event, Fatal) << "Unable to create eventfd"; } EventDispatcherPoll::~EventDispatcherPoll() { + close(eventfd_); } void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier) @@ -123,11 +134,13 @@ void EventDispatcherPoll::processEvents() /* Create the pollfd array. */ std::vector pollfds; - pollfds.reserve(notifiers_.size()); + pollfds.reserve(notifiers_.size() + 1); for (auto notifier : notifiers_) pollfds.push_back({ notifier.first, notifier.second.events(), 0 }); + pollfds.push_back({ eventfd_, POLLIN, 0 }); + /* Wait for events and process notifiers and timers. */ do { ret = poll(&pollfds); @@ -137,12 +150,20 @@ void EventDispatcherPoll::processEvents() ret = -errno; LOG(Event, Warning) << "poll() failed with " << strerror(-ret); } else if (ret > 0) { + processInterrupt(pollfds.back()); + pollfds.pop_back(); processNotifiers(pollfds); } processTimers(); } +void EventDispatcherPoll::interrupt() +{ + uint64_t value = 1; + write(eventfd_, &value, sizeof(value)); +} + short EventDispatcherPoll::EventNotifierSetPoll::events() const { short events = 0; @@ -186,6 +207,15 @@ int EventDispatcherPoll::poll(std::vector *pollfds) nextTimer ? &timeout : nullptr, nullptr); } +void EventDispatcherPoll::processInterrupt(const struct pollfd &pfd) +{ + if (!pfd.revents & POLLIN) + return; + + uint64_t value; + read(eventfd_, &value, sizeof(value)); +} + void EventDispatcherPoll::processNotifiers(const std::vector &pollfds) { static const struct { diff --git a/src/libcamera/include/event_dispatcher_poll.h b/src/libcamera/include/event_dispatcher_poll.h index ac3efde082b4..1c0066c24dc8 100644 --- a/src/libcamera/include/event_dispatcher_poll.h +++ b/src/libcamera/include/event_dispatcher_poll.h @@ -31,6 +31,7 @@ public: void unregisterTimer(Timer *timer); void processEvents(); + void interrupt(); private: struct EventNotifierSetPoll { @@ -40,8 +41,10 @@ private: std::map notifiers_; std::list timers_; + int eventfd_; int poll(std::vector *pollfds); + void processInterrupt(const struct pollfd &pfd); void processNotifiers(const std::vector &pollfds); void processTimers(); }; From patchwork Wed Jan 23 08:59:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 348 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8089E60C7F for ; Wed, 23 Jan 2019 09:59:30 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C3A3223F for ; Wed, 23 Jan 2019 09:59:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548233970; bh=KDgVquP+m6F7TWbHW2iZPAwE3pqrPt/LzxLNzahKTwQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=amwSUgqILAXzXhYRyvC58ji3pRZFs9smD+BwrxNGxa+BKYAARTlsTnli6Fi3G9GiT 9Yu8GGBEVpiWASGq5vWBLPWOpDC1l4fa2spzx4IkPqgJWSWO7IPnJBMqmypdLA1sfZ bx8dV/KqqksZiRdz3AlBxyaaR4piKpfduRr5a/t8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Jan 2019 10:59:23 +0200 Message-Id: <20190123085923.12524-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> References: <20190123085923.12524-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/4] tests: event-dispatcher: Add processEvents() interruption test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 23 Jan 2019 08:59:30 -0000 Test that the EventDispatcher::interrupt() function correctly interrupts the processEvents() function, both when called before processEvents() and when called while it is running. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- test/event-dispatcher.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/event-dispatcher.cpp b/test/event-dispatcher.cpp index 06c2657f09d6..e8818dcab4ad 100644 --- a/test/event-dispatcher.cpp +++ b/test/event-dispatcher.cpp @@ -18,16 +18,23 @@ using namespace std; using namespace libcamera; +static EventDispatcher *dispatcher; +static bool interrupt; + class EventDispatcherTest : public Test { protected: static void sigAlarmHandler(int) { cout << "SIGALARM received" << endl; + if (interrupt) + dispatcher->interrupt(); } int init() { + dispatcher = CameraManager::instance()->eventDispatcher(); + struct sigaction sa = {}; sa.sa_handler = &sigAlarmHandler; sigaction(SIGALRM, &sa, nullptr); @@ -37,7 +44,6 @@ protected: int run() { - EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); Timer timer; /* Event processing interruption by signal. */ @@ -48,6 +54,7 @@ protected: struct itimerval itimer = {}; itimer.it_value.tv_usec = 500000; + interrupt = false; setitimer(ITIMER_REAL, &itimer, nullptr); dispatcher->processEvents(); @@ -62,6 +69,29 @@ protected: return TestFail; } + /* Event processing interruption. */ + timer.start(1000); + dispatcher->interrupt(); + + dispatcher->processEvents(); + + if (!timer.isRunning()) { + cout << "Event processing immediate interruption failed" << endl; + return TestFail; + } + + timer.start(1000); + itimer.it_value.tv_usec = 500000; + interrupt = true; + setitimer(ITIMER_REAL, &itimer, nullptr); + + dispatcher->processEvents(); + + if (!timer.isRunning()) { + cout << "Event processing delayed interruption failed" << endl; + return TestFail; + } + return TestPass; }