summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcamera/camera_manager.cpp15
-rw-r--r--src/libcamera/include/thread.h61
-rw-r--r--src/libcamera/meson.build3
-rw-r--r--src/libcamera/thread.cpp335
4 files changed, 403 insertions, 11 deletions
diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
index 337496c2..2cf01423 100644
--- a/src/libcamera/camera_manager.cpp
+++ b/src/libcamera/camera_manager.cpp
@@ -14,6 +14,7 @@
#include "event_dispatcher_poll.h"
#include "log.h"
#include "pipeline_handler.h"
+#include "thread.h"
#include "utils.h"
/**
@@ -56,7 +57,7 @@ LOG_DEFINE_CATEGORY(Camera)
*/
CameraManager::CameraManager()
- : enumerator_(nullptr), dispatcher_(nullptr)
+ : enumerator_(nullptr)
{
}
@@ -247,12 +248,7 @@ CameraManager *CameraManager::instance()
*/
void CameraManager::setEventDispatcher(std::unique_ptr<EventDispatcher> dispatcher)
{
- if (dispatcher_) {
- LOG(Camera, Warning) << "Event dispatcher is already set";
- return;
- }
-
- dispatcher_ = std::move(dispatcher);
+ Thread::current()->setEventDispatcher(std::move(dispatcher));
}
/**
@@ -268,10 +264,7 @@ void CameraManager::setEventDispatcher(std::unique_ptr<EventDispatcher> dispatch
*/
EventDispatcher *CameraManager::eventDispatcher()
{
- if (!dispatcher_)
- dispatcher_ = utils::make_unique<EventDispatcherPoll>();
-
- return dispatcher_.get();
+ return Thread::current()->eventDispatcher();
}
} /* namespace libcamera */
diff --git a/src/libcamera/include/thread.h b/src/libcamera/include/thread.h
new file mode 100644
index 00000000..e881d90e
--- /dev/null
+++ b/src/libcamera/include/thread.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * thread.h - Thread support
+ */
+#ifndef __LIBCAMERA_THREAD_H__
+#define __LIBCAMERA_THREAD_H__
+
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include <libcamera/signal.h>
+
+namespace libcamera {
+
+class EventDispatcher;
+class ThreadData;
+class ThreadMain;
+
+using Mutex = std::mutex;
+using MutexLocker = std::unique_lock<std::mutex>;
+
+class Thread
+{
+public:
+ Thread();
+ virtual ~Thread();
+
+ void start();
+ void exit(int code = 0);
+ void wait();
+
+ bool isRunning();
+
+ Signal<Thread *> finished;
+
+ static Thread *current();
+
+ EventDispatcher *eventDispatcher();
+ void setEventDispatcher(std::unique_ptr<EventDispatcher> dispatcher);
+
+protected:
+ int exec();
+ virtual void run();
+
+private:
+ void startThread();
+ void finishThread();
+
+ friend class ThreadData;
+ friend class ThreadMain;
+
+ std::thread thread_;
+ ThreadData *data_;
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_THREAD_H__ */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index adc749ec..bad60da4 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -23,6 +23,7 @@ libcamera_sources = files([
'request.cpp',
'signal.cpp',
'stream.cpp',
+ 'thread.cpp',
'timer.cpp',
'utils.cpp',
'v4l2_controls.cpp',
@@ -45,6 +46,7 @@ libcamera_headers = files([
'include/media_device.h',
'include/media_object.h',
'include/pipeline_handler.h',
+ 'include/thread.h',
'include/utils.h',
'include/v4l2_device.h',
'include/v4l2_subdevice.h',
@@ -91,6 +93,7 @@ libcamera_sources += version_cpp
libcamera_deps = [
cc.find_library('dl'),
libudev,
+ dependency('threads'),
]
libcamera = shared_library('camera',
diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp
new file mode 100644
index 00000000..95636eca
--- /dev/null
+++ b/src/libcamera/thread.cpp
@@ -0,0 +1,335 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * thread.cpp - Thread support
+ */
+
+#include "thread.h"
+
+#include <atomic>
+
+#include <libcamera/event_dispatcher.h>
+
+#include "event_dispatcher_poll.h"
+#include "log.h"
+
+/**
+ * \file thread.h
+ * \brief Thread support
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Thread)
+
+class ThreadMain;
+
+/**
+ * \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_;
+
+ Mutex mutex_;
+
+ std::atomic<EventDispatcher *> dispatcher_;
+
+ std::atomic<bool> exit_;
+ int exitCode_;
+};
+
+/**
+ * \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_;
+ 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. A custom event dispatcher may be installed with
+ * setEventDispatcher(), otherwise a poll-based event dispatcher is used. This
+ * behaviour can be overriden by overloading the run() method.
+ */
+
+/**
+ * \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);
+
+ currentThreadData = data_;
+
+ run();
+}
+
+/**
+ * \brief Enter the event loop
+ *
+ * This method enter 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 overloaded to perform custom
+ * work. When this method returns the thread execution is stopped, and the \ref
+ * finished signal is emitted.
+ *
+ * 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);
+}
+
+/**
+ * \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.
+ */
+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
+ *
+ * This method waits until the thread finishes, or returns immediately if the
+ * thread is not running.
+ */
+void Thread::wait()
+{
+ if (thread_.joinable())
+ thread_.join();
+}
+
+/**
+ * \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.
+ *
+ * \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
+ * \return The Thread instance for the current thread
+ */
+Thread *Thread::current()
+{
+ ThreadData *data = ThreadData::current();
+ return data->thread_;
+}
+
+/**
+ * \brief Set the event dispatcher
+ * \param[in] dispatcher Pointer to the event dispatcher
+ *
+ * Threads that run an event loop require an event dispatcher to integrate
+ * event notification and timers with the loop. Users that want to provide
+ * their own event dispatcher shall call this method once and only once before
+ * the thread is started with start(). If no event dispatcher is provided, a
+ * default poll-based implementation will be used.
+ *
+ * The Thread takes ownership of the event dispatcher and will delete it when
+ * the thread is destroyed.
+ */
+void Thread::setEventDispatcher(std::unique_ptr<EventDispatcher> dispatcher)
+{
+ if (data_->dispatcher_.load(std::memory_order_relaxed)) {
+ LOG(Thread, Warning) << "Event dispatcher is already set";
+ return;
+ }
+
+ data_->dispatcher_.store(dispatcher.release(),
+ std::memory_order_relaxed);
+}
+
+/**
+ * \brief Retrieve the event dispatcher
+ *
+ * This method retrieves the event dispatcher set with setEventDispatcher().
+ * If no dispatcher has been set, a default poll-based implementation is created
+ * and returned, and no custom event dispatcher may be installed anymore.
+ *
+ * The returned event dispatcher is valid until the thread is destroyed.
+ *
+ * \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);
+}
+
+}; /* namespace libcamera */