summaryrefslogtreecommitdiff
path: root/src/libcamera/base
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcamera/base')
-rw-r--r--src/libcamera/base/bound_method.cpp109
-rw-r--r--src/libcamera/base/class.cpp181
-rw-r--r--src/libcamera/base/event_dispatcher.cpp116
-rw-r--r--src/libcamera/base/event_dispatcher_poll.cpp307
-rw-r--r--src/libcamera/base/event_notifier.cpp141
-rw-r--r--src/libcamera/base/file.cpp465
-rw-r--r--src/libcamera/base/log.cpp998
-rw-r--r--src/libcamera/base/meson.build49
-rw-r--r--src/libcamera/base/message.cpp166
-rw-r--r--src/libcamera/base/object.cpp300
-rw-r--r--src/libcamera/base/semaphore.cpp103
-rw-r--r--src/libcamera/base/signal.cpp179
-rw-r--r--src/libcamera/base/thread.cpp705
-rw-r--r--src/libcamera/base/timer.cpp185
-rw-r--r--src/libcamera/base/utils.cpp463
15 files changed, 4467 insertions, 0 deletions
diff --git a/src/libcamera/base/bound_method.cpp b/src/libcamera/base/bound_method.cpp
new file mode 100644
index 00000000..3ecec51c
--- /dev/null
+++ b/src/libcamera/base/bound_method.cpp
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * bound_method.cpp - Method bind and invocation
+ */
+
+#include <libcamera/base/bound_method.h>
+#include <libcamera/base/message.h>
+#include <libcamera/base/semaphore.h>
+#include <libcamera/base/thread.h>
+
+/**
+ * \file base/bound_method.h
+ * \brief Method bind and invocation
+ */
+
+namespace libcamera {
+
+/**
+ * \enum ConnectionType
+ * \brief Connection type for asynchronous communication
+ *
+ * This enumeration describes the possible types of asynchronous communication
+ * between a sender and a receiver. It applies to Signal::emit() and
+ * Object::invokeMethod().
+ *
+ * \var ConnectionTypeAuto
+ * \brief If the sender and the receiver live in the same thread,
+ * ConnectionTypeDirect is used. Otherwise ConnectionTypeQueued is used.
+ *
+ * \var ConnectionTypeDirect
+ * \brief The receiver is invoked immediately and synchronously in the sender's
+ * thread.
+ *
+ * \var ConnectionTypeQueued
+ * \brief The receiver is invoked asynchronously
+ *
+ * Invoke the receiver asynchronously in its thread when control returns to the
+ * thread's event loop. The sender proceeds without waiting for the invocation
+ * to complete.
+ *
+ * \var ConnectionTypeBlocking
+ * \brief The receiver is invoked synchronously
+ *
+ * If the sender and the receiver live in the same thread, this is equivalent to
+ * ConnectionTypeDirect. Otherwise, the receiver is invoked asynchronously in
+ * its thread when control returns to the thread's event loop. The sender
+ * blocks until the receiver signals the completion of the invocation.
+ */
+
+/**
+ * \brief Invoke the bound method with packed arguments
+ * \param[in] pack Packed arguments
+ * \param[in] deleteMethod True to delete \a this bound method instance when
+ * method invocation completes
+ *
+ * The bound method stores its return value, if any, in the arguments \a pack.
+ * For direct and blocking invocations, this is performed synchronously, and
+ * the return value contained in the pack may be used. For queued invocations,
+ * the return value is stored at an undefined point of time and shall thus not
+ * be used by the caller.
+ *
+ * \return True if the return value contained in the \a pack may be used by the
+ * caller, false otherwise
+ */
+bool BoundMethodBase::activatePack(std::shared_ptr<BoundMethodPackBase> pack,
+ bool deleteMethod)
+{
+ ConnectionType type = connectionType_;
+ if (type == ConnectionTypeAuto) {
+ if (Thread::current() == object_->thread())
+ type = ConnectionTypeDirect;
+ else
+ type = ConnectionTypeQueued;
+ } else if (type == ConnectionTypeBlocking) {
+ if (Thread::current() == object_->thread())
+ type = ConnectionTypeDirect;
+ }
+
+ switch (type) {
+ case ConnectionTypeDirect:
+ default:
+ invokePack(pack.get());
+ if (deleteMethod)
+ delete this;
+ return true;
+
+ case ConnectionTypeQueued: {
+ std::unique_ptr<Message> msg =
+ std::make_unique<InvokeMessage>(this, pack, nullptr, deleteMethod);
+ object_->postMessage(std::move(msg));
+ return false;
+ }
+
+ case ConnectionTypeBlocking: {
+ Semaphore semaphore;
+
+ std::unique_ptr<Message> msg =
+ std::make_unique<InvokeMessage>(this, pack, &semaphore, deleteMethod);
+ object_->postMessage(std::move(msg));
+
+ semaphore.acquire();
+ return true;
+ }
+ }
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/class.cpp b/src/libcamera/base/class.cpp
new file mode 100644
index 00000000..26b49677
--- /dev/null
+++ b/src/libcamera/base/class.cpp
@@ -0,0 +1,181 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * class.cpp - Utilities and helpers for classes
+ */
+
+#include <libcamera/base/class.h>
+
+/**
+ * \file class.h
+ * \brief Utilities to help constructing class interfaces
+ *
+ * The extensible class can be inherited to create public classes with stable
+ * ABIs.
+ */
+
+namespace libcamera {
+
+/**
+ * \def LIBCAMERA_DISABLE_COPY
+ * \brief Disable copy construction and assignment of the \a klass
+ * \param klass The name of the class
+ *
+ * Example usage:
+ * \code{.cpp}
+ * class NonCopyable
+ * {
+ * public:
+ * NonCopyable();
+ * ...
+ *
+ * private:
+ * LIBCAMERA_DISABLE_COPY(NonCopyable)
+ * };
+ * \endcode
+ */
+
+/**
+ * \def LIBCAMERA_DISABLE_MOVE
+ * \brief Disable move construction and assignment of the \a klass
+ * \param klass The name of the class
+ *
+ * Example usage:
+ * \code{.cpp}
+ * class NonMoveable
+ * {
+ * public:
+ * NonMoveable();
+ * ...
+ *
+ * private:
+ * LIBCAMERA_DISABLE_MOVE(NonMoveable)
+ * };
+ * \endcode
+ */
+
+/**
+ * \def LIBCAMERA_DISABLE_COPY_AND_MOVE
+ * \brief Disable copy and move construction and assignment of the \a klass
+ * \param klass The name of the class
+ *
+ * Example usage:
+ * \code{.cpp}
+ * class NonCopyableNonMoveable
+ * {
+ * public:
+ * NonCopyableNonMoveable();
+ * ...
+ *
+ * private:
+ * LIBCAMERA_DISABLE_COPY_AND_MOVE(NonCopyableNonMoveable)
+ * };
+ * \endcode
+ */
+
+/**
+ * \def LIBCAMERA_DECLARE_PRIVATE
+ * \brief Declare private data for a public class
+ *
+ * The LIBCAMERA_DECLARE_PRIVATE() macro plumbs the infrastructure necessary to
+ * make a class manage its private data through a d-pointer. It shall be used at
+ * the very top of the class definition.
+ */
+
+/**
+ * \def LIBCAMERA_DECLARE_PUBLIC
+ * \brief Declare public data for a private class
+ * \param klass The public class name
+ *
+ * The LIBCAMERA_DECLARE_PUBLIC() macro is the counterpart of
+ * LIBCAMERA_DECLARE_PRIVATE() to be used in the private data class. It shall be
+ * used at the very top of the private class definition, with the public class
+ * name passed as the \a klass parameter.
+ */
+
+/**
+ * \def LIBCAMERA_O_PTR()
+ * \brief Retrieve the public instance corresponding to the private data
+ *
+ * This macro is used in any member function of the private data class to access
+ * the public class instance corresponding to the private data.
+ */
+
+/**
+ * \class Extensible
+ * \brief Base class to manage private data through a d-pointer
+ *
+ * The Extensible class provides a base class to implement the
+ * <a href="https://wiki.qt.io/D-Pointer">d-pointer</a> design pattern (also
+ * known as <a href="https://en.wikipedia.org/wiki/Opaque_pointer">opaque pointer</a>
+ * or <a href="https://en.cppreference.com/w/cpp/language/pimpl">pImpl idiom</a>).
+ * It helps creating public classes that can be extended without breaking their
+ * ABI. Such classes store their private data in a separate private data object,
+ * referenced by a pointer in the public class (hence the name d-pointer).
+ *
+ * Classes that follow this design pattern are referred herein as extensible
+ * classes. To be extensible, a class PublicClass shall:
+ *
+ * - inherit from the Extensible class or from another extensible class
+ * - invoke the LIBCAMERA_DECLARE_PRIVATE() macro at the very top of the class
+ * definition
+ * - define a private data class named PublicClass::Private that inherits from
+ * the Private data class of the base class
+ * - invoke the LIBCAMERA_DECLARE_PUBLIC() macro at the very top of the Private
+ * data class definition
+ * - pass a pointer to a newly allocated Private data object to the constructor
+ * of the base class
+ *
+ * Additionally, if the PublicClass is not final, it shall expose one or more
+ * constructors that takes a pointer to a Private data instance, to be used by
+ * derived classes.
+ *
+ * The Private class is fully opaque to users of the libcamera public API.
+ * Internally, it can be kept private to the implementation of PublicClass, or
+ * be exposed to other classes. In the latter case, the members of the Private
+ * class need to be qualified with appropriate access specifiers. The
+ * PublicClass and Private classes always have full access to each other's
+ * protected and private members.
+ *
+ * The PublicClass exposes its Private data pointer through the _d() function.
+ */
+
+/**
+ * \brief Construct an instance of an Extensible class
+ * \param[in] d Pointer to the private data instance
+ */
+Extensible::Extensible(Extensible::Private *d)
+ : d_(d)
+{
+}
+
+/**
+ * \var Extensible::d_
+ * \brief Pointer to the private data instance
+ */
+
+/**
+ * \class Extensible::Private
+ * \brief Base class for private data managed through a d-pointer
+ */
+
+/**
+ * \brief Construct an instance of an Extensible class private data
+ * \param[in] o Pointer to the public class object
+ */
+Extensible::Private::Private(Extensible *o)
+ : o_(o)
+{
+}
+
+Extensible::Private::~Private()
+{
+}
+
+/**
+ * \var Extensible::Private::o_
+ * \brief Pointer to the public class object
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/event_dispatcher.cpp b/src/libcamera/base/event_dispatcher.cpp
new file mode 100644
index 00000000..4be89e81
--- /dev/null
+++ b/src/libcamera/base/event_dispatcher.cpp
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_dispatcher.cpp - Event dispatcher
+ */
+
+#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/log.h>
+
+/**
+ * \file base/event_dispatcher.h
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Event)
+
+/**
+ * \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 registers
+ * 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 registers 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[in] 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[in] 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[in] 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[in] 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.
+ */
+
+/**
+ * \fn EventDispatcher::interrupt()
+ * \brief Interrupt any running processEvents() call as soon as possible
+ *
+ * Calling this function interrupts any blocking processEvents() call in
+ * progress. The processEvents() function will return as soon as possible,
+ * after processing pending timers and events. If processEvents() isn't in
+ * progress, it will be interrupted immediately the next time it gets called.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/event_dispatcher_poll.cpp b/src/libcamera/base/event_dispatcher_poll.cpp
new file mode 100644
index 00000000..5839373a
--- /dev/null
+++ b/src/libcamera/base/event_dispatcher_poll.cpp
@@ -0,0 +1,307 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_dispatcher_poll.cpp - Poll-based event dispatcher
+ */
+
+#include <libcamera/base/event_dispatcher_poll.h>
+
+#include <algorithm>
+#include <chrono>
+#include <iomanip>
+#include <poll.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include <libcamera/base/event_notifier.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/thread.h>
+#include <libcamera/base/timer.h>
+#include <libcamera/base/utils.h>
+
+/**
+ * \file base/event_dispatcher_poll.h
+ */
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Event)
+
+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()
+ : processingEvents_(false)
+{
+ /*
+ * Create the event fd. Failures are fatal as we can't implement an
+ * interruptible dispatcher without the fd.
+ */
+ eventfd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+ if (eventfd_ < 0)
+ LOG(Event, Fatal) << "Unable to create eventfd";
+}
+
+EventDispatcherPoll::~EventDispatcherPoll()
+{
+ close(eventfd_);
+}
+
+void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier)
+{
+ EventNotifierSetPoll &set = notifiers_[notifier->fd()];
+ EventNotifier::Type type = notifier->type();
+
+ if (set.notifiers[type] && set.notifiers[type] != notifier) {
+ LOG(Event, 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(Event, Warning)
+ << notifierType(type) << " notifier for fd "
+ << notifier->fd() << " is not registered";
+ return;
+ }
+
+ set.notifiers[type] = nullptr;
+
+ /*
+ * Don't race with event processing if this method is called from an
+ * event notifier. The notifiers_ entry will be erased by
+ * processEvents().
+ */
+ if (processingEvents_)
+ return;
+
+ 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;
+
+ Thread::current()->dispatchMessages();
+
+ /* Create the pollfd array. */
+ std::vector<struct pollfd> pollfds;
+ pollfds.reserve(notifiers_.size() + 1);
+
+ for (auto notifier : notifiers_)
+ pollfds.push_back({ notifier.first, notifier.second.events(), 0 });
+
+ pollfds.push_back({ eventfd_, POLLIN, 0 });
+
+ /* Wait for events and process notifiers and timers. */
+ do {
+ ret = poll(&pollfds);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret < 0) {
+ ret = -errno;
+ LOG(Event, Warning) << "poll() failed with " << strerror(-ret);
+ } else if (ret > 0) {
+ processInterrupt(pollfds.back());
+ pollfds.pop_back();
+ processNotifiers(pollfds);
+ }
+
+ processTimers();
+}
+
+void EventDispatcherPoll::interrupt()
+{
+ uint64_t value = 1;
+ ssize_t ret = write(eventfd_, &value, sizeof(value));
+ if (ret != sizeof(value)) {
+ if (ret < 0)
+ ret = -errno;
+ LOG(Event, Error)
+ << "Failed to interrupt event dispatcher ("
+ << ret << ")";
+ }
+}
+
+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;
+}
+
+int EventDispatcherPoll::poll(std::vector<struct pollfd> *pollfds)
+{
+ /* Compute the timeout. */
+ Timer *nextTimer = !timers_.empty() ? timers_.front() : nullptr;
+ struct timespec timeout;
+
+ if (nextTimer) {
+ utils::time_point now = utils::clock::now();
+
+ if (nextTimer->deadline() > now)
+ timeout = utils::duration_to_timespec(nextTimer->deadline() - now);
+ else
+ timeout = { 0, 0 };
+
+ LOG(Event, Debug)
+ << "timeout " << timeout.tv_sec << "."
+ << std::setfill('0') << std::setw(9)
+ << timeout.tv_nsec;
+ }
+
+ return ppoll(pollfds->data(), pollfds->size(),
+ nextTimer ? &timeout : nullptr, nullptr);
+}
+
+void EventDispatcherPoll::processInterrupt(const struct pollfd &pfd)
+{
+ if (!(pfd.revents & POLLIN))
+ return;
+
+ uint64_t value;
+ ssize_t ret = read(eventfd_, &value, sizeof(value));
+ if (ret != sizeof(value)) {
+ if (ret < 0)
+ ret = -errno;
+ LOG(Event, Error)
+ << "Failed to process interrupt (" << ret << ")";
+ }
+}
+
+void EventDispatcherPoll::processNotifiers(const std::vector<struct pollfd> &pollfds)
+{
+ static const struct {
+ EventNotifier::Type type;
+ short events;
+ } events[] = {
+ { EventNotifier::Read, POLLIN },
+ { EventNotifier::Write, POLLOUT },
+ { EventNotifier::Exception, POLLPRI },
+ };
+
+ processingEvents_ = true;
+
+ for (const 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(Event, Warning)
+ << "Disabling " << notifierType(event.type)
+ << " due to invalid file descriptor "
+ << pfd.fd;
+ unregisterEventNotifier(notifier);
+ continue;
+ }
+
+ if (pfd.revents & event.events)
+ notifier->activated.emit(notifier);
+ }
+
+ /* Erase the notifiers_ entry if it is now empty. */
+ if (!set.notifiers[0] && !set.notifiers[1] && !set.notifiers[2])
+ notifiers_.erase(iter);
+ }
+
+ processingEvents_ = false;
+}
+
+void EventDispatcherPoll::processTimers()
+{
+ utils::time_point now = utils::clock::now();
+
+ 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/base/event_notifier.cpp b/src/libcamera/base/event_notifier.cpp
new file mode 100644
index 00000000..fd93c087
--- /dev/null
+++ b/src/libcamera/base/event_notifier.cpp
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * event_notifier.cpp - File descriptor event notifier
+ */
+
+#include <libcamera/base/event_notifier.h>
+
+#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/message.h>
+#include <libcamera/base/thread.h>
+
+#include <libcamera/camera_manager.h>
+
+/**
+ * \file event_notifier.h
+ * \brief File descriptor 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 setEnabled() 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 setEnabled() 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[in] fd The file descriptor to monitor
+ * \param[in] type The event type to monitor
+ * \param[in] parent The parent Object
+ */
+EventNotifier::EventNotifier(int fd, Type type, Object *parent)
+ : Object(parent), fd_(fd), type_(type), enabled_(false)
+{
+ setEnabled(true);
+}
+
+EventNotifier::~EventNotifier()
+{
+ setEnabled(false);
+}
+
+/**
+ * \fn EventNotifier::type()
+ * \brief Retrieve the type of the event being monitored
+ * \return The type of the event
+ */
+
+/**
+ * \fn EventNotifier::fd()
+ * \brief Retrieve 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 setEnabled()
+ */
+
+/**
+ * \brief Enable or disable the notifier
+ * \param[in] 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.
+ *
+ * \context This function is \threadbound.
+ */
+void EventNotifier::setEnabled(bool enable)
+{
+ if (enabled_ == enable)
+ return;
+
+ enabled_ = enable;
+
+ EventDispatcher *dispatcher = thread()->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.
+ */
+
+void EventNotifier::message(Message *msg)
+{
+ if (msg->type() == Message::ThreadMoveMessage) {
+ if (enabled_) {
+ setEnabled(false);
+ invokeMethod(&EventNotifier::setEnabled,
+ ConnectionTypeQueued, true);
+ }
+ }
+
+ Object::message(msg);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp
new file mode 100644
index 00000000..073666fa
--- /dev/null
+++ b/src/libcamera/base/file.cpp
@@ -0,0 +1,465 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * file.cpp - File I/O operations
+ */
+
+#include <libcamera/base/file.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file base/file.h
+ * \brief File I/O operations
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(File)
+
+/**
+ * \class File
+ * \brief Interface for I/O operations on files
+ *
+ * The File class provides an interface to perform I/O operations on files. It
+ * wraps opening, closing and mapping files in memory, and handles the cleaning
+ * of allocated resources.
+ *
+ * File instances are usually constructed with a file name, but the name can be
+ * set later through the setFileName() function. Instances are not automatically
+ * opened when constructed, and shall be opened explictly with open().
+ *
+ * Files can be mapped to the process memory with map(). Mapped regions can be
+ * unmapped manually with munmap(), and are automatically unmapped when the File
+ * is destroyed or when it is used to reference another file with setFileName().
+ */
+
+/**
+ * \enum File::MapFlag
+ * \brief Flags for the File::map() function
+ * \var File::MapNoOption
+ * \brief No option (used as default value)
+ * \var File::MapPrivate
+ * \brief The memory region is mapped as private, changes are not reflected in
+ * the file constents
+ */
+
+/**
+ * \enum File::OpenMode
+ * \brief Mode in which a file is opened
+ * \var File::NotOpen
+ * \brief The file is not open
+ * \var File::ReadOnly
+ * \brief The file is open for reading
+ * \var File::WriteOnly
+ * \brief The file is open for writing
+ * \var File::ReadWrite
+ * \brief The file is open for reading and writing
+ */
+
+/**
+ * \brief Construct a File to represent the file \a name
+ * \param[in] name The file name
+ *
+ * Upon construction the File object is closed and shall be opened with open()
+ * before performing I/O operations.
+ */
+File::File(const std::string &name)
+ : name_(name), fd_(-1), mode_(NotOpen), error_(0)
+{
+}
+
+/**
+ * \brief Construct a File without an associated name
+ *
+ * Before being used for any purpose, the file name shall be set with
+ * setFileName().
+ */
+File::File()
+ : fd_(-1), mode_(NotOpen), error_(0)
+{
+}
+
+/**
+ * \brief Destroy a File instance
+ *
+ * Any memory mapping associated with the File is unmapped, and the File is
+ * closed if it is open.
+ */
+File::~File()
+{
+ unmapAll();
+ close();
+}
+
+/**
+ * \fn const std::string &File::fileName() const
+ * \brief Retrieve the file name
+ * \return The file name
+ */
+
+/**
+ * \brief Set the name of the file
+ * \param[in] name The name of the file
+ *
+ * The \a name can contain an absolute path, a relative path or no path at all.
+ * Calling this function on an open file results in undefined behaviour.
+ *
+ * Any memory mapping associated with the File is unmapped.
+ */
+void File::setFileName(const std::string &name)
+{
+ if (isOpen()) {
+ LOG(File, Error)
+ << "Can't set file name on already open file " << name_;
+ return;
+ }
+
+ unmapAll();
+
+ name_ = name;
+}
+
+/**
+ * \brief Check if the file specified by fileName() exists
+ *
+ * This function checks if the file specified by fileName() exists. The File
+ * instance doesn't need to be open to check for file existence, and this
+ * function may return false even if the file is open, if it was deleted from
+ * the file system.
+ *
+ * \return True if the the file exists, false otherwise
+ */
+bool File::exists() const
+{
+ return exists(name_);
+}
+
+/**
+ * \brief Open the file in the given mode
+ * \param[in] mode The open mode
+ *
+ * This function opens the file specified by fileName() in \a mode. If the file
+ * doesn't exist and the mode is WriteOnly or ReadWrite, this function will
+ * attempt to create the file with initial permissions set to 0666 (modified by
+ * the process' umask).
+ *
+ * The error() status is updated.
+ *
+ * \return True on success, false otherwise
+ */
+bool File::open(File::OpenMode mode)
+{
+ if (isOpen()) {
+ LOG(File, Error) << "File " << name_ << " is already open";
+ return false;
+ }
+
+ int flags = (mode & ReadWrite) - 1;
+ if (mode & WriteOnly)
+ flags |= O_CREAT;
+
+ fd_ = ::open(name_.c_str(), flags, 0666);
+ if (fd_ < 0) {
+ error_ = -errno;
+ return false;
+ }
+
+ mode_ = mode;
+ error_ = 0;
+ return true;
+}
+
+/**
+ * \fn bool File::isOpen() const
+ * \brief Check if the file is open
+ * \return True if the file is open, false otherwise
+ */
+
+/**
+ * \fn OpenMode File::openMode() const
+ * \brief Retrieve the file open mode
+ * \return The file open mode
+ */
+
+/**
+ * \brief Close the file
+ *
+ * This function closes the File. If the File is not open, it performs no
+ * operation. Memory mappings created with map() are not destroyed when the
+ * file is closed.
+ */
+void File::close()
+{
+ if (fd_ == -1)
+ return;
+
+ ::close(fd_);
+ fd_ = -1;
+ mode_ = NotOpen;
+}
+
+/**
+ * \fn int File::error() const
+ * \brief Retrieve the file error status
+ *
+ * This function retrieves the error status from the last file open or I/O
+ * operation. The error status is a negative number as defined by errno.h. If
+ * no error occurred, this function returns 0.
+ *
+ * \return The file error status
+ */
+
+/**
+ * \brief Retrieve the file size
+ *
+ * This function retrieves the size of the file on the filesystem. The File
+ * instance shall be open to retrieve its size. The error() status is not
+ * modified, error codes are returned directly on failure.
+ *
+ * \return The file size in bytes on success, or a negative error code otherwise
+ */
+ssize_t File::size() const
+{
+ if (!isOpen())
+ return -EINVAL;
+
+ struct stat st;
+ int ret = fstat(fd_, &st);
+ if (ret < 0)
+ return -errno;
+
+ return st.st_size;
+}
+
+/**
+ * \brief Return current read or write position
+ *
+ * If the file is closed, this function returns 0.
+ *
+ * \return The current read or write position
+ */
+off_t File::pos() const
+{
+ if (!isOpen())
+ return 0;
+
+ return lseek(fd_, 0, SEEK_CUR);
+}
+
+/**
+ * \brief Set the read or write position
+ * \param[in] pos The desired position
+ * \return The resulting offset from the beginning of the file on success, or a
+ * negative error code otherwise
+ */
+off_t File::seek(off_t pos)
+{
+ if (!isOpen())
+ return -EINVAL;
+
+ off_t ret = lseek(fd_, pos, SEEK_SET);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+
+/**
+ * \brief Read data from the file
+ * \param[in] data Memory to read data into
+ *
+ * Read at most \a data.size() bytes from the file into \a data.data(), and
+ * return the number of bytes read. If less data than requested is available,
+ * the returned byte count may be smaller than the requested size. If no more
+ * data is available, 0 is returned.
+ *
+ * The position of the file as returned by pos() is advanced by the number of
+ * bytes read. If an error occurs, the position isn't modified.
+ *
+ * \return The number of bytes read on success, or a negative error code
+ * otherwise
+ */
+ssize_t File::read(const Span<uint8_t> &data)
+{
+ if (!isOpen())
+ return -EINVAL;
+
+ size_t readBytes = 0;
+ ssize_t ret = 0;
+
+ /* Retry in case of interrupted system calls. */
+ while (readBytes < data.size()) {
+ ret = ::read(fd_, data.data() + readBytes,
+ data.size() - readBytes);
+ if (ret <= 0)
+ break;
+
+ readBytes += ret;
+ }
+
+ if (ret < 0 && !readBytes)
+ return -errno;
+
+ return readBytes;
+}
+
+/**
+ * \brief Write data to the file
+ * \param[in] data Memory containing data to be written
+ *
+ * Write at most \a data.size() bytes from \a data.data() to the file, and
+ * return the number of bytes written. If the file system doesn't have enough
+ * space for the data, the returned byte count may be less than requested.
+ *
+ * The position of the file as returned by pos() is advanced by the number of
+ * bytes written. If an error occurs, the position isn't modified.
+ *
+ * \return The number of bytes written on success, or a negative error code
+ * otherwise
+ */
+ssize_t File::write(const Span<const uint8_t> &data)
+{
+ if (!isOpen())
+ return -EINVAL;
+
+ size_t writtenBytes = 0;
+
+ /* Retry in case of interrupted system calls. */
+ while (writtenBytes < data.size()) {
+ ssize_t ret = ::write(fd_, data.data() + writtenBytes,
+ data.size() - writtenBytes);
+ if (ret <= 0)
+ break;
+
+ writtenBytes += ret;
+ }
+
+ if (data.size() && !writtenBytes)
+ return -errno;
+
+ return writtenBytes;
+}
+
+/**
+ * \brief Map a region of the file in the process memory
+ * \param[in] offset The region offset within the file
+ * \param[in] size The region sise
+ * \param[in] flags The mapping flags
+ *
+ * This function maps a region of \a size bytes of the file starting at \a
+ * offset into the process memory. The File instance shall be open, but may be
+ * closed after mapping the region. Mappings stay valid when the File is
+ * closed, and are destroyed automatically when the File is deleted.
+ *
+ * If \a size is a negative value, this function maps the region starting at \a
+ * offset until the end of the file.
+ *
+ * The mapping memory protection is controlled by the file open mode, unless \a
+ * flags contains MapPrivate in which case the region is mapped in read/write
+ * mode.
+ *
+ * The error() status is updated.
+ *
+ * \return The mapped memory on success, or an empty span otherwise
+ */
+Span<uint8_t> File::map(off_t offset, ssize_t size, enum File::MapFlag flags)
+{
+ if (!isOpen()) {
+ error_ = -EBADF;
+ return {};
+ }
+
+ if (size < 0) {
+ size = File::size();
+ if (size < 0) {
+ error_ = size;
+ return {};
+ }
+
+ size -= offset;
+ }
+
+ int mmapFlags = flags & MapPrivate ? MAP_PRIVATE : MAP_SHARED;
+
+ int prot = 0;
+ if (mode_ & ReadOnly)
+ prot |= PROT_READ;
+ if (mode_ & WriteOnly)
+ prot |= PROT_WRITE;
+ if (flags & MapPrivate)
+ prot |= PROT_WRITE;
+
+ void *map = mmap(NULL, size, prot, mmapFlags, fd_, offset);
+ if (map == MAP_FAILED) {
+ error_ = -errno;
+ return {};
+ }
+
+ maps_.emplace(map, size);
+
+ error_ = 0;
+ return { static_cast<uint8_t *>(map), static_cast<size_t>(size) };
+}
+
+/**
+ * \brief Unmap a region mapped with map()
+ * \param[in] addr The region address
+ *
+ * The error() status is updated.
+ *
+ * \return True on success, or false if an error occurs
+ */
+bool File::unmap(uint8_t *addr)
+{
+ auto iter = maps_.find(static_cast<void *>(addr));
+ if (iter == maps_.end()) {
+ error_ = -ENOENT;
+ return false;
+ }
+
+ int ret = munmap(addr, iter->second);
+ if (ret < 0) {
+ error_ = -errno;
+ return false;
+ }
+
+ maps_.erase(iter);
+
+ error_ = 0;
+ return true;
+}
+
+void File::unmapAll()
+{
+ for (const auto &map : maps_)
+ munmap(map.first, map.second);
+
+ maps_.clear();
+}
+
+/**
+ * \brief Check if the file specified by \a name exists
+ * \param[in] name The file name
+ * \return True if the file exists, false otherwise
+ */
+bool File::exists(const std::string &name)
+{
+ struct stat st;
+ int ret = stat(name.c_str(), &st);
+ if (ret < 0)
+ return false;
+
+ /* Directories can not be handled here, even if they exist. */
+ return !S_ISDIR(st.st_mode);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp
new file mode 100644
index 00000000..1801ae26
--- /dev/null
+++ b/src/libcamera/base/log.cpp
@@ -0,0 +1,998 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2018, Google Inc.
+ *
+ * log.cpp - Logging infrastructure
+ */
+
+#include <libcamera/base/log.h>
+
+#include <array>
+#if HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unordered_set>
+
+#include <libcamera/logging.h>
+
+#include <libcamera/base/thread.h>
+#include <libcamera/base/utils.h>
+
+/**
+ * \file base/log.h
+ * \brief Logging infrastructure
+ *
+ * libcamera includes a logging infrastructure used through the library that
+ * allows inspection of internal operation in a user-configurable way. The log
+ * messages are grouped in categories that represent areas of libcamera, and
+ * output of messages for each category can be controlled by independent log
+ * levels.
+ *
+ * The levels are configurable through the LIBCAMERA_LOG_LEVELS environment
+ * variable that contains a comma-separated list of 'category:level' pairs.
+ *
+ * The category names are strings and can include a wildcard ('*') character at
+ * the end to match multiple categories.
+ *
+ * The level are either numeric values, or strings containing the log level
+ * name. The available log levels are DEBUG, INFO, WARN, ERROR and FATAL. Log
+ * message with a level higher than or equal to the configured log level for
+ * their category are output to the log, while other messages are silently
+ * discarded.
+ *
+ * By default log messages are output to stderr. They can be redirected to a log
+ * file by setting the LIBCAMERA_LOG_FILE environment variable to the name of
+ * the file. The file must be writable and is truncated if it exists. If any
+ * error occurs when opening the file, the file is ignored and the log is output
+ * to stderr.
+ */
+
+/**
+ * \file logging.h
+ * \brief Logging management
+ *
+ * API to change the logging output destination and log levels programatically.
+ */
+
+namespace libcamera {
+
+static int log_severity_to_syslog(LogSeverity severity)
+{
+ switch (severity) {
+ case LogDebug:
+ return LOG_DEBUG;
+ case LogInfo:
+ return LOG_INFO;
+ case LogWarning:
+ return LOG_WARNING;
+ case LogError:
+ return LOG_ERR;
+ case LogFatal:
+ return LOG_ALERT;
+ default:
+ return LOG_NOTICE;
+ }
+}
+
+static const char *log_severity_name(LogSeverity severity)
+{
+ static const char *const names[] = {
+ "DEBUG",
+ " INFO",
+ " WARN",
+ "ERROR",
+ "FATAL",
+ };
+
+ if (static_cast<unsigned int>(severity) < std::size(names))
+ return names[severity];
+ else
+ return "UNKWN";
+}
+
+/**
+ * \brief Log output
+ *
+ * The LogOutput class models a log output destination
+ */
+class LogOutput
+{
+public:
+ LogOutput(const char *path);
+ LogOutput(std::ostream *stream);
+ LogOutput();
+ ~LogOutput();
+
+ bool isValid() const;
+ void write(const LogMessage &msg);
+ void write(const std::string &msg);
+
+private:
+ void writeSyslog(LogSeverity severity, const std::string &msg);
+ void writeStream(const std::string &msg);
+
+ std::ostream *stream_;
+ LoggingTarget target_;
+};
+
+/**
+ * \brief Construct a log output based on a file
+ * \param[in] path Full path to log file
+ */
+LogOutput::LogOutput(const char *path)
+ : target_(LoggingTargetFile)
+{
+ stream_ = new std::ofstream(path);
+}
+
+/**
+ * \brief Construct a log output based on a stream
+ * \param[in] stream Stream to send log output to
+ */
+LogOutput::LogOutput(std::ostream *stream)
+ : stream_(stream), target_(LoggingTargetStream)
+{
+}
+
+/**
+ * \brief Construct a log output to syslog
+ */
+LogOutput::LogOutput()
+ : stream_(nullptr), target_(LoggingTargetSyslog)
+{
+ openlog("libcamera", LOG_PID, 0);
+}
+
+LogOutput::~LogOutput()
+{
+ switch (target_) {
+ case LoggingTargetFile:
+ delete stream_;
+ break;
+ case LoggingTargetSyslog:
+ closelog();
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * \brief Check if the log output is valid
+ * \return True if the log output is valid
+ */
+bool LogOutput::isValid() const
+{
+ switch (target_) {
+ case LoggingTargetFile:
+ return stream_->good();
+ case LoggingTargetStream:
+ return stream_ != nullptr;
+ default:
+ return true;
+ }
+}
+
+/**
+ * \brief Write message to log output
+ * \param[in] msg Message to write
+ */
+void LogOutput::write(const LogMessage &msg)
+{
+ std::string str;
+
+ switch (target_) {
+ case LoggingTargetSyslog:
+ str = std::string(log_severity_name(msg.severity())) + " "
+ + msg.category().name() + " " + msg.fileInfo() + " "
+ + msg.msg();
+ writeSyslog(msg.severity(), str);
+ break;
+ case LoggingTargetStream:
+ case LoggingTargetFile:
+ str = "[" + utils::time_point_to_string(msg.timestamp()) + "] ["
+ + std::to_string(Thread::currentId()) + "] "
+ + log_severity_name(msg.severity()) + " "
+ + msg.category().name() + " " + msg.fileInfo() + " "
+ + msg.msg();
+ writeStream(str);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * \brief Write string to log output
+ * \param[in] str String to write
+ */
+void LogOutput::write(const std::string &str)
+{
+ switch (target_) {
+ case LoggingTargetSyslog:
+ writeSyslog(LogDebug, str);
+ break;
+ case LoggingTargetStream:
+ case LoggingTargetFile:
+ writeStream(str);
+ break;
+ default:
+ break;
+ }
+}
+
+void LogOutput::writeSyslog(LogSeverity severity, const std::string &str)
+{
+ syslog(log_severity_to_syslog(severity), "%s", str.c_str());
+}
+
+void LogOutput::writeStream(const std::string &str)
+{
+ stream_->write(str.c_str(), str.size());
+ stream_->flush();
+}
+
+/**
+ * \brief Message logger
+ *
+ * The Logger class handles log configuration.
+ */
+class Logger
+{
+public:
+ ~Logger();
+
+ static Logger *instance();
+
+ void write(const LogMessage &msg);
+ void backtrace();
+
+ int logSetFile(const char *path);
+ int logSetStream(std::ostream *stream);
+ int logSetTarget(LoggingTarget target);
+ void logSetLevel(const char *category, const char *level);
+
+private:
+ Logger();
+
+ void parseLogFile();
+ void parseLogLevels();
+ static LogSeverity parseLogLevel(const std::string &level);
+
+ friend LogCategory;
+ void registerCategory(LogCategory *category);
+
+ std::unordered_set<LogCategory *> categories_;
+ std::list<std::pair<std::string, LogSeverity>> levels_;
+
+ std::shared_ptr<LogOutput> output_;
+};
+
+/**
+ * \enum LoggingTarget
+ * \brief Log destination type
+ * \var LoggingTargetNone
+ * \brief No logging destination
+ * \sa Logger::logSetTarget
+ * \var LoggingTargetSyslog
+ * \brief Log to syslog
+ * \sa Logger::logSetTarget
+ * \var LoggingTargetFile
+ * \brief Log to file
+ * \sa Logger::logSetFile
+ * \var LoggingTargetStream
+ * \brief Log to stream
+ * \sa Logger::logSetStream
+ */
+
+/**
+ * \brief Direct logging to a file
+ * \param[in] path Full path to the log file
+ *
+ * This function directs the log output to the file identified by \a path. The
+ * previous log target, if any, is closed, and all new log messages will be
+ * written to the new log file.
+ *
+ * If the function returns an error, the log target is not changed.
+ *
+ * \return Zero on success, or a negative error code otherwise
+ */
+int logSetFile(const char *path)
+{
+ return Logger::instance()->logSetFile(path);
+}
+
+/**
+ * \brief Direct logging to a stream
+ * \param[in] stream Stream to send log output to
+ *
+ * This function directs the log output to \a stream. The previous log target,
+ * if any, is closed, and all new log messages will be written to the new log
+ * stream.
+ *
+ * If the function returns an error, the log file is not changed
+ *
+ * \return Zero on success, or a negative error code otherwise.
+ */
+int logSetStream(std::ostream *stream)
+{
+ return Logger::instance()->logSetStream(stream);
+}
+
+/**
+ * \brief Set the logging target
+ * \param[in] target Logging destination
+ *
+ * This function sets the logging output to the target specified by \a target.
+ * The allowed values of \a target are LoggingTargetNone and
+ * LoggingTargetSyslog. LoggingTargetNone will send the log output to nowhere,
+ * and LoggingTargetSyslog will send the log output to syslog. The previous
+ * log target, if any, is closed, and all new log messages will be written to
+ * the new log destination.
+ *
+ * LoggingTargetFile and LoggingTargetStream are not valid values for \a target.
+ * Use logSetFile() and logSetStream() instead, respectively.
+ *
+ * If the function returns an error, the log file is not changed.
+ *
+ * \return Zero on success, or a negative error code otherwise.
+ */
+int logSetTarget(LoggingTarget target)
+{
+ return Logger::instance()->logSetTarget(target);
+}
+
+/**
+ * \brief Set the log level
+ * \param[in] category Logging category
+ * \param[in] level Log level
+ *
+ * This function sets the log level of \a category to \a level.
+ * \a level shall be one of the following strings:
+ * - "DEBUG"
+ * - "INFO"
+ * - "WARN"
+ * - "ERROR"
+ * - "FATAL"
+ *
+ * "*" is not a valid \a category for this function.
+ */
+void logSetLevel(const char *category, const char *level)
+{
+ Logger::instance()->logSetLevel(category, level);
+}
+
+Logger::~Logger()
+{
+ for (LogCategory *category : categories_)
+ delete category;
+}
+
+/**
+ * \brief Retrieve the logger instance
+ *
+ * The Logger is a singleton and can't be constructed manually. This function
+ * shall instead be used to retrieve the single global instance of the logger.
+ *
+ * \return The logger instance
+ */
+Logger *Logger::instance()
+{
+ static Logger instance;
+ return &instance;
+}
+
+/**
+ * \brief Write a message to the configured logger output
+ * \param[in] msg The message object
+ */
+void Logger::write(const LogMessage &msg)
+{
+ std::shared_ptr<LogOutput> output = std::atomic_load(&output_);
+ if (!output)
+ return;
+
+ output->write(msg);
+}
+
+/**
+ * \brief Write a backtrace to the log
+ */
+void Logger::backtrace()
+{
+#if HAVE_BACKTRACE
+ std::shared_ptr<LogOutput> output = std::atomic_load(&output_);
+ if (!output)
+ return;
+
+ void *buffer[32];
+ int num_entries = ::backtrace(buffer, std::size(buffer));
+ char **strings = backtrace_symbols(buffer, num_entries);
+ if (!strings)
+ return;
+
+ std::ostringstream msg;
+ msg << "Backtrace:" << std::endl;
+
+ /*
+ * Skip the first two entries that correspond to this method and
+ * ~LogMessage().
+ */
+ for (int i = 2; i < num_entries; ++i)
+ msg << strings[i] << std::endl;
+
+ output->write(msg.str());
+
+ free(strings);
+#endif
+}
+
+/**
+ * \brief Set the log file
+ * \param[in] path Full path to the log file
+ *
+ * \sa libcamera::logSetFile()
+ *
+ * \return Zero on success, or a negative error code otherwise.
+ */
+int Logger::logSetFile(const char *path)
+{
+ std::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(path);
+ if (!output->isValid())
+ return -EINVAL;
+
+ std::atomic_store(&output_, output);
+ return 0;
+}
+
+/**
+ * \brief Set the log stream
+ * \param[in] stream Stream to send log output to
+ *
+ * \sa libcamera::logSetStream()
+ *
+ * \return Zero on success, or a negative error code otherwise.
+ */
+int Logger::logSetStream(std::ostream *stream)
+{
+ std::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(stream);
+ std::atomic_store(&output_, output);
+ return 0;
+}
+
+/**
+ * \brief Set the log target
+ * \param[in] target Log destination
+ *
+ * \sa libcamera::logSetTarget()
+ *
+ * \return Zero on success, or a negative error code otherwise.
+ */
+int Logger::logSetTarget(enum LoggingTarget target)
+{
+ std::shared_ptr<LogOutput> output;
+
+ switch (target) {
+ case LoggingTargetSyslog:
+ output = std::make_shared<LogOutput>();
+ std::atomic_store(&output_, output);
+ break;
+ case LoggingTargetNone:
+ output = nullptr;
+ std::atomic_store(&output_, std::shared_ptr<LogOutput>());
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Set the log level
+ * \param[in] category Logging category
+ * \param[in] level Log level
+ *
+ * \sa libcamera::logSetLevel()
+ */
+void Logger::logSetLevel(const char *category, const char *level)
+{
+ LogSeverity severity = parseLogLevel(level);
+ if (severity == LogInvalid)
+ return;
+
+ for (LogCategory *c : categories_) {
+ if (!strcmp(c->name(), category)) {
+ c->setSeverity(severity);
+ break;
+ }
+ }
+}
+
+/**
+ * \brief Construct a logger
+ */
+Logger::Logger()
+{
+ parseLogFile();
+ parseLogLevels();
+}
+
+/**
+ * \brief Parse the log output file from the environment
+ *
+ * If the LIBCAMERA_LOG_FILE environment variable is set, open the file it
+ * points to and redirect the logger output to it. If the environment variable
+ * is set to "syslog", then the logger output will be directed to syslog. Errors
+ * are silently ignored and don't affect the logger output (set to stderr).
+ */
+void Logger::parseLogFile()
+{
+ const char *file = utils::secure_getenv("LIBCAMERA_LOG_FILE");
+ if (!file) {
+ logSetStream(&std::cerr);
+ return;
+ }
+
+ if (!strcmp(file, "syslog")) {
+ logSetTarget(LoggingTargetSyslog);
+ return;
+ }
+
+ logSetFile(file);
+}
+
+/**
+ * \brief Parse the log levels from the environment
+ *
+ * The log levels are stored in the LIBCAMERA_LOG_LEVELS environment variable
+ * as a list of "category:level" pairs, separated by commas (','). Parse the
+ * variable and store the levels to configure all log categories.
+ */
+void Logger::parseLogLevels()
+{
+ const char *debug = utils::secure_getenv("LIBCAMERA_LOG_LEVELS");
+ if (!debug)
+ return;
+
+ for (const char *pair = debug; *debug != '\0'; pair = debug) {
+ const char *comma = strchrnul(debug, ',');
+ size_t len = comma - pair;
+
+ /* Skip over the comma. */
+ debug = *comma == ',' ? comma + 1 : comma;
+
+ /* Skip to the next pair if the pair is empty. */
+ if (!len)
+ continue;
+
+ std::string category;
+ std::string level;
+
+ const char *colon = static_cast<const char *>(memchr(pair, ':', len));
+ if (!colon) {
+ /* 'x' is a shortcut for '*:x'. */
+ category = "*";
+ level = std::string(pair, len);
+ } else {
+ category = std::string(pair, colon - pair);
+ level = std::string(colon + 1, comma - colon - 1);
+ }
+
+ /* Both the category and the level must be specified. */
+ if (category.empty() || level.empty())
+ continue;
+
+ LogSeverity severity = parseLogLevel(level);
+ if (severity == LogInvalid)
+ continue;
+
+ levels_.push_back({ category, severity });
+ }
+}
+
+/**
+ * \brief Parse a log level string into a LogSeverity
+ * \param[in] level The log level string
+ *
+ * Log levels can be specified as an integer value in the range from LogDebug to
+ * LogFatal, or as a string corresponding to the severity name in uppercase. Any
+ * other value is invalid.
+ *
+ * \return The log severity, or LogInvalid if the string is invalid
+ */
+LogSeverity Logger::parseLogLevel(const std::string &level)
+{
+ static const char *const names[] = {
+ "DEBUG",
+ "INFO",
+ "WARN",
+ "ERROR",
+ "FATAL",
+ };
+
+ int severity;
+
+ if (std::isdigit(level[0])) {
+ char *endptr;
+ severity = strtoul(level.c_str(), &endptr, 10);
+ if (*endptr != '\0' || severity > LogFatal)
+ severity = LogInvalid;
+ } else {
+ severity = LogInvalid;
+ for (unsigned int i = 0; i < std::size(names); ++i) {
+ if (names[i] == level) {
+ severity = i;
+ break;
+ }
+ }
+ }
+
+ return static_cast<LogSeverity>(severity);
+}
+
+/**
+ * \brief Register a log category with the logger
+ * \param[in] category The log category
+ *
+ * Log categories must have unique names. If a category with the same name
+ * already exists this function performs no operation.
+ */
+void Logger::registerCategory(LogCategory *category)
+{
+ categories_.insert(category);
+
+ const std::string &name = category->name();
+ for (const std::pair<std::string, LogSeverity> &level : levels_) {
+ bool match = true;
+
+ for (unsigned int i = 0; i < level.first.size(); ++i) {
+ if (level.first[i] == '*')
+ break;
+
+ if (i >= name.size() ||
+ name[i] != level.first[i]) {
+ match = false;
+ break;
+ }
+ }
+
+ if (match) {
+ category->setSeverity(level.second);
+ break;
+ }
+ }
+}
+
+/**
+ * \enum LogSeverity
+ * Log message severity
+ * \var LogDebug
+ * Debug message
+ * \var LogInfo
+ * Informational message
+ * \var LogWarning
+ * 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
+ */
+
+/**
+ * \class LogCategory
+ * \brief A category of log message
+ *
+ * The LogCategory class represents a category of log messages, related to an
+ * area of the library. It groups all messages belonging to the same category,
+ * and is used to control the log level per group.
+ */
+
+/**
+ * \brief Construct a log category
+ * \param[in] name The category name
+ */
+LogCategory::LogCategory(const char *name)
+ : name_(name), severity_(LogSeverity::LogInfo)
+{
+ Logger::instance()->registerCategory(this);
+}
+
+/**
+ * \fn LogCategory::name()
+ * \brief Retrieve the log category name
+ * \return The log category name
+ */
+
+/**
+ * \fn LogCategory::severity()
+ * \brief Retrieve the severity of the log category
+ * \sa setSeverity()
+ * \return Return the severity of the log category
+ */
+
+/**
+ * \brief Set the severity of the log category
+ *
+ * Messages of severity higher than or equal to the severity of the log category
+ * are printed, other messages are discarded.
+ */
+void LogCategory::setSeverity(LogSeverity severity)
+{
+ severity_ = severity;
+}
+
+/**
+ * \brief Retrieve the default log category
+ *
+ * The default log category is named "default" and is used by the LOG() macro
+ * when no log category is specified.
+ *
+ * \return A reference to the default log category
+ */
+const LogCategory &LogCategory::defaultCategory()
+{
+ static const LogCategory *category = new LogCategory("default");
+ return *category;
+}
+
+/**
+ * \class LogMessage
+ * \brief Internal log message representation.
+ *
+ * The LogMessage class models a single message in the log. It serves as a
+ * helper to provide the std::ostream API for logging, and must never be used
+ * directly. Use the LOG() macro instead access the log infrastructure.
+ */
+
+/**
+ * \brief Construct a log message for a given category
+ * \param[in] fileName The file name where the message is logged from
+ * \param[in] line The line number where the message is logged from
+ * \param[in] category The log message category, controlling how the message
+ * will be displayed
+ * \param[in] severity The log message severity, controlling how the message
+ * will be displayed
+ *
+ * Create a log message pertaining to line \a line of file \a fileName. The
+ * \a severity argument sets the message severity to control whether it will be
+ * output or dropped.
+ */
+LogMessage::LogMessage(const char *fileName, unsigned int line,
+ const LogCategory &category, LogSeverity severity)
+ : category_(category), severity_(severity)
+{
+ init(fileName, line);
+}
+
+/**
+ * \brief Move-construct a log message
+ * \param[in] other The other message
+ *
+ * The move constructor is meant to support the _log() functions. Thanks to copy
+ * elision it will likely never be called, but C++11 only permits copy elision,
+ * it doesn't enforce it unlike C++17. To avoid potential link errors depending
+ * on the compiler type and version, and optimization level, the move
+ * constructor is defined even if it will likely never be called, and ensures
+ * that the destructor of the \a other message will not output anything to the
+ * log by setting the severity to LogInvalid.
+ */
+LogMessage::LogMessage(LogMessage &&other)
+ : msgStream_(std::move(other.msgStream_)), category_(other.category_),
+ severity_(other.severity_)
+{
+ other.severity_ = LogInvalid;
+}
+
+void LogMessage::init(const char *fileName, unsigned int line)
+{
+ /* Log the timestamp, severity and file information. */
+ timestamp_ = utils::clock::now();
+
+ std::ostringstream ossFileInfo;
+ ossFileInfo << utils::basename(fileName) << ":" << line;
+ fileInfo_ = ossFileInfo.str();
+}
+
+LogMessage::~LogMessage()
+{
+ /* Don't print anything if we have been moved to another LogMessage. */
+ if (severity_ == LogInvalid)
+ return;
+
+ msgStream_ << std::endl;
+
+ if (severity_ >= category_.severity())
+ Logger::instance()->write(*this);
+
+ if (severity_ == LogSeverity::LogFatal) {
+ Logger::instance()->backtrace();
+ std::abort();
+ }
+}
+
+/**
+ * \fn std::ostream& LogMessage::stream()
+ *
+ * Data is added to a LogMessage through the stream returned by this function.
+ * The stream implements the std::ostream API and can be used for logging as
+ * std::cout.
+ *
+ * \return A reference to the log message stream
+ */
+
+/**
+ * \fn LogMessage::timestamp()
+ * \brief Retrieve the timestamp of the log message
+ * \return The timestamp of the message
+ */
+
+/**
+ * \fn LogMessage::severity()
+ * \brief Retrieve the severity of the log message
+ * \return The severity of the message
+ */
+
+/**
+ * \fn LogMessage::category()
+ * \brief Retrieve the category of the log message
+ * \return The category of the message
+ */
+
+/**
+ * \fn LogMessage::fileInfo()
+ * \brief Retrieve the file info of the log message
+ * \return The file info of the message
+ */
+
+/**
+ * \fn LogMessage::msg()
+ * \brief Retrieve the message text of the log message
+ * \return The message text of the message, as a string
+ */
+
+/**
+ * \class Loggable
+ * \brief Base class to support log message extensions
+ *
+ * The Loggable class allows classes to extend log messages without any change
+ * to the way the LOG() macro is invoked. By inheriting from Loggable and
+ * implementing the logPrefix() virtual method, a class can specify extra
+ * information to be automatically added to messages logged from class member
+ * methods.
+ */
+
+Loggable::~Loggable()
+{
+}
+
+/**
+ * \fn Loggable::logPrefix()
+ * \brief Retrieve a string to be prefixed to the log message
+ *
+ * This method allows classes inheriting from the Loggable class to extend the
+ * logger with an object-specific prefix output right before the log message
+ * contents.
+ *
+ * \return A string to be prefixed to the log message
+ */
+
+/**
+ * \brief Create a temporary LogMessage object to log a message
+ * \param[in] category The log message category
+ * \param[in] severity The log message severity
+ * \param[in] fileName The file name where the message is logged from
+ * \param[in] line The line number where the message is logged from
+ *
+ * This method is used as a backeng by the LOG() macro to create a log message
+ * for locations inheriting from the Loggable class.
+ *
+ * \return A log message
+ */
+LogMessage Loggable::_log(const LogCategory *category, LogSeverity severity,
+ const char *fileName, unsigned int line) const
+{
+ LogMessage msg(fileName, line,
+ category ? *category : LogCategory::defaultCategory(),
+ severity);
+
+ msg.stream() << logPrefix() << ": ";
+ return msg;
+}
+
+/**
+ * \brief Create a temporary LogMessage object to log a message
+ * \param[in] category The log message category
+ * \param[in] severity The log message severity
+ * \param[in] fileName The file name where the message is logged from
+ * \param[in] line The line number where the message is logged from
+ *
+ * This function is used as a backeng by the LOG() macro to create a log
+ * message for locations not inheriting from the Loggable class.
+ *
+ * \return A log message
+ */
+LogMessage _log(const LogCategory *category, LogSeverity severity,
+ const char *fileName, unsigned int line)
+{
+ return LogMessage(fileName, line,
+ category ? *category : LogCategory::defaultCategory(),
+ severity);
+}
+
+/**
+ * \def LOG_DECLARE_CATEGORY(name)
+ * \hideinitializer
+ * \brief Declare a category of log messages
+ *
+ * This macro is used to declare a log category defined in another compilation
+ * unit by the LOG_DEFINE_CATEGORY() macro.
+ *
+ * The LOG_DECLARE_CATEGORY() macro must be used in the libcamera namespace.
+ *
+ * \sa LogCategory
+ */
+
+/**
+ * \def LOG_DEFINE_CATEGORY(name)
+ * \hideinitializer
+ * \brief Define a category of log messages
+ *
+ * This macro is used to define a log category that can then be used with the
+ * LOGC() macro. Category names shall be unique, if a category is shared between
+ * compilation units, it shall be defined in one compilation unit only and
+ * declared with LOG_DECLARE_CATEGORY() in the other compilation units.
+ *
+ * The LOG_DEFINE_CATEGORY() macro must be used in the libcamera namespace.
+ *
+ * \sa LogCategory
+ */
+
+/**
+ * \def LOG(category, severity)
+ * \hideinitializer
+ * \brief Log a message
+ * \param[in] category Category (optional)
+ * \param[in] severity Severity
+ *
+ * Return an std::ostream reference to which a message can be logged using the
+ * iostream API. The \a category, if specified, sets the message category. When
+ * absent the default category is used. The \a severity controls whether the
+ * message is printed or discarded, depending on the log level for the category.
+ *
+ * If the severity is set to Fatal, execution is aborted and the program
+ * terminates immediately after printing the message.
+ *
+ * \warning Logging from the destructor of a global object, either directly or
+ * indirectly, results in undefined behaviour.
+ *
+ * \todo Allow logging from destructors of global objects to the largest
+ * possible extent
+ */
+
+/**
+ * \def ASSERT(condition)
+ * \hideinitializer
+ * \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.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build
new file mode 100644
index 00000000..87172157
--- /dev/null
+++ b/src/libcamera/base/meson.build
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libcamera_base_sources = files([
+ 'class.cpp',
+ 'bound_method.cpp',
+ 'event_dispatcher.cpp',
+ 'event_dispatcher_poll.cpp',
+ 'event_notifier.cpp',
+ 'file.cpp',
+ 'log.cpp',
+ 'message.cpp',
+ 'object.cpp',
+ 'semaphore.cpp',
+ 'signal.cpp',
+ 'thread.cpp',
+ 'timer.cpp',
+ 'utils.cpp',
+])
+
+libcamera_base_deps = [
+ dependency('threads'),
+]
+
+# Internal components must use the libcamera_base_private dependency to enable
+# the use of headers which must not be exposed to the libcamera public api.
+libcamera_base_args = [ '-DLIBCAMERA_BASE_PRIVATE' ]
+
+libcamera_base_lib = shared_library('libcamera-base',
+ [libcamera_base_sources, libcamera_base_headers],
+ name_prefix : '',
+ install : true,
+ cpp_args : libcamera_base_args,
+ include_directories : libcamera_includes,
+ dependencies : libcamera_base_deps)
+
+libcamera_base = declare_dependency(sources : [
+ libcamera_base_headers,
+ ],
+ include_directories : libcamera_includes,
+ link_with : libcamera_base_lib)
+
+pkg_mod = import('pkgconfig')
+pkg_mod.generate(libcamera_base_lib,
+ version : '1.0',
+ description : 'Camera support base utility library',
+ subdirs : 'libcamera')
+
+libcamera_base_private = declare_dependency(dependencies : libcamera_base,
+ compile_args : libcamera_base_args)
diff --git a/src/libcamera/base/message.cpp b/src/libcamera/base/message.cpp
new file mode 100644
index 00000000..f1d772e4
--- /dev/null
+++ b/src/libcamera/base/message.cpp
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * message.cpp - Message queue support
+ */
+
+#include <libcamera/base/message.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/signal.h>
+
+/**
+ * \file base/message.h
+ * \brief Message queue support
+ *
+ * The messaging API enables inter-thread communication through message
+ * posting. Messages can be sent from any thread to any recipient deriving from
+ * the Object class.
+ *
+ * To post a message, the sender allocates it dynamically as instance of a class
+ * derived from Message. It then posts the message to an Object recipient
+ * through Object::postMessage(). Message ownership is passed to the object,
+ * thus the message shall not store any temporary data.
+ *
+ * The message is delivered in the context of the object's thread, through the
+ * Object::message() virtual method. After delivery the message is
+ * automatically deleted.
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Message)
+
+std::atomic_uint Message::nextUserType_{ Message::UserMessage };
+
+/**
+ * \class Message
+ * \brief A message that can be posted to a Thread
+ */
+
+/**
+ * \enum Message::Type
+ * \brief The message type
+ * \var Message::None
+ * \brief Invalid message type
+ * \var Message::InvokeMessage
+ * \brief Asynchronous method invocation across threads
+ * \var Message::ThreadMoveMessage
+ * \brief Object is being moved to a different thread
+ * \var Message::DeferredDelete
+ * \brief Object is scheduled for deletion
+ * \var Message::UserMessage
+ * \brief First value available for user-defined messages
+ */
+
+/**
+ * \brief Construct a message object of type \a type
+ * \param[in] type The message type
+ */
+Message::Message(Message::Type type)
+ : type_(type)
+{
+}
+
+Message::~Message()
+{
+}
+
+/**
+ * \fn Message::type()
+ * \brief Retrieve the message type
+ * \return The message type
+ */
+
+/**
+ * \fn Message::receiver()
+ * \brief Retrieve the message receiver
+ * \return The message receiver
+ */
+
+/**
+ * \brief Reserve and register a custom user-defined message type
+ *
+ * Custom message types use values starting at Message::UserMessage. Assigning
+ * custom types manually may lead to accidental duplicated types. To avoid this
+ * problem, this method reserves and returns the next available user-defined
+ * message type.
+ *
+ * The recommended way to use this method is to subclass Message and provide a
+ * static accessor for the custom message type.
+ *
+ * \code{.cpp}
+ * class MyCustomMessage : public Message
+ * {
+ * public:
+ * MyCustomMessage() : Message(type()) {}
+ *
+ * static Message::Type type()
+ * {
+ * static MessageType type = registerMessageType();
+ * return type;
+ * }
+ * };
+ * \endcode
+ *
+ * \return A new unique message type
+ */
+Message::Type Message::registerMessageType()
+{
+ return static_cast<Message::Type>(nextUserType_++);
+}
+
+/**
+ * \class InvokeMessage
+ * \brief A message carrying a method invocation across threads
+ */
+
+/**
+ * \brief Construct an InvokeMessage for method invocation on an Object
+ * \param[in] method The bound method
+ * \param[in] pack The packed method arguments
+ * \param[in] semaphore The semaphore used to signal message delivery
+ * \param[in] deleteMethod True to delete the \a method when the message is
+ * destroyed
+ */
+InvokeMessage::InvokeMessage(BoundMethodBase *method,
+ std::shared_ptr<BoundMethodPackBase> pack,
+ Semaphore *semaphore, bool deleteMethod)
+ : Message(Message::InvokeMessage), method_(method), pack_(pack),
+ semaphore_(semaphore), deleteMethod_(deleteMethod)
+{
+}
+
+InvokeMessage::~InvokeMessage()
+{
+ if (deleteMethod_)
+ delete method_;
+}
+
+/**
+ * \fn InvokeMessage::semaphore()
+ * \brief Retrieve the message semaphore passed to the constructor
+ * \return The message semaphore
+ */
+
+/**
+ * \brief Invoke the method bound to InvokeMessage::method_ with arguments
+ * InvokeMessage::pack_
+ */
+void InvokeMessage::invoke()
+{
+ method_->invokePack(pack_.get());
+}
+
+/**
+ * \var InvokeMessage::method_
+ * \brief The method to be invoked
+ */
+
+/**
+ * \var InvokeMessage::pack_
+ * \brief The packed method invocation arguments
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/object.cpp b/src/libcamera/base/object.cpp
new file mode 100644
index 00000000..25410ecd
--- /dev/null
+++ b/src/libcamera/base/object.cpp
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * object.cpp - Base object
+ */
+
+#include <libcamera/base/object.h>
+
+#include <algorithm>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/message.h>
+#include <libcamera/base/semaphore.h>
+#include <libcamera/base/signal.h>
+#include <libcamera/base/thread.h>
+#include <libcamera/base/utils.h>
+
+/**
+ * \file base/object.h
+ * \brief Base object to support automatic signal disconnection
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Object)
+
+/**
+ * \class Object
+ * \brief Base object to support automatic signal disconnection
+ *
+ * The Object class simplifies signal/slot handling for classes implementing
+ * slots. By inheriting from Object, an object is automatically disconnected
+ * from all connected signals when it gets destroyed.
+ *
+ * Object instances are bound to the thread of their parent, or the thread in
+ * which they're created when they have no parent. When a message is posted to
+ * an object, its handler will run in the object's thread. This allows
+ * implementing easy message passing between threads by inheriting from the
+ * Object class.
+ *
+ * Deleting an object from a thread other than the one the object is bound to is
+ * unsafe, unless the caller ensures that the object isn't processing any
+ * message concurrently.
+ *
+ * Object slots connected to signals will also run in the context of the
+ * object's thread, regardless of whether the signal is emitted in the same or
+ * in another thread.
+ *
+ * \sa Message, Signal, Thread
+ */
+
+/**
+ * \brief Construct an Object instance
+ * \param[in] parent The object parent
+ *
+ * The new Object instance is bound to the thread of its \a parent, or to the
+ * current thread if the \a parent is nullptr.
+ */
+Object::Object(Object *parent)
+ : parent_(parent), pendingMessages_(0)
+{
+ thread_ = parent ? parent->thread() : Thread::current();
+
+ if (parent)
+ parent->children_.push_back(this);
+}
+
+/**
+ * \brief Destroy an Object instance
+ *
+ * Deleting an Object automatically disconnects all signals from the Object's
+ * slots. All the Object's children are made orphan, but stay bound to their
+ * current thread.
+ *
+ * Object instances shall be destroyed from the thread they are bound to,
+ * otherwise undefined behaviour may occur. If deletion of an Object needs to
+ * be scheduled from a different thread, deleteLater() shall be used.
+ */
+Object::~Object()
+{
+ /*
+ * Move signals to a private list to avoid concurrent iteration and
+ * deletion of items from Signal::disconnect().
+ */
+ std::list<SignalBase *> signals(std::move(signals_));
+ for (SignalBase *signal : signals)
+ signal->disconnect(this);
+
+ if (pendingMessages_)
+ thread()->removeMessages(this);
+
+ if (parent_) {
+ auto it = std::find(parent_->children_.begin(),
+ parent_->children_.end(), this);
+ ASSERT(it != parent_->children_.end());
+ parent_->children_.erase(it);
+ }
+
+ for (auto child : children_)
+ child->parent_ = nullptr;
+}
+
+/**
+ * \brief Schedule deletion of the instance in the thread it belongs to
+ *
+ * This function schedules deletion of the Object when control returns to the
+ * event loop that the object belongs to. This ensures the object is destroyed
+ * from the right context, as required by the libcamera threading model.
+ *
+ * If this function is called before the thread's event loop is started, the
+ * object will be deleted when the event loop starts.
+ *
+ * Deferred deletion can be used to control the destruction context with shared
+ * pointers. An object managed with shared pointers is deleted when the last
+ * reference is destroyed, which makes difficult to ensure through software
+ * design which context the deletion will take place in. With a custom deleter
+ * for the shared pointer using deleteLater(), the deletion can be guaranteed to
+ * happen in the thread the object is bound to.
+ *
+ * \code{.cpp}
+ * std::shared_ptr<MyObject> createObject()
+ * {
+ * struct Deleter : std::default_delete<MyObject> {
+ * void operator()(MyObject *obj)
+ * {
+ * obj->deleteLater();
+ * }
+ * };
+ *
+ * MyObject *obj = new MyObject();
+ *
+ * return std::shared_ptr<MyObject>(obj, Deleter());
+ * }
+ * \endcode
+ *
+ * \context This function is \threadsafe.
+ */
+void Object::deleteLater()
+{
+ postMessage(std::make_unique<Message>(Message::DeferredDelete));
+}
+
+/**
+ * \brief Post a message to the object's thread
+ * \param[in] msg The message
+ *
+ * This method posts the message \a msg to the message queue of the object's
+ * thread, to be delivered to the object through the message() method in the
+ * context of its thread. Message ownership is passed to the thread, and the
+ * message will be deleted after being delivered.
+ *
+ * Messages are delivered through the thread's event loop. If the thread is not
+ * running its event loop the message will not be delivered until the event
+ * loop gets started.
+ *
+ * Due to their asynchronous nature, threads do not provide any guarantee that
+ * all posted messages are delivered before the thread is stopped. See
+ * \ref thread-stop for additional information.
+ *
+ * \context This function is \threadsafe.
+ */
+void Object::postMessage(std::unique_ptr<Message> msg)
+{
+ thread()->postMessage(std::move(msg), this);
+}
+
+/**
+ * \brief Message handler for the object
+ * \param[in] msg The message
+ *
+ * This virtual method receives messages for the object. It is called in the
+ * context of the object's thread, and can be overridden to process custom
+ * messages. The parent Object::message() method shall be called for any
+ * message not handled by the override method.
+ *
+ * The message \a msg is valid only for the duration of the call, no reference
+ * to it shall be kept after this method returns.
+ */
+void Object::message(Message *msg)
+{
+ switch (msg->type()) {
+ case Message::InvokeMessage: {
+ InvokeMessage *iMsg = static_cast<InvokeMessage *>(msg);
+ Semaphore *semaphore = iMsg->semaphore();
+ iMsg->invoke();
+
+ if (semaphore)
+ semaphore->release();
+
+ break;
+ }
+
+ case Message::DeferredDelete:
+ delete this;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * \fn R Object::invokeMethod()
+ * \brief Invoke a method asynchronously on an Object instance
+ * \param[in] func The object method to invoke
+ * \param[in] type Connection type for method invocation
+ * \param[in] args The method arguments
+ *
+ * This method invokes the member method \a func with arguments \a args, based
+ * on the connection \a type. Depending on the type, the method will be called
+ * synchronously in the same thread or asynchronously in the object's thread.
+ *
+ * Arguments \a args passed by value or reference are copied, while pointers
+ * are passed untouched. The caller shall ensure that any pointer argument
+ * remains valid until the method is invoked.
+ *
+ * Due to the asynchronous nature of threads, functions invoked asynchronously
+ * with the ConnectionTypeQueued type are not guaranteed to be called before
+ * the thread is stopped. See \ref thread-stop for additional information.
+ *
+ * \context This function is \threadsafe.
+ *
+ * \return For connection types ConnectionTypeDirect and
+ * ConnectionTypeBlocking, return the return value of the invoked method. For
+ * connection type ConnectionTypeQueued, return a default-constructed R value.
+ */
+
+/**
+ * \fn Object::thread()
+ * \brief Retrieve the thread the object is bound to
+ * \context This function is \threadsafe.
+ * \return The thread the object is bound to
+ */
+
+/**
+ * \brief Move the object and all its children to a different thread
+ * \param[in] thread The target thread
+ *
+ * This method moves the object and all its children from the current thread to
+ * the new \a thread.
+ *
+ * Before the object is moved, a Message::ThreadMoveMessage message is sent to
+ * it. The message() method can be reimplement in derived classes to be notified
+ * of the upcoming thread move and perform any required processing.
+ *
+ * Moving an object that has a parent is not allowed, and causes undefined
+ * behaviour.
+ *
+ * \context This function is thread-bound.
+ */
+void Object::moveToThread(Thread *thread)
+{
+ ASSERT(Thread::current() == thread_);
+
+ if (thread_ == thread)
+ return;
+
+ if (parent_) {
+ LOG(Object, Error)
+ << "Moving object to thread with a parent is not permitted";
+ return;
+ }
+
+ notifyThreadMove();
+
+ thread->moveObject(this);
+}
+
+void Object::notifyThreadMove()
+{
+ Message msg(Message::ThreadMoveMessage);
+ message(&msg);
+
+ for (auto child : children_)
+ child->notifyThreadMove();
+}
+
+/**
+ * \fn Object::parent()
+ * \brief Retrieve the object's parent
+ * \return The object's parent
+ */
+
+void Object::connect(SignalBase *signal)
+{
+ signals_.push_back(signal);
+}
+
+void Object::disconnect(SignalBase *signal)
+{
+ for (auto iter = signals_.begin(); iter != signals_.end(); ) {
+ if (*iter == signal)
+ iter = signals_.erase(iter);
+ else
+ iter++;
+ }
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/semaphore.cpp b/src/libcamera/base/semaphore.cpp
new file mode 100644
index 00000000..7aedc6a8
--- /dev/null
+++ b/src/libcamera/base/semaphore.cpp
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * semaphore.cpp - General-purpose counting semaphore
+ */
+
+#include <libcamera/base/semaphore.h>
+#include <libcamera/base/thread.h>
+
+/**
+ * \file base/semaphore.h
+ * \brief General-purpose counting semaphore
+ */
+
+namespace libcamera {
+
+/**
+ * \class Semaphore
+ * \brief General-purpose counting semaphore
+ *
+ * A semaphore is a locking primitive that protects resources. It is created
+ * with an initial number of resources (which may be 0), and offers two
+ * primitives to acquire and release resources. The acquire() method tries to
+ * acquire a number of resources, and blocks if not enough resources are
+ * available until they get released. The release() method releases a number of
+ * resources, waking up any consumer blocked on an acquire() call.
+ */
+
+/**
+ * \brief Construct a semaphore with \a n resources
+ * \param[in] n The resource count
+ */
+Semaphore::Semaphore(unsigned int n)
+ : available_(n)
+{
+}
+
+/**
+ * \brief Retrieve the number of available resources
+ * \return The number of available resources
+ */
+unsigned int Semaphore::available()
+{
+ MutexLocker locker(mutex_);
+ return available_;
+}
+
+/**
+ * \brief Acquire \a n resources
+ * \param[in] n The resource count
+ *
+ * This method attempts to acquire \a n resources. If \a n is higher than the
+ * number of available resources, the call will block until enough resources
+ * become available.
+ */
+void Semaphore::acquire(unsigned int n)
+{
+ MutexLocker locker(mutex_);
+ cv_.wait(locker, [&] { return available_ >= n; });
+ available_ -= n;
+}
+
+/**
+ * \brief Try to acquire \a n resources without blocking
+ * \param[in] n The resource count
+ *
+ * This method attempts to acquire \a n resources. If \a n is higher than the
+ * number of available resources, it returns false immediately without
+ * acquiring any resource. Otherwise it acquires the resources and returns
+ * true.
+ *
+ * \return True if the resources have been acquired, false otherwise
+ */
+bool Semaphore::tryAcquire(unsigned int n)
+{
+ MutexLocker locker(mutex_);
+ if (available_ < n)
+ return false;
+
+ available_ -= n;
+ return true;
+}
+
+/**
+ * \brief Release \a n resources
+ * \param[in] n The resource count
+ *
+ * This method releases \a n resources, increasing the available resource count
+ * by \a n. If the number of available resources becomes large enough for any
+ * consumer blocked on an acquire() call, those consumers get woken up.
+ */
+void Semaphore::release(unsigned int n)
+{
+ {
+ MutexLocker locker(mutex_);
+ available_ += n;
+ }
+
+ cv_.notify_all();
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/signal.cpp b/src/libcamera/base/signal.cpp
new file mode 100644
index 00000000..298b2d4b
--- /dev/null
+++ b/src/libcamera/base/signal.cpp
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * signal.cpp - Signal & slot implementation
+ */
+
+#include <libcamera/base/signal.h>
+
+#include <libcamera/base/thread.h>
+
+/**
+ * \file base/signal.h
+ * \brief Signal & slot implementation
+ */
+
+namespace libcamera {
+
+namespace {
+
+/*
+ * Mutex to protect the SignalBase::slots_ and Object::signals_ lists. If lock
+ * contention needs to be decreased, this could be replaced with locks in
+ * Object and SignalBase, or with a mutex pool.
+ */
+Mutex signalsLock;
+
+} /* namespace */
+
+void SignalBase::connect(BoundMethodBase *slot)
+{
+ MutexLocker locker(signalsLock);
+
+ Object *object = slot->object();
+ if (object)
+ object->connect(this);
+ slots_.push_back(slot);
+}
+
+void SignalBase::disconnect(Object *object)
+{
+ disconnect([object](SlotList::iterator &iter) {
+ return (*iter)->match(object);
+ });
+}
+
+void SignalBase::disconnect(std::function<bool(SlotList::iterator &)> match)
+{
+ MutexLocker locker(signalsLock);
+
+ for (auto iter = slots_.begin(); iter != slots_.end(); ) {
+ if (match(iter)) {
+ Object *object = (*iter)->object();
+ if (object)
+ object->disconnect(this);
+
+ delete *iter;
+ iter = slots_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+SignalBase::SlotList SignalBase::slots()
+{
+ MutexLocker locker(signalsLock);
+ return slots_;
+}
+
+/**
+ * \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.
+ *
+ * When a slot belongs to an instance of the Object class, the slot is called
+ * in the context of the thread that the object is bound to. If the signal is
+ * emitted from the same thread, the slot will be called synchronously, before
+ * Signal::emit() returns. If the signal is emitted from a different thread,
+ * the slot will be called asynchronously from the object's thread's event
+ * loop, after the Signal::emit() method returns, with a copy of the signal's
+ * arguments. The emitter shall thus ensure that any pointer or reference
+ * passed through the signal will remain valid after the signal is emitted.
+ */
+
+/**
+ * \fn Signal::connect(T *object, R (T::*func)(Args...))
+ * \brief Connect the signal to a member function slot
+ * \param[in] object The slot object pointer
+ * \param[in] func The slot member function
+ *
+ * If the typename T inherits from Object, the signal will be automatically
+ * disconnected from the \a func slot of \a object when \a object is destroyed.
+ * Otherwise the caller shall disconnect signals manually before destroying \a
+ * object.
+ *
+ * \context This function is \threadsafe.
+ */
+
+/**
+ * \fn Signal::connect(R (*func)(Args...))
+ * \brief Connect the signal to a static function slot
+ * \param[in] func The slot static function
+ *
+ * \context This function is \threadsafe.
+ */
+
+/**
+ * \fn Signal::disconnect()
+ * \brief Disconnect the signal from all slots
+ *
+ * \context This function is \threadsafe.
+ */
+
+/**
+ * \fn Signal::disconnect(T *object)
+ * \brief Disconnect the signal from all slots of the \a object
+ * \param[in] object The object pointer whose slots to disconnect
+ *
+ * \context This function is \threadsafe.
+ */
+
+/**
+ * \fn Signal::disconnect(T *object, R (T::*func)(Args...))
+ * \brief Disconnect the signal from the \a object slot member function \a func
+ * \param[in] object The object pointer whose slots to disconnect
+ * \param[in] func The slot member function to disconnect
+ *
+ * \context This function is \threadsafe.
+ */
+
+/**
+ * \fn Signal::disconnect(R (*func)(Args...))
+ * \brief Disconnect the signal from the slot static function \a func
+ * \param[in] func The slot static function to disconnect
+ *
+ * \context This function is \threadsafe.
+ */
+
+/**
+ * \fn Signal::emit(Args... args)
+ * \brief Emit the signal and call all connected slots
+ * \param args The arguments passed to the 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.
+ *
+ * This function is not \threadsafe, but thread-safety is guaranteed against
+ * concurrent connect() and disconnect() calls.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/thread.cpp b/src/libcamera/base/thread.cpp
new file mode 100644
index 00000000..1232f895
--- /dev/null
+++ b/src/libcamera/base/thread.cpp
@@ -0,0 +1,705 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * thread.cpp - Thread support
+ */
+
+#include <libcamera/base/thread.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <list>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/event_dispatcher_poll.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/message.h>
+
+/**
+ * \page thread Thread Support
+ *
+ * libcamera supports multi-threaded applications through a threading model that
+ * sets precise rules to guarantee thread-safe usage of the API. Additionally,
+ * libcamera makes internal use of threads, and offers APIs that simplify
+ * interactions with application threads. Careful compliance with the threading
+ * model will ensure avoidance of race conditions.
+ *
+ * Every thread created by libcamera is associated with an instance of the
+ * Thread class. Those threads run an internal event loop by default to
+ * dispatch events to objects. Additionally, the main thread of the application
+ * (defined as the thread that calls CameraManager::start()) is also associated
+ * with a Thread instance, but has no event loop accessible to libcamera. Other
+ * application threads are not visible to libcamera.
+ *
+ * \section thread-objects Threads and Objects
+ *
+ * Instances of the Object class and all its derived classes are thread-aware
+ * and are bound to the thread they are created in. They are said to *live* in
+ * a thread, and they interact with the event loop of their thread for the
+ * purpose of message passing and signal delivery. Messages posted to the
+ * object with Object::postMessage() will be delivered from the event loop of
+ * the thread that the object lives in. Signals delivered to the object, unless
+ * explicitly connected with ConnectionTypeDirect, will also be delivered from
+ * the object thread's event loop.
+ *
+ * All Object instances created internally by libcamera are bound to internal
+ * threads. As objects interact with thread event loops for proper operation,
+ * creating an Object instance in a thread that has no internal event loop (such
+ * as the main application thread, or libcamera threads that have a custom main
+ * loop), prevents some features of the Object class from being used. See
+ * Thread::exec() for more details.
+ *
+ * \section thread-signals Threads and Signals
+ *
+ * When sent to a receiver that does not inherit from the Object class, signals
+ * are delivered synchronously in the thread of the sender. When the receiver
+ * inherits from the Object class, delivery is by default asynchronous if the
+ * sender and receiver live in different threads. In that case, the signal is
+ * posted to the receiver's message queue and will be delivered from the
+ * receiver's event loop, running in the receiver's thread. This mechanism can
+ * be overridden by selecting a different connection type when calling
+ * Signal::connect().
+ *
+ * \section thread-reentrancy Reentrancy and Thread-Safety
+ *
+ * Through the documentation, several terms are used to define how classes and
+ * their member functions can be used from multiple threads.
+ *
+ * - A **reentrant** function may be called simultaneously from multiple
+ * threads if and only if each invocation uses a different instance of the
+ * class. This is the default for all member functions not explictly marked
+ * otherwise.
+ *
+ * - \anchor thread-safe A **thread-safe** function may be called
+ * simultaneously from multiple threads on the same instance of a class. A
+ * thread-safe function is thus reentrant. Thread-safe functions may also be
+ * called simultaneously with any other reentrant function of the same class
+ * on the same instance.
+ *
+ * - \anchor thread-bound A **thread-bound** function may be called only from
+ * the thread that the class instances lives in (see section \ref
+ * thread-objects). For instances of classes that do not derive from the
+ * Object class, this is the thread in which the instance was created. A
+ * thread-bound function is not thread-safe, and may or may not be reentrant.
+ *
+ * Neither reentrancy nor thread-safety, in this context, mean that a function
+ * may be called simultaneously from the same thread, for instance from a
+ * callback invoked by the function. This may deadlock and isn't allowed unless
+ * separately documented.
+ *
+ * A class is defined as reentrant, thread-safe or thread-bound if all its
+ * member functions are reentrant, thread-safe or thread-bound respectively.
+ * Some member functions may additionally be documented as having additional
+ * thread-related attributes.
+ *
+ * Most classes are reentrant but not thread-safe, as making them fully
+ * thread-safe would incur locking costs considered prohibitive for the
+ * expected use cases.
+ */
+
+/**
+ * \file base/thread.h
+ * \brief Thread support
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Thread)
+
+class ThreadMain;
+
+/**
+ * \brief A queue of posted messages
+ */
+class MessageQueue
+{
+public:
+ /**
+ * \brief List of queued Message instances
+ */
+ std::list<std::unique_ptr<Message>> list_;
+ /**
+ * \brief Protects the \ref list_
+ */
+ Mutex mutex_;
+ /**
+ * \brief The recursion level for recursive Thread::dispatchMessages()
+ * calls
+ */
+ unsigned int recursion_ = 0;
+};
+
+/**
+ * \brief Thread-local internal data
+ */
+class ThreadData
+{
+public:
+ ThreadData()
+ : thread_(nullptr), running_(false), dispatcher_(nullptr)
+ {
+ }
+
+ static ThreadData *current();
+
+private:
+ friend class Thread;
+ friend class ThreadMain;
+
+ Thread *thread_;
+ bool running_;
+ pid_t tid_;
+
+ Mutex mutex_;
+
+ std::atomic<EventDispatcher *> dispatcher_;
+
+ std::condition_variable cv_;
+ std::atomic<bool> exit_;
+ int exitCode_;
+
+ MessageQueue messages_;
+};
+
+/**
+ * \brief Thread wrapper for the main thread
+ */
+class ThreadMain : public Thread
+{
+public:
+ ThreadMain()
+ {
+ data_->running_ = true;
+ }
+
+protected:
+ void run() override
+ {
+ LOG(Thread, Fatal) << "The main thread can't be restarted";
+ }
+};
+
+static thread_local ThreadData *currentThreadData = nullptr;
+static ThreadMain mainThread;
+
+/**
+ * \brief Retrieve thread-local internal data for the current thread
+ * \return The thread-local internal data for the current thread
+ */
+ThreadData *ThreadData::current()
+{
+ if (currentThreadData)
+ return currentThreadData;
+
+ /*
+ * The main thread doesn't receive thread-local data when it is
+ * started, set it here.
+ */
+ ThreadData *data = mainThread.data_;
+ data->tid_ = syscall(SYS_gettid);
+ currentThreadData = data;
+ return data;
+}
+
+/**
+ * \typedef Mutex
+ * \brief An alias for std::mutex
+ */
+
+/**
+ * \typedef MutexLocker
+ * \brief An alias for std::unique_lock<std::mutex>
+ */
+
+/**
+ * \class Thread
+ * \brief A thread of execution
+ *
+ * The Thread class is a wrapper around std::thread that handles integration
+ * with the Object, Signal and EventDispatcher classes.
+ *
+ * Thread instances by default run an event loop until the exit() method is
+ * called. The event loop dispatches events (messages, notifiers and timers)
+ * sent to the objects living in the thread. This behaviour can be modified by
+ * overriding the run() function.
+ *
+ * \section thread-stop Stopping Threads
+ *
+ * Threads can't be forcibly stopped. Instead, a thread user first requests the
+ * thread to exit and then waits for the thread's main function to react to the
+ * request and return, at which points the thread will stop.
+ *
+ * For threads running exec(), the exit() function is used to request the thread
+ * to exit. For threads subclassing the Thread class and implementing a custom
+ * run() function, a subclass-specific mechanism shall be provided. In either
+ * case, the wait() function shall be called to wait for the thread to stop.
+ *
+ * Due to their asynchronous nature, threads are subject to race conditions when
+ * they stop. This is of particular importance for messages posted to the thread
+ * with postMessage() (and the other mechanisms that rely on it, such as
+ * Object::invokeMethod() or asynchronous signal delivery). To understand the
+ * issues, three contexts need to be considered:
+ *
+ * - The worker is the Thread performing work and being instructed to stop.
+ * - The controller is the context which instructs the worker thread to stop.
+ * - The other contexts are any threads other than the worker and controller
+ * that interact with the worker thread.
+ *
+ * Messages posted to the worker thread from the controller context before
+ * calling exit() are queued to the thread's message queue, and the Thread class
+ * offers no guarantee that those messages will be processed before the thread
+ * stops. This allows threads to stop fast.
+ *
+ * A thread that requires delivery of messages posted from the controller
+ * context before exit() should reimplement the run() function and call
+ * dispatchMessages() after exec().
+ *
+ * Messages posted to the worker thread from the other contexts are asynchronous
+ * with respect to the exit() call from the controller context. There is no
+ * guarantee as to whether those messages will be processed or not before the
+ * thread stops.
+ *
+ * Messages that are not processed will stay in the queue, in the exact same way
+ * as messages posted after the thread has stopped. They will be processed when
+ * the thread is restarted. If the thread is never restarted, they will be
+ * deleted without being processed when the Thread instance is destroyed.
+ */
+
+/**
+ * \brief Create a thread
+ */
+Thread::Thread()
+{
+ data_ = new ThreadData;
+ data_->thread_ = this;
+}
+
+Thread::~Thread()
+{
+ delete data_->dispatcher_.load(std::memory_order_relaxed);
+ delete data_;
+}
+
+/**
+ * \brief Start the thread
+ */
+void Thread::start()
+{
+ MutexLocker locker(data_->mutex_);
+
+ if (data_->running_)
+ return;
+
+ data_->running_ = true;
+ data_->exitCode_ = -1;
+ data_->exit_.store(false, std::memory_order_relaxed);
+
+ thread_ = std::thread(&Thread::startThread, this);
+}
+
+void Thread::startThread()
+{
+ struct ThreadCleaner {
+ ThreadCleaner(Thread *thread, void (Thread::*cleaner)())
+ : thread_(thread), cleaner_(cleaner)
+ {
+ }
+ ~ThreadCleaner()
+ {
+ (thread_->*cleaner_)();
+ }
+
+ Thread *thread_;
+ void (Thread::*cleaner_)();
+ };
+
+ /*
+ * Make sure the thread is cleaned up even if the run method exits
+ * abnormally (for instance via a direct call to pthread_cancel()).
+ */
+ thread_local ThreadCleaner cleaner(this, &Thread::finishThread);
+
+ data_->tid_ = syscall(SYS_gettid);
+ currentThreadData = data_;
+
+ run();
+}
+
+/**
+ * \brief Enter the event loop
+ *
+ * This method enters an event loop based on the event dispatcher instance for
+ * the thread, and blocks until the exit() method is called. It is meant to be
+ * called within the thread from the run() method and shall not be called
+ * outside of the thread.
+ *
+ * \return The exit code passed to the exit() method
+ */
+int Thread::exec()
+{
+ MutexLocker locker(data_->mutex_);
+
+ EventDispatcher *dispatcher = eventDispatcher();
+
+ locker.unlock();
+
+ while (!data_->exit_.load(std::memory_order_acquire))
+ dispatcher->processEvents();
+
+ locker.lock();
+
+ return data_->exitCode_;
+}
+
+/**
+ * \brief Main method of the thread
+ *
+ * When the thread is started with start(), it calls this method in the context
+ * of the new thread. The run() method can be overridden to perform custom
+ * work, either custom initialization and cleanup before and after calling the
+ * Thread::exec() function, or a custom thread loop altogether. When this
+ * method returns the thread execution is stopped, and the \ref finished signal
+ * is emitted.
+ *
+ * Note that if this function is overridden and doesn't call Thread::exec(), no
+ * events will be dispatched to the objects living in the thread. These objects
+ * will not be able to use the EventNotifier, Timer or Message facilities. This
+ * includes functions that rely on message dispatching, such as
+ * Object::deleteLater().
+ *
+ * The base implementation just calls exec().
+ */
+void Thread::run()
+{
+ exec();
+}
+
+void Thread::finishThread()
+{
+ data_->mutex_.lock();
+ data_->running_ = false;
+ data_->mutex_.unlock();
+
+ finished.emit(this);
+ data_->cv_.notify_all();
+}
+
+/**
+ * \brief Stop the thread's event loop
+ * \param[in] code The exit code
+ *
+ * This method interrupts the event loop started by the exec() method, causing
+ * exec() to return \a code.
+ *
+ * Calling exit() on a thread that reimplements the run() method and doesn't
+ * call exec() will likely have no effect.
+ *
+ * \context This function is \threadsafe.
+ */
+void Thread::exit(int code)
+{
+ data_->exitCode_ = code;
+ data_->exit_.store(true, std::memory_order_release);
+
+ EventDispatcher *dispatcher = data_->dispatcher_.load(std::memory_order_relaxed);
+ if (!dispatcher)
+ return;
+
+ dispatcher->interrupt();
+}
+
+/**
+ * \brief Wait for the thread to finish
+ * \param[in] duration Maximum wait duration
+ *
+ * This function waits until the thread finishes or the \a duration has
+ * elapsed, whichever happens first. If \a duration is equal to
+ * utils::duration::max(), the wait never times out. If the thread is not
+ * running the function returns immediately.
+ *
+ * \context This function is \threadsafe.
+ *
+ * \return True if the thread has finished, or false if the wait timed out
+ */
+bool Thread::wait(utils::duration duration)
+{
+ bool hasFinished = true;
+
+ {
+ MutexLocker locker(data_->mutex_);
+
+ if (duration == utils::duration::max())
+ data_->cv_.wait(locker, [&]() { return !data_->running_; });
+ else
+ hasFinished = data_->cv_.wait_for(locker, duration,
+ [&]() { return !data_->running_; });
+ }
+
+ if (thread_.joinable())
+ thread_.join();
+
+ return hasFinished;
+}
+
+/**
+ * \brief Check if the thread is running
+ *
+ * A Thread instance is considered as running once the underlying thread has
+ * started. This method guarantees that it returns true after the start()
+ * method returns, and false after the wait() method returns.
+ *
+ * \context This function is \threadsafe.
+ *
+ * \return True if the thread is running, false otherwise
+ */
+bool Thread::isRunning()
+{
+ MutexLocker locker(data_->mutex_);
+ return data_->running_;
+}
+
+/**
+ * \var Thread::finished
+ * \brief Signal the end of thread execution
+ */
+
+/**
+ * \brief Retrieve the Thread instance for the current thread
+ * \context This function is \threadsafe.
+ * \return The Thread instance for the current thread
+ */
+Thread *Thread::current()
+{
+ ThreadData *data = ThreadData::current();
+ return data->thread_;
+}
+
+/**
+ * \brief Retrieve the ID of the current thread
+ *
+ * The thread ID corresponds to the Linux thread ID (TID) as returned by the
+ * gettid system call.
+ *
+ * \context This function is \threadsafe.
+ *
+ * \return The ID of the current thread
+ */
+pid_t Thread::currentId()
+{
+ ThreadData *data = ThreadData::current();
+ return data->tid_;
+}
+
+/**
+ * \brief Retrieve the event dispatcher
+ *
+ * This function retrieves the internal event dispatcher for the thread. The
+ * returned event dispatcher is valid until the thread is destroyed.
+ *
+ * \context This function is \threadsafe.
+ *
+ * \return Pointer to the event dispatcher
+ */
+EventDispatcher *Thread::eventDispatcher()
+{
+ if (!data_->dispatcher_.load(std::memory_order_relaxed))
+ data_->dispatcher_.store(new EventDispatcherPoll(),
+ std::memory_order_release);
+
+ return data_->dispatcher_.load(std::memory_order_relaxed);
+}
+
+/**
+ * \brief Post a message to the thread for the \a receiver
+ * \param[in] msg The message
+ * \param[in] receiver The receiver
+ *
+ * This method stores the message \a msg in the message queue of the thread for
+ * the \a receiver and wake up the thread's event loop. Message ownership is
+ * passed to the thread, and the message will be deleted after being delivered.
+ *
+ * Messages are delivered through the thread's event loop. If the thread is not
+ * running its event loop the message will not be delivered until the event
+ * loop gets started.
+ *
+ * When the thread is stopped, posted messages may not have all been processed.
+ * See \ref thread-stop for additional information.
+ *
+ * If the \a receiver is not bound to this thread the behaviour is undefined.
+ *
+ * \context This function is \threadsafe.
+ *
+ * \sa exec()
+ */
+void Thread::postMessage(std::unique_ptr<Message> msg, Object *receiver)
+{
+ msg->receiver_ = receiver;
+
+ ASSERT(data_ == receiver->thread()->data_);
+
+ MutexLocker locker(data_->messages_.mutex_);
+ data_->messages_.list_.push_back(std::move(msg));
+ receiver->pendingMessages_++;
+ locker.unlock();
+
+ EventDispatcher *dispatcher =
+ data_->dispatcher_.load(std::memory_order_acquire);
+ if (dispatcher)
+ dispatcher->interrupt();
+}
+
+/**
+ * \brief Remove all posted messages for the \a receiver
+ * \param[in] receiver The receiver
+ *
+ * If the \a receiver is not bound to this thread the behaviour is undefined.
+ */
+void Thread::removeMessages(Object *receiver)
+{
+ ASSERT(data_ == receiver->thread()->data_);
+
+ MutexLocker locker(data_->messages_.mutex_);
+ if (!receiver->pendingMessages_)
+ return;
+
+ std::vector<std::unique_ptr<Message>> toDelete;
+ for (std::unique_ptr<Message> &msg : data_->messages_.list_) {
+ if (!msg)
+ continue;
+ if (msg->receiver_ != receiver)
+ continue;
+
+ /*
+ * Move the message to the pending deletion list to delete it
+ * after releasing the lock. The messages list element will
+ * contain a null pointer, and will be removed when dispatching
+ * messages.
+ */
+ toDelete.push_back(std::move(msg));
+ receiver->pendingMessages_--;
+ }
+
+ ASSERT(!receiver->pendingMessages_);
+ locker.unlock();
+
+ toDelete.clear();
+}
+
+/**
+ * \brief Dispatch posted messages for this thread
+ * \param[in] type The message type
+ *
+ * This function immediately dispatches all the messages previously posted for
+ * this thread with postMessage() that match the message \a type. If the \a type
+ * is Message::Type::None, all messages are dispatched.
+ *
+ * Messages shall only be dispatched from the current thread, typically within
+ * the thread from the run() function. Calling this function outside of the
+ * thread results in undefined behaviour.
+ *
+ * This function is not thread-safe, but it may be called recursively in the
+ * same thread from an object's message handler. It guarantees delivery of
+ * messages in the order they have been posted in all cases.
+ */
+void Thread::dispatchMessages(Message::Type type)
+{
+ ASSERT(data_ == ThreadData::current());
+
+ ++data_->messages_.recursion_;
+
+ MutexLocker locker(data_->messages_.mutex_);
+
+ std::list<std::unique_ptr<Message>> &messages = data_->messages_.list_;
+
+ for (std::unique_ptr<Message> &msg : messages) {
+ if (!msg)
+ continue;
+
+ if (type != Message::Type::None && msg->type() != type)
+ continue;
+
+ /*
+ * Move the message, setting the entry in the list to null. It
+ * will cause recursive calls to ignore the entry, and the erase
+ * loop at the end of the function to delete it from the list.
+ */
+ std::unique_ptr<Message> message = std::move(msg);
+
+ Object *receiver = message->receiver_;
+ ASSERT(data_ == receiver->thread()->data_);
+ receiver->pendingMessages_--;
+
+ locker.unlock();
+ receiver->message(message.get());
+ message.reset();
+ locker.lock();
+ }
+
+ /*
+ * If the recursion level is 0, erase all null messages in the list. We
+ * can't do so during recursion, as it would invalidate the iterator of
+ * the outer calls.
+ */
+ if (!--data_->messages_.recursion_) {
+ for (auto iter = messages.begin(); iter != messages.end(); ) {
+ if (!*iter)
+ iter = messages.erase(iter);
+ else
+ ++iter;
+ }
+ }
+}
+
+/**
+ * \brief Move an \a object and all its children to the thread
+ * \param[in] object The object
+ */
+void Thread::moveObject(Object *object)
+{
+ ThreadData *currentData = object->thread_->data_;
+ ThreadData *targetData = data_;
+
+ MutexLocker lockerFrom(currentData->messages_.mutex_, std::defer_lock);
+ MutexLocker lockerTo(targetData->messages_.mutex_, std::defer_lock);
+ std::lock(lockerFrom, lockerTo);
+
+ moveObject(object, currentData, targetData);
+}
+
+void Thread::moveObject(Object *object, ThreadData *currentData,
+ ThreadData *targetData)
+{
+ /* Move pending messages to the message queue of the new thread. */
+ if (object->pendingMessages_) {
+ unsigned int movedMessages = 0;
+
+ for (std::unique_ptr<Message> &msg : currentData->messages_.list_) {
+ if (!msg)
+ continue;
+ if (msg->receiver_ != object)
+ continue;
+
+ targetData->messages_.list_.push_back(std::move(msg));
+ movedMessages++;
+ }
+
+ if (movedMessages) {
+ EventDispatcher *dispatcher =
+ targetData->dispatcher_.load(std::memory_order_acquire);
+ if (dispatcher)
+ dispatcher->interrupt();
+ }
+ }
+
+ object->thread_ = this;
+
+ /* Move all children. */
+ for (auto child : object->children_)
+ moveObject(child, currentData, targetData);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/timer.cpp b/src/libcamera/base/timer.cpp
new file mode 100644
index 00000000..9c54352d
--- /dev/null
+++ b/src/libcamera/base/timer.cpp
@@ -0,0 +1,185 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * timer.cpp - Generic timer
+ */
+
+#include <libcamera/base/timer.h>
+
+#include <chrono>
+
+#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/message.h>
+#include <libcamera/base/thread.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/camera_manager.h>
+
+/**
+ * \file base/timer.h
+ * \brief Generic timer
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Timer)
+
+/**
+ * \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().
+ *
+ * The timer deadline is specified as either a duration in milliseconds or an
+ * absolute time point. If the deadline is set to the current time or to the
+ * past, the timer will time out immediately when execution returns to the
+ * event loop of the timer's thread.
+ *
+ * Timers run in the thread they belong to, and thus emit the \a ref timeout
+ * signal from that thread. To avoid race conditions they must not be started
+ * or stopped from a different thread, attempts to do so will be rejected and
+ * logged, and may cause undefined behaviour.
+ */
+
+/**
+ * \brief Construct a timer
+ * \param[in] parent The parent Object
+ */
+Timer::Timer(Object *parent)
+ : Object(parent), running_(false)
+{
+}
+
+Timer::~Timer()
+{
+ stop();
+}
+
+/**
+ * \fn Timer::start(unsigned int msec)
+ * \brief Start or restart the timer with a timeout of \a msec
+ * \param[in] msec The timer duration in milliseconds
+ *
+ * If the timer is already running it will be stopped and restarted.
+ *
+ * \context This function is \threadbound.
+ */
+
+/**
+ * \brief Start or restart the timer with a timeout of \a duration
+ * \param[in] duration The timer duration in milliseconds
+ *
+ * If the timer is already running it will be stopped and restarted.
+ *
+ * \context This function is \threadbound.
+ */
+void Timer::start(std::chrono::milliseconds duration)
+{
+ start(utils::clock::now() + duration);
+}
+
+/**
+ * \brief Start or restart the timer with a \a deadline
+ * \param[in] deadline The timer deadline
+ *
+ * If the timer is already running it will be stopped and restarted.
+ *
+ * \context This function is \threadbound.
+ */
+void Timer::start(std::chrono::steady_clock::time_point deadline)
+{
+ if (Thread::current() != thread()) {
+ LOG(Timer, Error) << "Timer can't be started from another thread";
+ return;
+ }
+
+ deadline_ = deadline;
+
+ LOG(Timer, Debug)
+ << "Starting timer " << this << ": deadline "
+ << utils::time_point_to_string(deadline_);
+
+ if (isRunning())
+ unregisterTimer();
+
+ registerTimer();
+}
+
+/**
+ * \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.
+ *
+ * \context This function is \threadbound.
+ */
+void Timer::stop()
+{
+ if (!isRunning())
+ return;
+
+ if (Thread::current() != thread()) {
+ LOG(Timer, Error) << "Timer can't be stopped from another thread";
+ return;
+ }
+
+ unregisterTimer();
+}
+
+void Timer::registerTimer()
+{
+ thread()->eventDispatcher()->registerTimer(this);
+ running_ = true;
+}
+
+void Timer::unregisterTimer()
+{
+ running_ = false;
+ thread()->eventDispatcher()->unregisterTimer(this);
+}
+
+/**
+ * \brief Check if the timer is running
+ * \return True if the timer is running, false otherwise
+ */
+bool Timer::isRunning() const
+{
+ return running_;
+}
+
+/**
+ * \fn Timer::deadline()
+ * \brief Retrieve the timer deadline
+ * \return The timer deadline
+ */
+
+/**
+ * \var Timer::timeout
+ * \brief Signal emitted when the timer times out
+ *
+ * The timer pointer is passed as a parameter.
+ */
+
+void Timer::message(Message *msg)
+{
+ if (msg->type() == Message::ThreadMoveMessage) {
+ if (isRunning()) {
+ unregisterTimer();
+ invokeMethod(&Timer::registerTimer,
+ ConnectionTypeQueued);
+ }
+ }
+
+ Object::message(msg);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp
new file mode 100644
index 00000000..45b92b67
--- /dev/null
+++ b/src/libcamera/base/utils.cpp
@@ -0,0 +1,463 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * utils.cpp - Miscellaneous utility functions
+ */
+
+#include <libcamera/base/utils.h>
+
+#include <iomanip>
+#include <sstream>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * \file base/utils.h
+ * \brief Miscellaneous utility functions
+ */
+
+namespace libcamera {
+
+namespace utils {
+
+/**
+ * \brief Strip the directory prefix from the path
+ * \param[in] path The path to process
+ *
+ * basename is implemented differently across different C libraries. This
+ * implementation matches the one provided by the GNU libc, and does not
+ * modify its input parameter.
+ *
+ * \return A pointer within the given path without any leading directory
+ * components.
+ */
+const char *basename(const char *path)
+{
+ const char *base = strrchr(path, '/');
+ return base ? base + 1 : path;
+}
+
+/**
+ * \brief Get an environment variable
+ * \param[in] name The name of the variable to return
+ *
+ * The environment list is searched to find the variable 'name', and the
+ * corresponding string is returned.
+ *
+ * If 'secure execution' is required then this function always returns NULL to
+ * avoid vulnerabilities that could occur if set-user-ID or set-group-ID
+ * programs accidentally trust the environment.
+ *
+ * \note Not all platforms may support the features required to implement the
+ * secure execution check, in which case this function behaves as getenv(). A
+ * notable example of this is Android.
+ *
+ * \return A pointer to the value in the environment or NULL if the requested
+ * environment variable doesn't exist or if secure execution is required.
+ */
+char *secure_getenv(const char *name)
+{
+#if HAVE_SECURE_GETENV
+ return ::secure_getenv(name);
+#else
+#if HAVE_ISSETUGID
+ if (issetugid())
+ return NULL;
+#endif
+ return getenv(name);
+#endif
+}
+
+/**
+ * \brief Identify the dirname portion of a path
+ * \param[in] path The full path to parse
+ *
+ * This function conforms with the behaviour of the %dirname() function as
+ * defined by POSIX.
+ *
+ * \return A string of the directory component of the path
+ */
+std::string dirname(const std::string &path)
+{
+ if (path.empty())
+ return ".";
+
+ /*
+ * Skip all trailing slashes. If the path is only made of slashes,
+ * return "/".
+ */
+ size_t pos = path.size() - 1;
+ while (path[pos] == '/') {
+ if (!pos)
+ return "/";
+ pos--;
+ }
+
+ /*
+ * Find the previous slash. If the path contains no non-trailing slash,
+ * return ".".
+ */
+ while (path[pos] != '/') {
+ if (!pos)
+ return ".";
+ pos--;
+ }
+
+ /*
+ * Return the directory name up to (but not including) any trailing
+ * slash. If this would result in an empty string, return "/".
+ */
+ while (path[pos] == '/') {
+ if (!pos)
+ return "/";
+ pos--;
+ }
+
+ return path.substr(0, pos + 1);
+}
+
+/**
+ * \fn std::vector<typename T::key_type> map_keys(const T &map)
+ * \brief Retrieve the keys of a std::map<>
+ * \param[in] map The map whose keys to retrieve
+ * \return A std::vector<> containing the keys of \a map
+ */
+
+/**
+ * \fn libcamera::utils::set_overlap(InputIt1 first1, InputIt1 last1,
+ * InputIt2 first2, InputIt2 last2)
+ * \brief Count the number of elements in the intersection of two ranges
+ *
+ * Count the number of elements in the intersection of the sorted ranges [\a
+ * first1, \a last1) and [\a first1, \a last2). Elements are compared using
+ * operator< and the ranges must be sorted with respect to the same.
+ *
+ * \return The number of elements in the intersection of the two ranges
+ */
+
+/**
+ * \typedef clock
+ * \brief The libcamera clock (monotonic)
+ */
+
+/**
+ * \typedef duration
+ * \brief The libcamera duration related to libcamera::utils::clock
+ */
+
+/**
+ * \typedef time_point
+ * \brief The libcamera time point related to libcamera::utils::clock
+ */
+
+/**
+ * \brief Convert a duration to a timespec
+ * \param[in] value The duration
+ * \return A timespec expressing the duration
+ */
+struct timespec duration_to_timespec(const duration &value)
+{
+ uint64_t nsecs = std::chrono::duration_cast<std::chrono::nanoseconds>(value).count();
+ struct timespec ts;
+ ts.tv_sec = nsecs / 1000000000ULL;
+ ts.tv_nsec = nsecs % 1000000000ULL;
+ return ts;
+}
+
+/**
+ * \brief Convert a time point to a string representation
+ * \param[in] time The time point
+ * \return A string representing the time point in hh:mm:ss.nanoseconds format
+ */
+std::string time_point_to_string(const time_point &time)
+{
+ uint64_t nsecs = std::chrono::duration_cast<std::chrono::nanoseconds>(time.time_since_epoch()).count();
+ unsigned int secs = nsecs / 1000000000ULL;
+
+ std::ostringstream ossTimestamp;
+ ossTimestamp.fill('0');
+ ossTimestamp << secs / (60 * 60) << ":"
+ << std::setw(2) << (secs / 60) % 60 << ":"
+ << std::setw(2) << secs % 60 << "."
+ << std::setw(9) << nsecs % 1000000000ULL;
+ return ossTimestamp.str();
+}
+
+std::basic_ostream<char, std::char_traits<char>> &
+operator<<(std::basic_ostream<char, std::char_traits<char>> &stream, const _hex &h)
+{
+ stream << "0x";
+
+ std::ostream::fmtflags flags = stream.setf(std::ios_base::hex,
+ std::ios_base::basefield);
+ std::streamsize width = stream.width(h.w);
+ char fill = stream.fill('0');
+
+ stream << h.v;
+
+ stream.flags(flags);
+ stream.width(width);
+ stream.fill(fill);
+
+ return stream;
+}
+
+/**
+ * \fn hex(T value, unsigned int width)
+ * \brief Write an hexadecimal value to an output string
+ * \param value The value
+ * \param width The width
+ *
+ * Return an object of unspecified type such that, if \a os is the name of an
+ * output stream of type std::ostream, and T is an integer type, then the
+ * expression
+ *
+ * \code{.cpp}
+ * os << utils::hex(value)
+ * \endcode
+ *
+ * will output the \a value to the stream in hexadecimal form with the base
+ * prefix and the filling character set to '0'. The field width is set to \a
+ * width if specified to a non-zero value, or to the native width of type T
+ * otherwise. The \a os stream configuration is not modified.
+ */
+
+/**
+ * \brief Copy a string with a size limit
+ * \param[in] dst The destination string
+ * \param[in] src The source string
+ * \param[in] size The size of the destination string
+ *
+ * This function copies the null-terminated string \a src to \a dst with a limit
+ * of \a size - 1 characters, and null-terminates the result if \a size is
+ * larger than 0. If \a src is larger than \a size - 1, \a dst is truncated.
+ *
+ * \return The size of \a src
+ */
+size_t strlcpy(char *dst, const char *src, size_t size)
+{
+ if (size) {
+ strncpy(dst, src, size);
+ dst[size - 1] = '\0';
+ }
+
+ return strlen(src);
+}
+
+details::StringSplitter::StringSplitter(const std::string &str, const std::string &delim)
+ : str_(str), delim_(delim)
+{
+}
+
+details::StringSplitter::iterator::iterator(const details::StringSplitter *ss, std::string::size_type pos)
+ : ss_(ss), pos_(pos)
+{
+ next_ = ss_->str_.find(ss_->delim_, pos_);
+}
+
+details::StringSplitter::iterator &details::StringSplitter::iterator::operator++()
+{
+ pos_ = next_;
+ if (pos_ != std::string::npos) {
+ pos_ += ss_->delim_.length();
+ next_ = ss_->str_.find(ss_->delim_, pos_);
+ }
+
+ return *this;
+}
+
+std::string details::StringSplitter::iterator::operator*() const
+{
+ std::string::size_type count;
+ count = next_ != std::string::npos ? next_ - pos_ : next_;
+ return ss_->str_.substr(pos_, count);
+}
+
+bool details::StringSplitter::iterator::operator!=(const details::StringSplitter::iterator &other) const
+{
+ return pos_ != other.pos_;
+}
+
+details::StringSplitter::iterator details::StringSplitter::begin() const
+{
+ return iterator(this, 0);
+}
+
+details::StringSplitter::iterator details::StringSplitter::end() const
+{
+ return iterator(this, std::string::npos);
+}
+
+/**
+ * \fn template<typename Container, typename UnaryOp> \
+ * std::string utils::join(const Container &items, const std::string &sep, UnaryOp op)
+ * \brief Join elements of a container in a string with a separator
+ * \param[in] items The container
+ * \param[in] sep The separator to add between elements
+ * \param[in] op A function that converts individual elements to strings
+ *
+ * This function joins all elements in the \a items container into a string and
+ * returns it. The \a sep separator is added between elements. If the container
+ * elements are not implicitly convertible to std::string, the \a op function
+ * shall be provided to perform conversion of elements to std::string.
+ *
+ * \return A string that concatenates all elements in the container
+ */
+
+/**
+ * \fn split(const std::string &str, const std::string &delim)
+ * \brief Split a string based on a delimiter
+ * \param[in] str The string to split
+ * \param[in] delim The delimiter string
+ *
+ * This function splits the string \a str into substrings based on the
+ * delimiter \a delim. It returns an object of unspecified type that can be
+ * used in a range-based for loop and yields the substrings in sequence.
+ *
+ * \return An object that can be used in a range-based for loop to iterate over
+ * the substrings
+ */
+details::StringSplitter split(const std::string &str, const std::string &delim)
+{
+ /** \todo Try to avoid copies of str and delim */
+ return details::StringSplitter(str, delim);
+}
+
+/**
+ * \brief Remove any non-ASCII characters from a string
+ * \param[in] str The string to strip
+ *
+ * Remove all non-ASCII characters from a string.
+ *
+ * \return A string equal to \a str stripped out of all non-ASCII characters
+ */
+std::string toAscii(const std::string &str)
+{
+ std::string ret;
+ for (const char &c : str)
+ if (!(c & 0x80))
+ ret += c;
+ return ret;
+}
+
+/**
+ * \fn alignDown(unsigned int value, unsigned int alignment)
+ * \brief Align \a value down to \a alignment
+ * \param[in] value The value to align
+ * \param[in] alignment The alignment
+ * \return The value rounded down to the nearest multiple of \a alignment
+ */
+
+/**
+ * \fn alignUp(unsigned int value, unsigned int alignment)
+ * \brief Align \a value up to \a alignment
+ * \param[in] value The value to align
+ * \param[in] alignment The alignment
+ * \return The value rounded up to the nearest multiple of \a alignment
+ */
+
+/**
+ * \fn reverse(T &&iterable)
+ * \brief Wrap an iterable to reverse iteration in a range-based loop
+ * \param[in] iterable The iterable
+ * \return A value of unspecified type that, when used in a range-based for
+ * loop, will cause the loop to iterate over the \a iterable in reverse order
+ */
+
+/**
+ * \fn enumerate(T &iterable)
+ * \brief Wrap an iterable to enumerate index and value in a range-based loop
+ * \param[in] iterable The iterable
+ *
+ * Range-based for loops are handy and widely preferred in C++, but are limited
+ * in their ability to replace for loops that require access to a loop counter.
+ * The enumerate() function solves this problem by wrapping the \a iterable in
+ * an adapter that, when used as a range-expression, will provide iterators
+ * whose value_type is a pair of index and value reference.
+ *
+ * The iterable must support std::begin() and std::end(). This includes all
+ * containers provided by the standard C++ library, as well as C-style arrays.
+ *
+ * A typical usage pattern would use structured binding to store the index and
+ * value in two separate variables:
+ *
+ * \code{.cpp}
+ * std::vector<int> values = ...;
+ *
+ * for (auto [index, value] : utils::enumerate(values)) {
+ * ...
+ * }
+ * \endcode
+ *
+ * Note that the argument to enumerate() has to be an lvalue, as the lifetime
+ * of any rvalue would not be extended to the whole for loop. The compiler will
+ * complain if an rvalue is passed to the function, in which case it should be
+ * stored in a local variable before the loop.
+ *
+ * \return A value of unspecified type that, when used in a range-based for
+ * loop, iterates over an indexed view of the \a iterable
+ */
+
+/**
+ * \class Duration
+ * \brief Helper class from std::chrono::duration that represents a time
+ * duration in nanoseconds with double precision
+ */
+
+/**
+ * \fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)
+ * \brief Construct a Duration by converting an arbitrary std::chrono::duration
+ * \param[in] d The std::chrono::duration object to convert from
+ *
+ * The constructed \a Duration object is internally represented in double
+ * precision with nanoseconds ticks.
+ */
+
+/**
+ * \fn Duration::get<Period>()
+ * \brief Retrieve the tick count, converted to the timebase provided by the
+ * template argument Period of type \a std::ratio
+ *
+ * A typical usage example is given below:
+ *
+ * \code{.cpp}
+ * utils::Duration d = 5s;
+ * double d_in_ms = d.get<std::milli>();
+ * \endcode
+ *
+ * \return The tick count of the Duration expressed in \a Period
+ */
+
+/**
+ * \fn Duration::operator bool()
+ * \brief Boolean operator to test if a \a Duration holds a non-zero time value
+ *
+ * \return True if \a Duration is a non-zero time value, False otherwise
+ */
+
+} /* namespace utils */
+
+#ifndef __DOXYGEN__
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
+ const utils::Duration &d)
+{
+ std::basic_ostringstream<CharT, Traits> s;
+
+ s.flags(os.flags());
+ s.imbue(os.getloc());
+ s.setf(std::ios_base::fixed, std::ios_base::floatfield);
+ s.precision(2);
+ s << d.get<std::micro>() << "us";
+ return os << s.str();
+}
+
+template
+std::basic_ostream<char, std::char_traits<char>> &
+operator<< <char, std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>> &os,
+ const utils::Duration &d);
+#endif
+
+} /* namespace libcamera */