From patchwork Sun Jan 6 02:33:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 150 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id F218B60B2E for ; Sun, 6 Jan 2019 03:32:26 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 726D8513 for ; Sun, 6 Jan 2019 03:32:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741946; bh=+0ER7+nIS3PE8vH/Tgfdbnl5PktMPE0/jBgk03Jh7Gs=; h=From:To:Subject:Date:From; b=lRUSDb1cFDa7MSfIHIiPMlYxB3sGRX7Ltm4wtUSb8I2hN5/KhAl671pcwzaExl518 lf9VBkOS7c7Ja4MC2EnGrPdeODFEr4MgfyRE6wbSsUQJU/9S7XKcF4xS/4LrwaFcL3 /k8OZHUsuEiDIFwwb5LP95f2rv1a+M4YzUhb084o= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:18 +0200 Message-Id: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 01/11] libcamera: log: Add a LogFatal log level 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: Sun, 06 Jan 2019 02:32:27 -0000 The LogFatal log level is similar to the LogError level, but additionally abort program execution. This is useful to implement assertion handlers. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/libcamera/include/log.h | 2 ++ src/libcamera/log.cpp | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h index 74439848c6ca..03842be02d0e 100644 --- a/src/libcamera/include/log.h +++ b/src/libcamera/include/log.h @@ -16,6 +16,7 @@ enum LogSeverity { LogInfo, LogWarning, LogError, + LogFatal, }; class LogMessage @@ -30,6 +31,7 @@ public: private: std::ostringstream msgStream; + LogSeverity severity_; }; #define LOG(severity) LogMessage(__FILE__, __LINE__, Log##severity).stream() diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp index 44b3a5bbe279..a5823c64eaa6 100644 --- a/src/libcamera/log.cpp +++ b/src/libcamera/log.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -31,6 +32,8 @@ namespace libcamera { * Warning message, signals a potential issue * \var LogError * Error message, signals an unrecoverable issue + * \var LogFatal + * Fatal message, signals an unrecoverable issue and aborts execution */ /** @@ -40,15 +43,19 @@ namespace libcamera { * Return an std::ostream reference to which a message can be logged using the * iostream API. The \a severity controls whether the message is printed or * dropped, depending on the global log level. + * + * If the severity is set to Fatal, execution is aborted and the program + * terminates immediately after printing the message. */ static const char *log_severity_name(LogSeverity severity) { static const char * const names[] = { - " DBG", - "INFO", - "WARN", - " ERR", + " DBG", + " INFO", + " WARN", + " ERR", + "FATAL", }; if (static_cast(severity) < ARRAY_SIZE(names)) @@ -73,6 +80,7 @@ static const char *log_severity_name(LogSeverity severity) */ LogMessage::LogMessage(const char *fileName, unsigned int line, LogSeverity severity) + : severity_(severity) { /* Log the timestamp, severity and file information. */ struct timespec timestamp; @@ -93,6 +101,9 @@ LogMessage::~LogMessage() std::string msg(msgStream.str()); fwrite(msg.data(), msg.size(), 1, stderr); fflush(stderr); + + if (severity_ == LogSeverity::LogFatal) + std::abort(); } /** From patchwork Sun Jan 6 02:33:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 151 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4135260B2E for ; Sun, 6 Jan 2019 03:32:27 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C3E2198C for ; Sun, 6 Jan 2019 03:32:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741946; bh=0XeIsh/ltcYJ77DUd2giB2BsNTR17n8e+KhSX8engPE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=J7qOj624zfagj9Mcgl0NVk5dYO6Jjo6juyH33s8O7m09c/PSUHsAP7vxMJcnnWNEq tCPHnVKPQ56DWhlDtcu8rLsNjND+IUWmHfJ+qnW3VMMiWHgw+MPa1Z81TKNzIQuBG+ tH9UtAsBt4d6OOxv45fKMGu8sRP2BQ6MN/RReFvo= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:19 +0200 Message-Id: <20190106023328.10989-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 02/11] libcamera: log: Add an ASSERT macro 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: Sun, 06 Jan 2019 02:32:27 -0000 The ASSERT() macro is similar to the assert() macro defined by the C standard, but uses the libcamera logging infrastructure. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/libcamera/include/log.h | 9 +++++++++ src/libcamera/log.cpp | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h index 03842be02d0e..774916f04274 100644 --- a/src/libcamera/include/log.h +++ b/src/libcamera/include/log.h @@ -36,6 +36,15 @@ private: #define LOG(severity) LogMessage(__FILE__, __LINE__, Log##severity).stream() +#ifdef NDEBUG +#define ASSERT(condition) static_cast({ \ + if (condition) \ + LOG(Fatal) << "assertion \"" #condition "\" failed"; \ +}) +#else +#define ASSERT(condition) static_cast(false && (condition)) +#endif + } /* namespace libcamera */ #endif /* __LIBCAMERA_LOG_H__ */ diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp index a5823c64eaa6..4165cbd654fc 100644 --- a/src/libcamera/log.cpp +++ b/src/libcamera/log.cpp @@ -48,6 +48,22 @@ namespace libcamera { * terminates immediately after printing the message. */ +/** + * \def ASSERT(condition) + * \brief Abort program execution if assertion is failed + * + * If \a condition is false, ASSERT() logs an error message with the Fatal log + * level and aborts program execution. + * + * If the macro NDEBUG is defined before including log.h, ASSERT() generates no + * code. + * + * Using conditions that have side effects with ASSERT() is not recommended, as + * these effects would depend on whether NDEBUG is defined or not. Similarly, + * ASSERT() should not be used to check for errors that can occur under normal + * conditions as those checks would then be removed when compiling with NDEBUG. + */ + static const char *log_severity_name(LogSeverity severity) { static const char * const names[] = { From patchwork Sun Jan 6 02:33:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 152 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 85BFB60B2E for ; Sun, 6 Jan 2019 03:32:27 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 245BF513 for ; Sun, 6 Jan 2019 03:32:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741947; bh=5d6i/BBznSoao+qquiwR3emq5p6mlKKhj4zQUtQJEos=; h=From:To:Subject:Date:In-Reply-To:References:From; b=dpYM/rCEhScUm9Gahh1TW85ujyIYv27Hb3yG0HJ1rW9xdinBa/cx4w1uvgmlUFNql p4Wf0JhFDQupvZ2MkYVhRKvUdOxE6b4VpN9QFb8L2OLhvvhLP/tW4bSlnULukNkYhr UXYtNx7XaI8hGTP8JJ2GN4+qgWKtMQ2bFxO9RM9Q= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:20 +0200 Message-Id: <20190106023328.10989-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 03/11] libcamera: log: Pad timestamp fields with zeros 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: Sun, 06 Jan 2019 02:32:27 -0000 The logger prints the timestamp fields with a fixed width, but pads them with spaces instead of zeros. Fix this. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/libcamera/log.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp index 4165cbd654fc..e4d07fbbea3b 100644 --- a/src/libcamera/log.cpp +++ b/src/libcamera/log.cpp @@ -101,10 +101,12 @@ LogMessage::LogMessage(const char *fileName, unsigned int line, /* Log the timestamp, severity and file information. */ struct timespec timestamp; clock_gettime(CLOCK_MONOTONIC, ×tamp); + msgStream.fill('0'); msgStream << "[" << timestamp.tv_sec / (60 * 60) << ":" << std::setw(2) << (timestamp.tv_sec / 60) % 60 << ":" << std::setw(2) << timestamp.tv_sec % 60 << "." << std::setw(9) << timestamp.tv_nsec << "]"; + msgStream.fill(' '); msgStream << " " << log_severity_name(severity); msgStream << " " << basename(fileName) << ":" << line << " "; From patchwork Sun Jan 6 02:33:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 153 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D3D6060B2E for ; Sun, 6 Jan 2019 03:32:27 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7478598C for ; Sun, 6 Jan 2019 03:32:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741947; bh=Ul2/eLTgDrmri2CL85FO+TRHiaTiObBDZCXmYikoYVg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=P3dPAmzry7mPw36XJ4sncVzQ79FblW/EIzNyc9ko7PJAogR9bQKgy8JUv00l9bYDM OZsFzSUGIk/HHCNNkMdxwjRynGTyXdl6Yu+PPXP3eAgg+oqPkBKIGrOAnqU6nG5vRb hC5LLn2nwoWv9L+1fcSSREjg9a05riaGSYit+J9Q= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:21 +0200 Message-Id: <20190106023328.10989-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 04/11] libcamera: camera_manager: Make the class a singleton 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: Sun, 06 Jan 2019 02:32:28 -0000 There can only be a single camera manager instance in the application. Creating it as a singleton helps avoiding mistakes. It also allows the camera manager to be used as a storage of global data, such as the future event dispatcher. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Reviewed-by: Jacopo Mondi --- include/libcamera/camera_manager.h | 6 ++++-- src/libcamera/camera_manager.cpp | 15 +++++++++++++++ test/list.cpp | 4 +--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 2768a5bd2384..56a3f32d8b6f 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -19,15 +19,17 @@ class PipelineHandler; class CameraManager { public: - CameraManager(); - int start(); void stop(); std::vector list() const; Camera *get(const std::string &name); + static CameraManager *instance(); + private: + CameraManager(); + DeviceEnumerator *enumerator_; std::vector pipes_; }; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 50a805fc665c..db2bc4b7424e 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -161,4 +161,19 @@ Camera *CameraManager::get(const std::string &name) return nullptr; } +/** + * \brief Retrieve the camera manager instance + * + * The CameraManager is a singleton and can't be constructed manually. This + * function can instead be used to retrieve the single global instance of the + * manager. + * + * \return The camera manager instance + */ +CameraManager *CameraManager::instance() +{ + static CameraManager manager; + return &manager; +} + } /* namespace libcamera */ diff --git a/test/list.cpp b/test/list.cpp index 39b8a41d1fef..49610c697c63 100644 --- a/test/list.cpp +++ b/test/list.cpp @@ -19,7 +19,7 @@ class ListTest : public Test protected: int init() { - cm = new CameraManager(); + cm = CameraManager::instance(); if (!cm) return -ENOMEM; @@ -43,8 +43,6 @@ protected: void cleanup() { cm->stop(); - - delete cm; } private: From patchwork Sun Jan 6 02:33:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 154 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 309BA60B2E for ; Sun, 6 Jan 2019 03:32:28 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C202B513 for ; Sun, 6 Jan 2019 03:32:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741947; bh=oY+skbLzSkMGki+7qahzSd6PhQrV5oiC09QvwpXNQEA=; h=From:To:Subject:Date:In-Reply-To:References:From; b=OMZLnUFvjG8Ia+ktlwsnkSiZ2WGSuESdVITNeBhKDWMEiiC0UT5IqjEtUcAdn/VMb 0FndixfZJpGn/v4Wa9/Cv3k/ox9m4yBCkqToXSxcgL/f3GLHtDWG4Yxw9DLYDbkKC2 eOTwEsUR5+UcEVlQN0UKwv33xdRzrFDahSe/OdWM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:22 +0200 Message-Id: <20190106023328.10989-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 05/11] libcamera: Add signal/slot communication mechanism 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: Sun, 06 Jan 2019 02:32:28 -0000 Introduce a Signal class that allows connecting event sources (signals) to event listeners (slots) without adding any boilerplate code usually associated with the observer or listener design patterns. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- Documentation/Doxyfile.in | 3 +- include/libcamera/meson.build | 1 + include/libcamera/signal.h | 117 ++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + src/libcamera/signal.cpp | 44 +++++++++++++ 5 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 include/libcamera/signal.h create mode 100644 src/libcamera/signal.cpp diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index b1a70d36eee5..558a1ce04377 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -860,7 +860,8 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = libcamera::SlotBase \ + libcamera::Slot # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 3e04557d66b1..6f87689ea528 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -2,6 +2,7 @@ libcamera_api = files([ 'camera.h', 'camera_manager.h', 'libcamera.h', + 'signal.h', ]) install_headers(libcamera_api, diff --git a/include/libcamera/signal.h b/include/libcamera/signal.h new file mode 100644 index 000000000000..fceb852158ec --- /dev/null +++ b/include/libcamera/signal.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * signal.h - Signal & slot implementation + */ +#ifndef __LIBCAMERA_SIGNAL_H__ +#define __LIBCAMERA_SIGNAL_H__ + +#include +#include + +namespace libcamera { + +template +class Signal; + +template +class SlotBase +{ +public: + SlotBase(void *obj) + : obj_(obj) { } + virtual ~SlotBase() { } + + virtual void invoke(Args... args) = 0; + +protected: + friend class Signal; + void *obj_; +}; + +template +class Slot : public SlotBase +{ +public: + Slot(T *obj, void(T::*func)(Args...)) + : SlotBase(obj), func_(func) { } + + void invoke(Args... args) { (reinterpret_cast(this->obj_)->*func_)(args...); } + +private: + friend class Signal; + void(T::*func_)(Args...); +}; + +template +class Signal +{ +public: + Signal() { } + ~Signal() + { + for (SlotBase *slot : slots_) + delete slot; + } + + template + void connect(T *object, void(T::*func)(Args...)) + { + slots_.push_back(new Slot(object, func)); + } + + void disconnect() + { + for (SlotBase *slot : slots_) + delete slot; + slots_.clear(); + } + + template + void disconnect(T *object) + { + for (auto iter = slots_.begin(); iter != slots_.end(); ) { + SlotBase *slot = *iter; + if (slot->obj_ == object) { + iter = slots_.erase(iter); + delete slot; + } else { + ++iter; + } + } + } + + template + void disconnect(T *object, void(T::*func)(Args...)) + { + for (auto iter = slots_.begin(); iter != slots_.end(); ) { + SlotBase *slot = *iter; + if (slot->obj_ == object && + reinterpret_cast *>(slot)->func_ == func) { + iter = slots_.erase(iter); + delete slot; + } else { + ++iter; + } + } + } + + void emit(Args... args) + { + /* + * Make a copy of the slots list as the slot could call the + * disconnect operation, invalidating the iterator. + */ + std::vector *> slots{ slots_.begin(), slots_.end() }; + for (SlotBase *slot : slots) + slot->invoke(args...); + } + +private: + std::list *> slots_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_SIGNAL_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 78562299fc42..3ec86e75b57e 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -6,6 +6,7 @@ libcamera_sources = files([ 'media_device.cpp', 'media_object.cpp', 'pipeline_handler.cpp', + 'signal.cpp', ]) libcamera_headers = files([ diff --git a/src/libcamera/signal.cpp b/src/libcamera/signal.cpp new file mode 100644 index 000000000000..8b5a6c285c55 --- /dev/null +++ b/src/libcamera/signal.cpp @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * signal.cpp - Signal & slot implementation + */ + +namespace libcamera { + +/** + * \class Signal + * \brief Generic signal and slot communication mechanism + * + * Signals and slots are a language construct aimed at communication between + * objects through the observer pattern without the need for boilerplate code. + * See http://doc.qt.io/qt-5/signalsandslots.html for more information. + */ + +/** + * \fn Signal::connect() + * \brief Connect the signal to a slot + */ + +/** + * \fn Signal::disconnect() + * \brief Disconnect the signal from all slots + */ + +/** + * \fn Signal::disconnect(T *object) + * \brief Disconnect the signal from all slots of the \a object + */ + +/** + * \fn Signal::disconnect(T *object, void(T::*func)(Args...)) + * \brief Disconnect the signal from a slot of the \a object + */ + +/** + * \fn Signal::emit() + * \brief Emit the signal and call all connected slots + */ + +} /* namespace libcamera */ From patchwork Sun Jan 6 02:33:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 155 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 9053860B30 for ; Sun, 6 Jan 2019 03:32:28 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1B6CF98C for ; Sun, 6 Jan 2019 03:32:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741948; bh=OscvqIS7SMJlbGCvASn5yQ8xW4BQJQMt8YLrwUE3Kuk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=CkmG+Y8t0L99DdtV/hC6dcW4bmXfGnHhOVQ8vzjCbzqPKf0V9cMEuQf41sqQIGYvH PIVdkNv4rakFpOk+nqmu2nRSGGzdcyn6WpDkCHAbHA8HpRl+Y05fyOEzfelOYUBTCS +pRyDoW4rb3dL7TE4UO5YUzhLz0gZKmC0+gQ6J+o= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:23 +0200 Message-Id: <20190106023328.10989-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 06/11] libcamera: Add event notification infrastructure 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: Sun, 06 Jan 2019 02:32:29 -0000 Add three new classes, EventDispatcher, EventNotifier and Timer, that define APIs for file descriptor event notification and timers. The implementation of the EventDispatcher is meant to be provided to libcamera by the application. The event dispatcher is integrated twith the camera manager to implement automatic registration of timers and events. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- include/libcamera/camera_manager.h | 7 ++ include/libcamera/event_dispatcher.h | 33 ++++++++ include/libcamera/event_notifier.h | 42 ++++++++++ include/libcamera/libcamera.h | 1 + include/libcamera/meson.build | 3 + include/libcamera/timer.h | 37 +++++++++ src/libcamera/camera_manager.cpp | 42 +++++++++- src/libcamera/event_dispatcher.cpp | 102 +++++++++++++++++++++++ src/libcamera/event_notifier.cpp | 118 +++++++++++++++++++++++++++ src/libcamera/meson.build | 3 + src/libcamera/timer.cpp | 105 ++++++++++++++++++++++++ 11 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 include/libcamera/event_dispatcher.h create mode 100644 include/libcamera/event_notifier.h create mode 100644 include/libcamera/timer.h create mode 100644 src/libcamera/event_dispatcher.cpp create mode 100644 src/libcamera/event_notifier.cpp create mode 100644 src/libcamera/timer.cpp diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 56a3f32d8b6f..cb6a6d084a4a 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -14,6 +14,7 @@ namespace libcamera { class Camera; class DeviceEnumerator; +class EventDispatcher; class PipelineHandler; class CameraManager @@ -27,11 +28,17 @@ public: static CameraManager *instance(); + void setEventDispatcher(EventDispatcher *dispatcher); + EventDispatcher *eventDispatcher(); + private: CameraManager(); + ~CameraManager(); DeviceEnumerator *enumerator_; std::vector pipes_; + + EventDispatcher *dispatcher_; }; } /* namespace libcamera */ diff --git a/include/libcamera/event_dispatcher.h b/include/libcamera/event_dispatcher.h new file mode 100644 index 000000000000..c20518c6866d --- /dev/null +++ b/include/libcamera/event_dispatcher.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_dispatcher.h - Event dispatcher + */ +#ifndef __LIBCAMERA_EVENT_DISPATCHER_H__ +#define __LIBCAMERA_EVENT_DISPATCHER_H__ + +#include + +namespace libcamera { + +class EventNotifier; +class Timer; + +class EventDispatcher +{ +public: + virtual ~EventDispatcher(); + + virtual void registerEventNotifier(EventNotifier *notifier) = 0; + virtual void unregisterEventNotifier(EventNotifier *notifier) = 0; + + virtual void registerTimer(Timer *timer) = 0; + virtual void unregisterTimer(Timer *timer) = 0; + + virtual void processEvents() = 0; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_EVENT_DISPATCHER_H__ */ diff --git a/include/libcamera/event_notifier.h b/include/libcamera/event_notifier.h new file mode 100644 index 000000000000..278a5ff6f9e5 --- /dev/null +++ b/include/libcamera/event_notifier.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_notifer.h - Event notifier + */ +#ifndef __LIBCAMERA_EVENT_NOTIFIER_H__ +#define __LIBCAMERA_EVENT_NOTIFIER_H__ + +#include + +namespace libcamera { + +class EventNotifier +{ +public: + enum Type { + Read, + Write, + Exception, + }; + + EventNotifier(int fd, Type type); + virtual ~EventNotifier(); + + Type type() const { return type_; } + int fd() const { return fd_; } + + bool isEnabled() const { return enabled_; } + void setEnabled(bool enable); + + Signal activated; + +private: + int fd_; + Type type_; + bool enabled_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_EVENT_NOTIFIER_H__ */ diff --git a/include/libcamera/libcamera.h b/include/libcamera/libcamera.h index f9556a8bce62..785babefa1c8 100644 --- a/include/libcamera/libcamera.h +++ b/include/libcamera/libcamera.h @@ -9,5 +9,6 @@ #include #include +#include #endif /* __LIBCAMERA_LIBCAMERA_H__ */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 6f87689ea528..d7cb55ba4a49 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -1,8 +1,11 @@ libcamera_api = files([ 'camera.h', 'camera_manager.h', + 'event_dispatcher.h', + 'event_notifier.h', 'libcamera.h', 'signal.h', + 'timer.h', ]) install_headers(libcamera_api, diff --git a/include/libcamera/timer.h b/include/libcamera/timer.h new file mode 100644 index 000000000000..97dcc01f493d --- /dev/null +++ b/include/libcamera/timer.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * timer.h - Generic timer + */ +#ifndef __LIBCAMERA_TIMER_H__ +#define __LIBCAMERA_TIMER_H__ + +#include + +#include + +namespace libcamera { + +class Timer +{ +public: + Timer(); + + void start(unsigned int msec); + void stop(); + bool isRunning() const; + + unsigned int interval() const { return interval_; } + uint64_t deadline() const { return deadline_; } + + Signal timeout; + +private: + unsigned int interval_; + uint64_t deadline_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_TIMER_H__ */ diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index db2bc4b7424e..0dbf31f47039 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -6,8 +6,10 @@ */ #include +#include #include "device_enumerator.h" +#include "log.h" #include "pipeline_handler.h" /** @@ -37,10 +39,15 @@ namespace libcamera { CameraManager::CameraManager() - : enumerator_(nullptr) + : enumerator_(nullptr), dispatcher_(nullptr) { } +CameraManager::~CameraManager() +{ + delete dispatcher_; +} + /** * \brief Start the camera manager * @@ -176,4 +183,37 @@ CameraManager *CameraManager::instance() return &manager; } +/** + * \brief Set the event dispatcher + * \param dispatcher Pointer to the event dispatcher + * + * libcamera requires an event dispatcher to integrate event notification and + * timers with the application event loop. Applications shall call this function + * once and only once before the camera manager is started with start() to set + * the event dispatcher. + * + * The CameraManager takes ownership of the event dispatcher and will delete it + * when the application terminates. + */ +void CameraManager::setEventDispatcher(EventDispatcher *dispatcher) +{ + if (dispatcher_) { + LOG(Warning) << "Event dispatcher is already set"; + return; + } + + dispatcher_ = dispatcher; +} + +/** + * \fn CameraManager::eventDispatcher() + * \brief Retrieve the event dispatcher + * \return Pointer to the event dispatcher, or nullptr if no event dispatcher + * has been set + */ +EventDispatcher *CameraManager::eventDispatcher() +{ + return dispatcher_; +} + } /* namespace libcamera */ diff --git a/src/libcamera/event_dispatcher.cpp b/src/libcamera/event_dispatcher.cpp new file mode 100644 index 000000000000..61013f218887 --- /dev/null +++ b/src/libcamera/event_dispatcher.cpp @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_dispatcher.cpp - Event dispatcher + */ + +#include + +/** + * \file event_dispatcher.h + */ + +namespace libcamera { + +/** + * \class EventDispatcher + * \brief Interface to manage the libcamera events and timers + * + * The EventDispatcher class allows the integration of the application event + * loop with libcamera by abstracting how events and timers are managed and + * processed. + * + * To listen to events, libcamera creates EventNotifier instances and register + * them with the dispatcher with registerEventNotifier(). The event notifier + * \ref EventNotifier::activated signal is then emitted by the dispatcher + * whenever the event is detected. + * + * To set timers, libcamera creates Timer instances and register them with the + * dispatcher with registerTimer(). The timer \ref Timer::timeout signal is then + * emitted by the dispatcher when the timer times out. + */ + +EventDispatcher::~EventDispatcher() +{ +} + +/** + * \fn EventDispatcher::registerEventNotifier() + * \brief Register an event notifier + * \param notifier The event notifier to register + * + * Once the \a notifier is registered with the dispatcher, the dispatcher will + * emit the notifier \ref EventNotifier::activated signal whenever a + * corresponding event is detected on the notifier's file descriptor. The event + * is monitored until the notifier is unregistered with + * unregisterEventNotifier(). + * + * Registering multiple notifiers for the same file descriptor and event type is + * not allowed. + */ + +/** + * \fn EventDispatcher::unregisterEventNotifier() + * \brief Unregister an event notifier + * \param notifier The event notifier to unregister + * + * After this function returns the \a notifier is guaranteed not to emit the + * \ref EventNotifier::activated signal. + * + * If the notifier isn't registered, this function performs no operation. + */ + +/** + * \fn EventDispatcher::registerTimer() + * \brief Register a timer + * \param timer The timer to register + * + * Once the \a timer is registered with the dispatcher, the dispatcher will emit + * the timer \ref Timer::timeout signal when the timer times out. The timer can + * be unregistered with unregisterTimer() before it times out, in which case the + * signal will not be emitted. + * + * When the \a timer times out, it is automatically unregistered by the + * dispatcher and can be registered back as early as from the \ref Timer::timeout + * signal handlers. + * + * Registering the same timer multiple times is not allowed. + */ + +/** + * \fn EventDispatcher::unregisterTimer() + * \brief Unregister a timer + * \param timer The timer to unregister + * + * After this function returns the \a timer is guaranteed not to emit the + * \ref Timer::timeout signal. + * + * If the timer isn't registered, this function performs no operation. + */ + +/** + * \fn EventDispatcher::processEvents() + * \brief Wait for and process pending events + * + * This function processes all pending events associated with registered event + * notifiers and timers and signals the corresponding EventNotifier and Timer + * objects. If no events are pending, it waits for the first event and processes + * it before returning. + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/event_notifier.cpp b/src/libcamera/event_notifier.cpp new file mode 100644 index 000000000000..edaf66725dc5 --- /dev/null +++ b/src/libcamera/event_notifier.cpp @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_notifier.cpp - Event notifier + */ + +#include +#include +#include + +/** + * \file event_notifier.h + * \brief Event notifier + */ + +namespace libcamera { + +/** + * \class EventNotifier + * \brief Notify of activity on a file descriptor + * + * The EventNotifier models a file descriptor event source that can be + * monitored. It is created with the file descriptor to be monitored and the + * type of event, and is enabled by default. It will emit the \ref activated + * signal whenever an event of the monitored type occurs on the file descriptor. + * + * Supported type of events are EventNotifier::Read, EventNotifier::Write and + * EventNotifier::Exception. The type is specified when constructed the + * notifier, and can be retrieved using the type() function. To listen to + * multiple event types on the same file descriptor multiple notifiers must be + * created. + * + * The notifier can be disabled with the setEnable() function. When the notifier + * is disabled it ignores events and does not emit the \ref activated signal. + * The notifier can then be re-enabled with the setEnable() function. + * + * Notifier events are detected and dispatched from the + * EventDispatcher::processEvents() function. + */ + +/** + * \enum EventNotifier::Type + * Type of file descriptor event to listen for. + * \var EventNotifier::Read + * Data is available to be read from the file descriptor + * \var EventNotifier::Write + * Data can be written to the file descriptor + * \var EventNotifier::Exception + * An exception has occurred on the file descriptor + */ + +/** + * \brief Construct an event notifier with a file descriptor and event type + * \param fd The file descriptor to monitor + * \param type The event type to monitor + */ +EventNotifier::EventNotifier(int fd, Type type) + : fd_(fd), type_(type), enabled_(false) +{ + setEnabled(true); +} + +EventNotifier::~EventNotifier() +{ + setEnabled(false); +} + +/** + * \fn EventNotifier::type() + * \brief Retrive the type of the event being monitored + * \return The type of the event + */ + +/** + * \fn EventNotifier::fd() + * \brief Retrive the file descriptor being monitored + * \return The file descriptor + */ + +/** + * \fn EventNotifier::isEnabled() + * \brief Retrieve the notifier state + * \return true if the notifier is enabled, or false otherwise + * \sa setEnable() + */ + +/** + * \brief Enable or disable the notifier + * \param enable true to enable the notifier, false to disable it + * + * This function enables or disables the notifier. A disabled notifier ignores + * events and does not emit the \ref activated signal. + */ +void EventNotifier::setEnabled(bool enable) +{ + if (enabled_ == enable) + return; + + enabled_ = enable; + + EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); + if (enable) + dispatcher->registerEventNotifier(this); + else + dispatcher->unregisterEventNotifier(this); +} + +/** + * \var EventNotifier::activated + * \brief Signal emitted when the event occurs + * + * This signal is emitted when the event \ref type() occurs on the file + * descriptor monitored by the notifier. The notifier pointer is passed as a + * parameter. + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 3ec86e75b57e..61fb93205b34 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -2,11 +2,14 @@ libcamera_sources = files([ 'camera.cpp', 'camera_manager.cpp', 'device_enumerator.cpp', + 'event_dispatcher.cpp', + 'event_notifier.cpp', 'log.cpp', 'media_device.cpp', 'media_object.cpp', 'pipeline_handler.cpp', 'signal.cpp', + 'timer.cpp', ]) libcamera_headers = files([ diff --git a/src/libcamera/timer.cpp b/src/libcamera/timer.cpp new file mode 100644 index 000000000000..57c49aa2ef36 --- /dev/null +++ b/src/libcamera/timer.cpp @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * timer.cpp - Generic timer + */ + +#include + +#include +#include +#include + +#include "log.h" + +/** + * \file timer.h + * \brief Generic timer + */ + +namespace libcamera { + +/** + * \class Timer + * \brief Single-shot timer interface + * + * The Timer class models a single-shot timer that is started with start() and + * emits the \ref timeout signal when it times out. + * + * Once started the timer will run until it times out. It can be stopped with + * stop(), and once it times out or is stopped, can be started again with + * start(). + */ + +/** + * \brief Construct a timer + */ +Timer::Timer() +{ +} + +/** + * \brief Start or restart the timer with a timeout of \a msec + * \param msec The timer duration in milliseconds + * + * If the timer is already running it will be stopped and restarted. + */ +void Timer::start(unsigned int msec) +{ + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + + interval_ = msec; + deadline_ = tp.tv_sec * 1000000000ULL + tp.tv_nsec + msec * 1000000; + + LOG(Debug) << "Starting timer " << this << " with interval " << msec + << ": deadline " << deadline_; + + CameraManager::instance()->eventDispatcher()->registerTimer(this); +} + +/** + * \brief Stop the timer + * + * After this function returns the timer is guaranteed not to emit the + * \ref timeout signal. + * + * If the timer is not running this function performs no operation. + */ +void Timer::stop() +{ + CameraManager::instance()->eventDispatcher()->unregisterTimer(this); + + deadline_ = 0; +} + +/** + * \brief + * \return true if the timer is running, false otherwise + */ +bool Timer::isRunning() const +{ + return deadline_ != 0; +} + +/** + * \fn Timer::interval() + * \brief Retrieve the timer interval + * \return The timer interval in milliseconds + */ + +/** + * \fn Timer::deadline() + * \brief Retrieve the timer deadline + * \return The timer deadline in nanoseconds + */ + +/** + * \var Timer::timeout + * \brief Signal emitted when the timer times out + * + * The timer pointer is passed as a parameter. + */ + +} /* namespace libcamera */ From patchwork Sun Jan 6 02:33:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 156 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 25F6160B30 for ; Sun, 6 Jan 2019 03:32:29 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 89558513 for ; Sun, 6 Jan 2019 03:32:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741948; bh=py70DnK3sBvfhGAMM2t+fS8lbvYWXP2or1PGF+FJlyU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=i6JCtQJcoNcGQ4U20PTk7cOFJc3eIbH0tStTwAMKOUJzI11HMNIUdly+uEulv3o4Q gfdu6Xw6OLqviIRsJgbBES5htfAY+LlAnT34O7AOJJxRLws+bTVNPvjKgmpIWwBB7h thHp22RNTDc0GnV9hv7hPeCgwxl3H7bOgZNuFtmU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:24 +0200 Message-Id: <20190106023328.10989-7-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 07/11] libcamera: Add a poll-based event dispatcher 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: Sun, 06 Jan 2019 02:32:29 -0000 Provide a poll-based event dispatcher implementation as convenience for applications that don't need a custom event loop. The poll-based dispatcher is automatically instantiated if the application doesn't provide its own dispatcher. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- include/libcamera/libcamera.h | 1 + src/libcamera/camera_manager.cpp | 19 +- src/libcamera/event_dispatcher_poll.cpp | 232 ++++++++++++++++++ src/libcamera/include/event_dispatcher_poll.h | 50 ++++ src/libcamera/meson.build | 2 + 5 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 src/libcamera/event_dispatcher_poll.cpp create mode 100644 src/libcamera/include/event_dispatcher_poll.h diff --git a/include/libcamera/libcamera.h b/include/libcamera/libcamera.h index 785babefa1c8..2dcaeda49812 100644 --- a/include/libcamera/libcamera.h +++ b/include/libcamera/libcamera.h @@ -10,5 +10,6 @@ #include #include #include +#include #endif /* __LIBCAMERA_LIBCAMERA_H__ */ diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 0dbf31f47039..4475ab1f9db2 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -9,6 +9,7 @@ #include #include "device_enumerator.h" +#include "event_dispatcher_poll.h" #include "log.h" #include "pipeline_handler.h" @@ -188,9 +189,10 @@ CameraManager *CameraManager::instance() * \param dispatcher Pointer to the event dispatcher * * libcamera requires an event dispatcher to integrate event notification and - * timers with the application event loop. Applications shall call this function - * once and only once before the camera manager is started with start() to set - * the event dispatcher. + * timers with the application event loop. Applications that want to provide + * their own event dispatcher shall call this function once and only once before + * the camera manager is started with start(). If no event dispatcher is + * provided, a default poll-based implementation will be used. * * The CameraManager takes ownership of the event dispatcher and will delete it * when the application terminates. @@ -208,11 +210,18 @@ void CameraManager::setEventDispatcher(EventDispatcher *dispatcher) /** * \fn CameraManager::eventDispatcher() * \brief Retrieve the event dispatcher - * \return Pointer to the event dispatcher, or nullptr if no event dispatcher - * has been set + * + * This function retrieves the event dispatcher set with setEventDispatcher(). + * If no dispatcher has been set, a default poll-based implementation is created + * and returned. + * + * \return Pointer to the event dispatcher */ EventDispatcher *CameraManager::eventDispatcher() { + if (!dispatcher_) + dispatcher_ = new EventDispatcherPoll(); + return dispatcher_; } diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp new file mode 100644 index 000000000000..2c64c1446772 --- /dev/null +++ b/src/libcamera/event_dispatcher_poll.cpp @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_dispatcher_poll.cpp - Poll-based event dispatcher + */ + +#include +#include +#include +#include + +#include +#include + +#include "event_dispatcher_poll.h" +#include "log.h" + +/** + * \file event_dispatcher_poll.h + */ + +namespace libcamera { + +static const char *notifierType(EventNotifier::Type type) +{ + if (type == EventNotifier::Read) + return "read"; + if (type == EventNotifier::Write) + return "write"; + if (type == EventNotifier::Exception) + return "exception"; + + return ""; +} + +/** + * \class EventDispatcherPoll + * \brief A poll-based event dispatcher + */ + +EventDispatcherPoll::EventDispatcherPoll() +{ +} + +EventDispatcherPoll::~EventDispatcherPoll() +{ +} + +void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier) +{ + EventNotifierSetPoll &set = notifiers_[notifier->fd()]; + EventNotifier::Type type = notifier->type(); + + if (set.notifiers[type] && set.notifiers[type] != notifier) + LOG(Warning) << "Duplicate " << notifierType(type) + << " notifiers for fd " << notifier->fd(); + + set.notifiers[type] = notifier; +} + +void EventDispatcherPoll::unregisterEventNotifier(EventNotifier *notifier) +{ + auto iter = notifiers_.find(notifier->fd()); + if (iter == notifiers_.end()) + return; + + EventNotifierSetPoll &set = iter->second; + EventNotifier::Type type = notifier->type(); + + if (!set.notifiers[type]) + return; + + if (set.notifiers[type] != notifier) { + LOG(Warning) << "Duplicate " << notifierType(type) + << " notifiers for fd " << notifier->fd(); + return; + } + + set.notifiers[type] = nullptr; + if (!set.notifiers[0] && !set.notifiers[1] && !set.notifiers[2]) + notifiers_.erase(iter); +} + +void EventDispatcherPoll::registerTimer(Timer *timer) +{ + for (auto iter = timers_.begin(); iter != timers_.end(); ++iter) { + if ((*iter)->deadline() > timer->deadline()) { + timers_.insert(iter, timer); + return; + } + } + + timers_.push_back(timer); +} + +void EventDispatcherPoll::unregisterTimer(Timer *timer) +{ + for (auto iter = timers_.begin(); iter != timers_.end(); ++iter) { + if (*iter == timer) { + timers_.erase(iter); + return; + } + + /* + * As the timers list is ordered, we can stop as soon as we go + * past the deadline. + */ + if ((*iter)->deadline() > timer->deadline()) + break; + } +} + +void EventDispatcherPoll::processEvents() +{ + int ret; + + /* Create the pollfd array. */ + std::vector pollfds; + pollfds.reserve(notifiers_.size()); + + 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(Debug) << "timeout " << timeout.tv_sec << "." + << std::setfill('0') << std::setw(9) + << timeout.tv_nsec; + } + + /* Wait for events and process notifers and timers. */ + ret = ppoll(pollfds.data(), pollfds.size(), + nextTimer ? &timeout : nullptr, nullptr); + if (ret < 0) { + ret = -errno; + LOG(Warning) << "poll() failed with " << strerror(-ret); + } else if (ret > 0) { + processNotifiers(pollfds); + } + + processTimers(); +} + +short EventDispatcherPoll::EventNotifierSetPoll::events() const +{ + short events = 0; + + if (notifiers[EventNotifier::Read]) + events |= POLLIN; + if (notifiers[EventNotifier::Write]) + events |= POLLOUT; + if (notifiers[EventNotifier::Exception]) + events |= POLLPRI; + + return events; +} + +void EventDispatcherPoll::processNotifiers(std::vector &pollfds) +{ + static const struct { + EventNotifier::Type type; + short events; + } events[] = { + { EventNotifier::Read, POLLIN }, + { EventNotifier::Write, POLLOUT }, + { EventNotifier::Exception, POLLPRI }, + }; + + for (const struct pollfd &pfd : pollfds) { + auto iter = notifiers_.find(pfd.fd); + ASSERT(iter == notifiers_.end()); + + EventNotifierSetPoll &set = iter->second; + + for (const auto &event : events) { + EventNotifier *notifier = set.notifiers[event.type]; + + if (!notifier) + continue; + + /* + * If the file descriptor is invalid, disable the + * notifier immediately. + */ + if (pfd.revents & POLLNVAL) { + LOG(Warning) << "Disabling " << notifierType(event.type) + << " due to invalid file descriptor " + << pfd.fd; + unregisterEventNotifier(notifier); + } + + if (pfd.revents & event.events) + notifier->activated.emit(notifier); + } + } +} + +void EventDispatcherPoll::processTimers() +{ + struct timespec ts; + uint64_t now; + clock_gettime(CLOCK_MONOTONIC, &ts); + now = ts.tv_sec * 1000000000ULL + ts.tv_nsec; + + while (!timers_.empty()) { + Timer *timer = timers_.front(); + if (timer->deadline() > now) + break; + + timers_.pop_front(); + timer->stop(); + timer->timeout.emit(timer); + } +} + +} /* namespace libcamera */ diff --git a/src/libcamera/include/event_dispatcher_poll.h b/src/libcamera/include/event_dispatcher_poll.h new file mode 100644 index 000000000000..600289e455e2 --- /dev/null +++ b/src/libcamera/include/event_dispatcher_poll.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_loop.h - Camera object interface + */ +#ifndef __LIBCAMERA_EVENT_DISPATCHER_POLL_H__ +#define __LIBCAMERA_EVENT_DISPATCHER_POLL_H__ + +#include + +#include +#include +#include + +namespace libcamera { + +class EventNotifier; +class Timer; + +class EventDispatcherPoll final : public EventDispatcher +{ +public: + EventDispatcherPoll(); + ~EventDispatcherPoll(); + + void registerEventNotifier(EventNotifier *notifier); + void unregisterEventNotifier(EventNotifier *notifier); + + void registerTimer(Timer *timer); + void unregisterTimer(Timer *timer); + + void processEvents(); + +private: + struct EventNotifierSetPoll { + short events() const; + EventNotifier *notifiers[3]; + }; + + std::map notifiers_; + std::list timers_; + + void processNotifiers(std::vector &pollfds); + void processTimers(); +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_EVENT_DISPATCHER_POLL_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 61fb93205b34..abf9a71d4172 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -3,6 +3,7 @@ libcamera_sources = files([ 'camera_manager.cpp', 'device_enumerator.cpp', 'event_dispatcher.cpp', + 'event_dispatcher_poll.cpp', 'event_notifier.cpp', 'log.cpp', 'media_device.cpp', @@ -14,6 +15,7 @@ libcamera_sources = files([ libcamera_headers = files([ 'include/device_enumerator.h', + 'include/event_dispatcher_poll.h', 'include/log.h', 'include/media_device.h', 'include/media_object.h', From patchwork Sun Jan 6 02:33:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 157 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 77C9560B30 for ; Sun, 6 Jan 2019 03:32:29 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 171E999A for ; Sun, 6 Jan 2019 03:32:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741949; bh=mHrjQwJOrSGvhmrtNLbgoTwE/CB9cLEnbDoXs+VBP3g=; h=From:To:Subject:Date:In-Reply-To:References:From; b=C4yxMPM0RIJFeQ99x/cusxH4oFsn7ZZzhAfVDHjfFqAK3V2tHycJB+zLfzL3SVB5l GM/T6mlqlaGtMymfJjuf/8sJ5BsdTdUSHS9M4SzUiA7gtGhsDcM/Gmo9Sp68AEKnYi 6eJIsGfjsFkvOQc5cAmhpTmz6hl22axPgBFM5RA4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:25 +0200 Message-Id: <20190106023328.10989-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 08/11] test: Rename list test to list-cameras 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: Sun, 06 Jan 2019 02:32:29 -0000 The list test generates a list binary in the test directory, which conflicts with the C++ std::list header of the same name. The binary gets included instead of the header file, breaking compilation. Rename the test to avoid this. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- test/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/meson.build b/test/meson.build index 184a7eeb5e27..638e8d51c131 100644 --- a/test/meson.build +++ b/test/meson.build @@ -3,7 +3,7 @@ subdir('libtest') subdir('media_device') public_tests = [ - ['list', 'list.cpp'], + ['list-cameras', 'list.cpp'], ] internal_tests = [ From patchwork Sun Jan 6 02:33:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 158 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C9EC560B30 for ; Sun, 6 Jan 2019 03:32:29 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 65AA7513 for ; Sun, 6 Jan 2019 03:32:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741949; bh=BGU7tHjIBA/1Zl5jndxrWB7NtF1QU/eQzxggY2N68Wk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=DFb/hZaIDEh+sClsyFyp6+EMRtZNYkc0zgW5S8x32ZmqTicDrtZ9x009McKmVJnOn k7kyRYLBxcQ2K1PwDL8/5coRCAf5NFvvfFsvEYRaBlIVPWU0RBrhuH2JMauSL2VTqh KtPMfFTFFk9LgN8ixNlVCzZ0TLCilk9jZNJoH/HE= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:26 +0200 Message-Id: <20190106023328.10989-9-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 09/11] test: Add signal/slot 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: Sun, 06 Jan 2019 02:32:30 -0000 Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- test/meson.build | 1 + test/signal.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 test/signal.cpp diff --git a/test/meson.build b/test/meson.build index 638e8d51c131..4d334025f3d3 100644 --- a/test/meson.build +++ b/test/meson.build @@ -4,6 +4,7 @@ subdir('media_device') public_tests = [ ['list-cameras', 'list.cpp'], + ['signal', 'signal.cpp'], ] internal_tests = [ diff --git a/test/signal.cpp b/test/signal.cpp new file mode 100644 index 000000000000..5d96958c6d4c --- /dev/null +++ b/test/signal.cpp @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * signal.cpp - Signal test + */ + +#include +#include + +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class SignalTest : public Test +{ +protected: + void slotVoid() + { + called_ = true; + } + + void slotDisconnect() + { + called_ = true; + signalVoid_.disconnect(this, &SignalTest::slotDisconnect); + } + + void slotInteger1(int value) + { + values_[0] = value; + } + + void slotInteger2(int value) + { + values_[1] = value; + } + + void slotMultiArgs(int value, const std::string &name) + { + values_[2] = value; + name_ = name; + } + + int init() + { + return 0; + } + + int run() + { + /* Test signal emission and reception. */ + called_ = false; + signalVoid_.connect(this, &SignalTest::slotVoid); + signalVoid_.emit(); + + if (!called_) { + cout << "Signal emission test failed" << endl; + return TestFail; + } + + /* Test signal with parameters. */ + values_[2] = 0; + name_.clear(); + signalMultiArgs_.connect(this, &SignalTest::slotMultiArgs); + signalMultiArgs_.emit(42, "H2G2"); + + if (values_[2] != 42 || name_ != "H2G2") { + cout << "Signal parameters test failed" << endl; + return TestFail; + } + + /* Test signal connected to multiple slots. */ + memset(values_, 0, sizeof(values_)); + signalInt_.connect(this, &SignalTest::slotInteger1); + signalInt_.connect(this, &SignalTest::slotInteger2); + signalInt_.emit(42); + + if (values_[0] != 42 || values_[1] != 42 || values_[2] != 0) { + cout << "Signal multi slot test failed" << endl; + return TestFail; + } + + /* Test disconnection of a single slot. */ + memset(values_, 0, sizeof(values_)); + signalInt_.disconnect(this, &SignalTest::slotInteger2); + signalInt_.emit(42); + + if (values_[0] != 42 || values_[1] != 0 || values_[2] != 0) { + cout << "Signal slot disconnection test failed" << endl; + return TestFail; + } + + /* Test disconnection of a whole object. */ + memset(values_, 0, sizeof(values_)); + signalInt_.disconnect(this); + signalInt_.emit(42); + + if (values_[0] != 0 || values_[1] != 0 || values_[2] != 0) { + cout << "Signal object disconnection test failed" << endl; + return TestFail; + } + + /* Test disconnection of a whole signal. */ + memset(values_, 0, sizeof(values_)); + signalInt_.connect(this, &SignalTest::slotInteger1); + signalInt_.connect(this, &SignalTest::slotInteger2); + signalInt_.disconnect(); + signalInt_.emit(42); + + if (values_[0] != 0 || values_[1] != 0 || values_[2] != 0) { + cout << "Signal object disconnection test failed" << endl; + return TestFail; + } + + /* Test disconnection from slot. */ + signalVoid_.disconnect(); + signalVoid_.connect(this, &SignalTest::slotDisconnect); + + signalVoid_.emit(); + called_ = false; + signalVoid_.emit(); + + if (called_) { + cout << "Signal disconnection from slot test failed" << endl; + return TestFail; + } + + return TestPass; + } + + void cleanup() + { + } + +private: + Signal<> signalVoid_; + Signal signalInt_; + Signal signalMultiArgs_; + + bool called_; + int values_[3]; + std::string name_; +}; + +TEST_REGISTER(SignalTest) From patchwork Sun Jan 6 02:33:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 159 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 2896460B45 for ; Sun, 6 Jan 2019 03:32:30 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B74CA99A for ; Sun, 6 Jan 2019 03:32:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741949; bh=VNMkZIqq5U/OE47VJFWGJVOH6e/51C9z3VfVxO8AZgo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=lYnUDzsJqhH+8C7uJTJYw2ib8qUEUOPsq7CndUktSTHe75h3mup9rH1/CpFnSCC5m wx3WNIA+wPYQtOdVWgpYiw+IXe9qsade5aj8b7r9Dghpb8GL/s7UgbU8BXx75bNe1/ dp2h8/gfKgu+qXC8/bEOAcmbiHO7ILRMECJy11lY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:27 +0200 Message-Id: <20190106023328.10989-10-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 10/11] test: Add timer 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: Sun, 06 Jan 2019 02:32:30 -0000 Signed-off-by: Laurent Pinchart --- test/meson.build | 1 + test/timer.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 test/timer.cpp diff --git a/test/meson.build b/test/meson.build index 4d334025f3d3..12e7b30d4f73 100644 --- a/test/meson.build +++ b/test/meson.build @@ -5,6 +5,7 @@ subdir('media_device') public_tests = [ ['list-cameras', 'list.cpp'], ['signal', 'signal.cpp'], + ['timer', 'timer.cpp'], ] internal_tests = [ diff --git a/test/timer.cpp b/test/timer.cpp new file mode 100644 index 000000000000..6a3cda70bef4 --- /dev/null +++ b/test/timer.cpp @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * timer.cpp - Timer test + */ + +#include + +#include +#include +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class ManagedTimer : public Timer +{ +public: + ManagedTimer() : Timer() + { + timeout.connect(this, &ManagedTimer::timeoutHandler); + } + + void start(int msec) + { + interval_ = msec; + clock_gettime(CLOCK_MONOTONIC, &start_); + expiration_ = { 0, 0 }; + + Timer::start(msec); + } + + int jitter() + { + int duration = (expiration_.tv_sec - start_.tv_sec) * 1000; + duration += (expiration_.tv_nsec - start_.tv_nsec) / 1000000; + return abs(duration - interval_); + } + +private: + void timeoutHandler(Timer *timer) + { + clock_gettime(CLOCK_MONOTONIC, &expiration_); + } + + int interval_; + struct timespec start_; + struct timespec expiration_; +}; + +class TimerTest : public Test +{ +protected: + int init() + { + return 0; + } + + int run() + { + EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); + ManagedTimer timer; + ManagedTimer timer2; + + /* Timer expiration. */ + timer.start(1000); + + if (!timer.isRunning()) { + cout << "Timer expiration test failed" << endl; + return TestFail; + } + + dispatcher->processEvents(); + + if (timer.isRunning() || timer.jitter() > 50) { + cout << "Timer expiration test failed" << endl; + return TestFail; + } + + /* Timer restart. */ + timer.start(500); + + if (!timer.isRunning()) { + cout << "Timer restart test failed" << endl; + return TestFail; + } + + dispatcher->processEvents(); + + if (timer.isRunning() || timer.jitter() > 50) { + cout << "Timer restart test failed" << endl; + return TestFail; + } + + /* Two timers. */ + timer.start(1000); + timer2.start(300); + + dispatcher->processEvents(); + + if (!timer.isRunning()) { + cout << "Two timers test failed" << endl; + return TestFail; + } + + if (timer2.jitter() > 50) { + cout << "Two timers test failed" << endl; + return TestFail; + } + + dispatcher->processEvents(); + + if (timer.jitter() > 50) { + cout << "Two timers test failed" << endl; + return TestFail; + } + + /* Restart timer before expiration. */ + timer.start(1000); + timer2.start(300); + + dispatcher->processEvents(); + + if (timer2.jitter() > 50) { + cout << "Two timers test failed" << endl; + return TestFail; + } + + timer.start(1000); + + dispatcher->processEvents(); + + if (timer.jitter() > 50) { + cout << "Two timers test failed" << endl; + return TestFail; + } + + return TestPass; + } + + void cleanup() + { + } +}; + +TEST_REGISTER(TimerTest) From patchwork Sun Jan 6 02:33:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 160 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7857560B49 for ; Sun, 6 Jan 2019 03:32:30 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 16094513 for ; Sun, 6 Jan 2019 03:32:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546741950; bh=Tc4Wf0VAQO5mKzYZoUU69kcuFeSmmSDiRQUyEsy9xr4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=fBN6XlzAPZfJVUC9038u93Ont0R04iyUCBfd5KFULkISPa5Ic4LhxygBiWMyKq/Nz kZXeLGiE9bcaJPN20hsm97jEJr1Ogvcmc7GiznG/oeu2MqNRbjCxrvM2I7gpR0IGSH NO8toyK1vuFAWI7F1948ztPNqFk34QrNN1xp5m+I= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 6 Jan 2019 04:33:28 +0200 Message-Id: <20190106023328.10989-11-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> References: <20190106023328.10989-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 11/11] test: Add event notifier 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: Sun, 06 Jan 2019 02:32:31 -0000 Signed-off-by: Laurent Pinchart --- test/event.cpp | 117 +++++++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 2 files changed, 118 insertions(+) create mode 100644 test/event.cpp diff --git a/test/event.cpp b/test/event.cpp new file mode 100644 index 000000000000..52bc0c7e77f5 --- /dev/null +++ b/test/event.cpp @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event.cpp - Event test + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class EventTest : public Test +{ +protected: + void readReady(EventNotifier *notifier) + { + size_ = read(notifier->fd(), data_, sizeof(data_)); + notified_ = true; + } + + int init() + { + return pipe(pipefd_); + } + + int run() + { + EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); + std::string data("H2G2"); + Timer timeout; + + EventNotifier readNotifier(pipefd_[0], EventNotifier::Read); + readNotifier.activated.connect(this, &EventTest::readReady); + + /* Test read notification with data. */ + memset(data_, 0, sizeof(data_)); + size_ = 0; + + write(pipefd_[1], data.data(), data.size()); + + timeout.start(100); + dispatcher->processEvents(); + timeout.stop(); + + if (static_cast(size_) != data.size()) { + cout << "Event notifier read ready test failed" << endl; + return TestFail; + } + + /* Test read notification without data. */ + notified_ = false; + + timeout.start(100); + dispatcher->processEvents(); + timeout.stop(); + + if (notified_) { + cout << "Event notifier read no ready test failed" << endl; + return TestFail; + } + + /* Test read notifier disabling. */ + notified_ = false; + readNotifier.setEnabled(false); + + write(pipefd_[1], data.data(), data.size()); + + timeout.start(100); + dispatcher->processEvents(); + timeout.stop(); + + if (notified_) { + cout << "Event notifier read disabling failed" << endl; + return TestFail; + } + + /* Test read notifier enabling. */ + notified_ = false; + readNotifier.setEnabled(true); + + timeout.start(100); + dispatcher->processEvents(); + timeout.stop(); + + if (!notified_) { + cout << "Event notifier read enabling test failed" << endl; + return TestFail; + } + + return TestPass; + } + + void cleanup() + { + close(pipefd_[0]); + close(pipefd_[1]); + } + +private: + int pipefd_[2]; + + bool notified_; + char data_[16]; + ssize_t size_; +}; + +TEST_REGISTER(EventTest) diff --git a/test/meson.build b/test/meson.build index 12e7b30d4f73..e923404502b7 100644 --- a/test/meson.build +++ b/test/meson.build @@ -3,6 +3,7 @@ subdir('libtest') subdir('media_device') public_tests = [ + ['event', 'event.cpp'], ['list-cameras', 'list.cpp'], ['signal', 'signal.cpp'], ['timer', 'timer.cpp'],