From patchwork Mon Jan 7 23:11:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 164 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 D5AE760B2F for ; Tue, 8 Jan 2019 00:10:47 +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 5D85AE4E for ; Tue, 8 Jan 2019 00:10:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902647; bh=kNEiHO6mGEMjpYrC1VfFDRa+lwkG3MBWL+XDWZWxNGE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Nl3sQA4TgTJ4+sHM67EYvJ5Xqfj4Vax8QPw49XMLzHRElLc3q+tWKES/SIm6pHsEg 8ZrpSLhPaWuDyzKxlQHbqiq8gt5L5oRI/9ZcMJtCH4rpIblDokz9WqyEI3A9aEXCys E/P9A/Kn0xCD/LezngyCwujsvS/HQ5aR8IlcZBuI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:41 +0200 Message-Id: <20190107231151.23291-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:48 -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 Reviewed-by: Kieran Bingham --- 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 Mon Jan 7 23:11:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 165 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 1C51A60B2F for ; Tue, 8 Jan 2019 00:10:48 +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 A91BCE51 for ; Tue, 8 Jan 2019 00:10:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902647; bh=1iJndoZVAjgGU+M99GCZ2vKTWSQf5zRnJfjSlaFkzvE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=ceChrHHLXVXW7nm0G4+VbJS/pkSox5zFm4mfxuXhx7YolfJ+I/hFlZ0nsQI7Fetbi +hCZ8bLQON9n4e+6m4/mSfrGaL9/tI6+raAU+2GmfPF26cM9csufai38sEETPLDupc 1INHcZJf9GfIZjFdxdFv8WFbtdcw5DMmbaagTTI0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:42 +0200 Message-Id: <20190107231151.23291-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:48 -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 Reviewed-by: Kieran Bingham --- Changes since v1: - Fix syntax error in ASSERT() macro - Fix NDEBUG conditional compilation - Fix typo in documentation - Fix ASSERT() condition check --- 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..c1af3741ffee 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() +#ifndef 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..6785d371449e 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 fails + * + * 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 Mon Jan 7 23:11:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 166 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 843C4600CC for ; Tue, 8 Jan 2019 00:10:48 +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 087E4530 for ; Tue, 8 Jan 2019 00:10:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902648; bh=rNX/4JQBdYqjmk8vMJqf81o78lM0u93vAU04+tVxWiE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=EFHq87tgplne4TllGU7NQHZMCxpf+CQgoktUhgGLCtMSG5TLt2JO5LJlMS8ONQWBL gtnBBPCKRPiamPBqnP/ov4IOPB7nlGMTaST25iU9bxhvXzPvdxkVb6GqbUdnI7XoL4 xMmp00Ib9Oj0BNMQSNW/8GGqmvamzlyi9XXnQnlg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:43 +0200 Message-Id: <20190107231151.23291-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:48 -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 6785d371449e..c1ec55618ea3 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 Mon Jan 7 23:11:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 167 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D2A1B60B31 for ; Tue, 8 Jan 2019 00:10:48 +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 707A8E4E for ; Tue, 8 Jan 2019 00:10:48 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902648; bh=UJI6UaC1EAZPRNdZguGohhk0zzUbkT6HuCDJAn3gmjM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=l8YXNdCfEDginB8ahFaBQ6kQExpj3/sKlrKqCrwj+1K46iJp6ByonRzTG2reQMca9 yOkpzVACUvYCoaBy8sEJjZxkALGLEw/pauhbyXrHIoHZqV3qCHoVhpDA5i6agQkDq3 ltlFuYVHmf2OWWdxdWDTOnUuZh5sBVuqkU1hzZHk= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:44 +0200 Message-Id: <20190107231151.23291-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:49 -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 --- Changes since v1: - Delete copy constructor and assignment operator - Fix documentation style issue --- include/libcamera/camera_manager.h | 8 ++++++-- src/libcamera/camera_manager.cpp | 15 +++++++++++++++ test/list.cpp | 7 +------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 2768a5bd2384..e14da0f893b3 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -19,15 +19,19 @@ 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(); + CameraManager(const CameraManager &) = delete; + void operator=(const CameraManager &) = delete; + DeviceEnumerator *enumerator_; std::vector pipes_; }; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 50a805fc665c..1a9d2f38e3b9 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 shall 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..e2026c99c5b8 100644 --- a/test/list.cpp +++ b/test/list.cpp @@ -19,10 +19,7 @@ class ListTest : public Test protected: int init() { - cm = new CameraManager(); - if (!cm) - return -ENOMEM; - + cm = CameraManager::instance(); cm->start(); return 0; @@ -43,8 +40,6 @@ protected: void cleanup() { cm->stop(); - - delete cm; } private: From patchwork Mon Jan 7 23:11:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 168 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 31AFD60B2F for ; Tue, 8 Jan 2019 00:10:49 +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 C18D3530 for ; Tue, 8 Jan 2019 00:10:48 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902648; bh=Tnpr9gZIVtVejz0SiinFSY7a4IvfEApBUvEcSHgVzKk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=IO/BmILEzRf9Y04IP/s9eESSrjhYJubpnrSXVBNUj1g+t3m7HBBqcU4i1R8nbfFO2 M4jRIgIp9qT8KTrsx+4D97eFga6VyVEMpy7rB91yNevcu2c6BeEAxQlIMFSunwYKJF GHx17M28P3b16J9i4EqPsrv0dhRi078MSejHM7/I= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:45 +0200 Message-Id: <20190107231151.23291-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:49 -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 Reviewed-by: Jacopo Mondi --- Changes since v1: - Improve documentation - Add support for static slots --- Documentation/Doxyfile.in | 4 +- include/libcamera/meson.build | 1 + include/libcamera/signal.h | 154 ++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + src/libcamera/signal.cpp | 84 +++++++++++++++++++ 5 files changed, 243 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..aac20839c837 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -860,7 +860,9 @@ 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::SlotMember \ + libcamera::SlotStatic # 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..458db1ac0060 --- /dev/null +++ b/include/libcamera/signal.h @@ -0,0 +1,154 @@ +/* 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 SlotMember : public SlotBase +{ +public: + SlotMember(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 SlotStatic : public SlotBase +{ +public: + SlotStatic(void(*func)(Args...)) + : SlotBase(nullptr), func_(func) { } + + void invoke(Args... args) { (*func_)(args...); } + +private: + friend class Signal; + void(*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 SlotMember(object, func)); + } + + void connect(void(*func)(Args...)) + { + slots_.push_back(new SlotStatic(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 the obj_ pointer matches the object types must + * match, so we can safely case to SlotMember. + */ + if (slot->obj_ == object && + reinterpret_cast *>(slot)->func_ == func) { + iter = slots_.erase(iter); + delete slot; + } else { + ++iter; + } + } + } + + void disconnect(void(*func)(Args...)) + { + for (auto iter = slots_.begin(); iter != slots_.end(); ) { + SlotBase *slot = *iter; + if (slot->obj_ == nullptr && + 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..0fd3bb2a34a1 --- /dev/null +++ b/src/libcamera/signal.cpp @@ -0,0 +1,84 @@ +/* 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. + * + * Signals model events that can be observed from objects unrelated to the event + * source. Slots are functions that are called in response to a signal. Signals + * can be connected to and disconnected from slots dynamically at runtime. When + * a signal is emitted, all connected slots are called sequentially in the order + * they have been connected. + * + * Signals are defined with zero, one or more typed parameters. They are emitted + * with a value for each of the parameters, and those values are passed to the + * connected slots. + * + * Slots are normal static or class member functions. In order to be connected + * to a signal, their signature must match the signal type (taking the same + * arguments as the signal and returning void). + * + * Connecting a signal to a slot results in the slot being called with the + * arguments passed to the emit() function when the signal is emitted. Multiple + * slots can be connected to the same signal, and multiple signals can connected + * to the same slot. Duplicate connections between a signal and a slot are + * allowed and result in the slot being called multiple times for the same + * signal emission. + */ + +/** + * \fn Signal::connect(T *object, void(T::*func)(Args...)) + * \brief Connect the signal to a member function slot + * \param object The slot object pointer + * \param func The slot member function + */ + +/** + * \fn Signal::connect(void(*func)(Args...)) + * \brief Connect the signal to a static function slot + * \param func The slot static function + */ + +/** + * \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 the \a object slot member function \a func + */ + +/** + * \fn Signal::disconnect(void(*func)(Args...)) + * \brief Disconnect the signal from the slot static function \a func + */ + +/** + * \fn Signal::emit(Args... args) + * \brief Emit the signal and call all connected slots + * + * Emitting a signal calls all connected slots synchronously and sequentially in + * the order the slots have been connected. The arguments passed to the emit() + * function are passed to the slot functions unchanged. If a slot modifies one + * of the arguments (when passed by pointer or reference), the modification is + * thus visible to all subsequently called slots. + */ + +} /* namespace libcamera */ From patchwork Mon Jan 7 23:11:46 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 169 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 99A1B60B2F for ; Tue, 8 Jan 2019 00:10:49 +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 22DF8E4E for ; Tue, 8 Jan 2019 00:10:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902649; bh=ZdJ3cpMu3rZEsVfhygsfZTk0wbjDh5qBWDixSWLxIQg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=mFDOSgB1QgEwTztNm2F4exmVArMgzwmcSS2EymCwUOhGxQjCzY2hjMU1wcKOBQunG a92ri7ZLP3QLMjjhnOkNYqJTV2usHlS7Mfo+DrblDIOM30WUyVz9Gbz3t+oC5qEN77 O2y/2vy2vdWx8S5o15Kpt94qJU1SxhX1AiOh5JB4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:46 +0200 Message-Id: <20190107231151.23291-7-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:50 -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 Reviewed-by: Jacopo Mondi --- Changes since v1: - Rename EventNotifier::isEnabled() to EventNotifier::enabled() - Fix typo in documentation --- 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 | 103 +++++++++++++++++++++++ src/libcamera/event_notifier.cpp | 121 +++++++++++++++++++++++++++ src/libcamera/meson.build | 3 + src/libcamera/timer.cpp | 105 +++++++++++++++++++++++ 11 files changed, 496 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 e14da0f893b3..15e7c162032a 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,13 +28,19 @@ public: static CameraManager *instance(); + void setEventDispatcher(EventDispatcher *dispatcher); + EventDispatcher *eventDispatcher(); + private: CameraManager(); CameraManager(const CameraManager &) = delete; void operator=(const CameraManager &) = delete; + ~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..1e9b6da1340c --- /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_notifier.h - File descriptor 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 enabled() 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 1a9d2f38e3b9..cbfd977f1e3c 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..b893ab046985 --- /dev/null +++ b/src/libcamera/event_dispatcher.cpp @@ -0,0 +1,103 @@ +/* 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 and results in undefined behaviour. + */ + +/** + * \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 and results in + * undefined behaviour. + */ + +/** + * \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..8dc45a546a11 --- /dev/null +++ b/src/libcamera/event_notifier.cpp @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * event_notifier.cpp - File descriptor 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 constructing 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. + * + * Creating multiple notifiers of the same type for the same file descriptor is + * not allowed and results in undefined behaviour. + * + * 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::enabled() + * \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 Mon Jan 7 23:11:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 170 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 EFDD760B3A for ; Tue, 8 Jan 2019 00:10:49 +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 86F16E51 for ; Tue, 8 Jan 2019 00:10:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902649; bh=SiRsLN0Zc/8GSpeiZorpxXuDQ+BXneo3nlJDf1BZCEM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=bWmuUJmmBx4zO2qDRw0R2qodq/KS2i8hluOhDMOvq1ZzQIoP1U2EtpHEmGWX/IclI j0IWFVN/b2q+skj2oP5WQnuyiLKoG1sTlnpxq/vY6mkokvAsJvIAAgrNXoqnlWb+CA M42A2Fdoj5l5KWo4JZnf+H6cROH8IbTpr0Ap3JW0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:47 +0200 Message-Id: <20190107231151.23291-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:50 -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 --- Changes since v1: - Fix ASSERT() condition check - Ignore duplicate notifiers - Clarify warning messages for event notifier (un)registration errors --- include/libcamera/libcamera.h | 1 + src/libcamera/camera_manager.cpp | 19 +- src/libcamera/event_dispatcher_poll.cpp | 234 ++++++++++++++++++ src/libcamera/include/event_dispatcher_poll.h | 50 ++++ src/libcamera/meson.build | 2 + 5 files changed, 301 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 cbfd977f1e3c..348ea2fedf64 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..0cd08b2b4cc1 --- /dev/null +++ b/src/libcamera/event_dispatcher_poll.cpp @@ -0,0 +1,234 @@ +/* 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) << "Ignoring duplicate " << notifierType(type) + << " notifier for fd " << notifier->fd(); + return; + } + + 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) << notifierType(type) << " notifier for fd " + << notifier->fd() << " is not registered"; + 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 Mon Jan 7 23:11:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 171 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A1C260B3A for ; Tue, 8 Jan 2019 00:10:50 +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 DDC82E4E for ; Tue, 8 Jan 2019 00:10:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902650; bh=q/MK6T7+X50nZAAvplPMZSHn42sNXzDv4+8ni+PC0l8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=B3Zi9E5YdFPj2X+3YrN0R0PcVAcousV7Obkzsp3bdvvy6nqZa24gQpzMTAx6PZ0yG rnyYPkIL8YEfr0RYaut6y2C+Pf1aAGmEkyW6O4KRskXx1fsQx/OTOQjoapatZLXaJR tXQ8bAsjy3+vQZ1jsJrjAs/lEzLJVlxmKN8LO9zM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:48 +0200 Message-Id: <20190107231151.23291-9-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:50 -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 --- Changes since v1: - Rename list.cpp to list-cameras.cpp --- test/{list.cpp => list-cameras.cpp} | 0 test/meson.build | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename test/{list.cpp => list-cameras.cpp} (100%) diff --git a/test/list.cpp b/test/list-cameras.cpp similarity index 100% rename from test/list.cpp rename to test/list-cameras.cpp diff --git a/test/meson.build b/test/meson.build index 184a7eeb5e27..30350d2219a1 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-cameras.cpp'], ] internal_tests = [ From patchwork Mon Jan 7 23:11:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 172 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2B17760B41 for ; Tue, 8 Jan 2019 00:10:51 +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 755D7E51 for ; Tue, 8 Jan 2019 00:10:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902650; bh=IU6XMvY5UCWwLLrR458LSJmrgzXqYmdrBFGJZp+ZtZQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=H+MPVXaHzh/eIno7+hctn+RcZsgLhZem3jw4N3/OkhEFkAnZld6rfpey2vQFJsdBb wijjTJoT6tj76GsjFkSohty9E6n2yFF+L6hsr2ZDCU+/nzDw8VYnuqiI2LDaeMKvwP Bt8GkoYSDg96jq07Of/Wc+Jmewg9abodDYmq5Tkg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:49 +0200 Message-Id: <20190107231151.23291-10-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:51 -0000 Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- test/meson.build | 1 + test/signal.cpp | 159 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 test/signal.cpp diff --git a/test/meson.build b/test/meson.build index 30350d2219a1..4000bd51808a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -4,6 +4,7 @@ subdir('media_device') public_tests = [ ['list-cameras', 'list-cameras.cpp'], + ['signal', 'signal.cpp'], ] internal_tests = [ diff --git a/test/signal.cpp b/test/signal.cpp new file mode 100644 index 000000000000..ab69ca1b663d --- /dev/null +++ b/test/signal.cpp @@ -0,0 +1,159 @@ +/* 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; + +static int valueStatic_ = 0; + +static void slotStatic(int value) +{ + valueStatic_ = value; +} + +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_)); + valueStatic_ = 0; + signalInt_.connect(this, &SignalTest::slotInteger1); + signalInt_.connect(this, &SignalTest::slotInteger2); + signalInt_.connect(&slotStatic); + signalInt_.emit(42); + + if (values_[0] != 42 || values_[1] != 42 || values_[2] != 0 || + valueStatic_ != 42) { + 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 Mon Jan 7 23:11:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 173 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 6DC2E60B3B for ; Tue, 8 Jan 2019 00:10:51 +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 0C81D530 for ; Tue, 8 Jan 2019 00:10:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902651; bh=EFC44LTYh6j6cf8EntrQmYs5h81lgV/hX2CyS04vst4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=e7gulBaor8unftMQ1vxzhjy9GVC0qUpc1fVRKYcVhy2qvEY6tmglmPfjg7u9hS0sn MeZ/S/G79kQrs/2hN6/+CLv13Jbu+Kk6G2ICM3vXpHp4/+KP2/pPCccA+upC51G3Wx /7pW+layo+TjVvBDKEX6m7eYXQH2awqGCp91eono= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:50 +0200 Message-Id: <20190107231151.23291-11-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:51 -0000 Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- 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 4000bd51808a..f26c8701d827 100644 --- a/test/meson.build +++ b/test/meson.build @@ -5,6 +5,7 @@ subdir('media_device') public_tests = [ ['list-cameras', 'list-cameras.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 Mon Jan 7 23:11:51 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 174 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 C11F860B50 for ; Tue, 8 Jan 2019 00:10:51 +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 5B20117CF for ; Tue, 8 Jan 2019 00:10:51 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546902651; bh=HNfymYSEmXhYJ09QR+EBjEfNVr259lR2sHWY52LI+38=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Pd0kGO41oTSbv7O4ItrVtF+JeJ/5/1MICrXtGxLW5jDV2s+MgO9a3hGaUqxL3Yjf+ hsGT1kHSsfd2D1T+ljJeWg7gRXo5K8Kt8SukUBsdjbXIPuBNj5ugEENSaqiFHIgl4V j3bCPi3/vX/IY+iNWTyUv/A+8WClDV3Rb5wYOk1o= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jan 2019 01:11:51 +0200 Message-Id: <20190107231151.23291-12-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> References: <20190107231151.23291-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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: Mon, 07 Jan 2019 23:10:51 -0000 Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- 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 f26c8701d827..32152888a55e 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-cameras.cpp'], ['signal', 'signal.cpp'], ['timer', 'timer.cpp'],