@@ -15,6 +15,7 @@
#include <libcamera/base/class.h>
#include <libcamera/base/mutex.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/thread_annotations.h>
@@ -30,7 +31,7 @@ class MediaDevice;
class PipelineHandler;
class PipelineHandlerFactoryBase;
-class CameraManager::Private : public Extensible::Private, public Thread
+class CameraManager::Private : public Extensible::Private, public Thread, public Object
{
LIBCAMERA_DECLARE_PUBLIC(CameraManager)
@@ -54,6 +55,7 @@ protected:
private:
int init();
std::vector<const PipelineHandlerFactoryBase *> pipelineFactories() const;
+ int startThread() LIBCAMERA_TSA_EXCLUDES(mutex_);
void createPipelineHandlers();
void pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory);
std::shared_ptr<PipelineHandler> findMatchingHandler(const MediaDevice *media);
@@ -72,6 +74,8 @@ private:
bool initialized_ LIBCAMERA_TSA_GUARDED_BY(mutex_);
int status_ LIBCAMERA_TSA_GUARDED_BY(mutex_);
+ bool started_ LIBCAMERA_TSA_GUARDED_BY(mutex_) = false;
+
std::unique_ptr<DeviceEnumerator> enumerator_;
/*
@@ -41,14 +41,45 @@ LOG_DEFINE_CATEGORY(Camera)
CameraManager::Private::Private()
: Thread("CameraManager"), initialized_(false)
{
+ /*
+ * Bind this Object to its own thread, so that work can be marshalled
+ * onto the camera manager thread with invokeMethod().
+ */
+ moveToThread(this);
}
int CameraManager::Private::start()
{
- int status;
+ return startThread();
+}
+
+/*
+ * Start the camera manager thread if not started yet, and wait for its
+ * initialization to complete. Returns the initialization status.
+ */
+int CameraManager::Private::startThread()
+{
+ bool start = false;
+
+ {
+ MutexLocker locker(mutex_);
- /* Start the thread and wait for initialization to complete. */
- Thread::start();
+ if (!started_) {
+ started_ = true;
+ initialized_ = false;
+ start = true;
+ }
+ }
+
+ if (start)
+ Thread::start();
+
+ /*
+ * Wait for initialization to complete, whether this call started the
+ * thread or another one did, as the caller may otherwise proceed before
+ * the thread is ready.
+ */
+ int status;
{
MutexLocker locker(mutex_);
@@ -78,7 +109,7 @@ void CameraManager::Private::run()
status_ = ret;
initialized_ = true;
mutex_.unlock();
- cv_.notify_one();
+ cv_.notify_all();
if (ret < 0) {
cleanup();
@@ -228,6 +259,11 @@ void CameraManager::Private::cleanup()
{
enumerator_->devicesAdded.disconnect(this);
+ {
+ MutexLocker locker(mutex_);
+ started_ = false;
+ }
+
/*
* Release all references to cameras to ensure they all get destroyed
* before the device enumerator deletes the media devices. Cameras are
The upcoming enumerate() and initialize() interfaces are called from the application thread, but the device enumerator, pipeline handlers and media devices they operate on are all bound to the camera manager thread, so their work must be marshalled onto it. CameraManager::Private is a Thread and not an Object, so it cannot be the target of Object::invokeMethod(). Derive it from Object as well and move it to its own thread on construction. Changes in future commits can then start the thread and invoke work on it regardless of the order in which they are called. notify_all() replaces notify_one() as more than one caller can now be waiting in startThread() for the same initialisation. Signed-off-by: Naushir Patuck <naush@raspberrypi.com> --- include/libcamera/internal/camera_manager.h | 6 ++- src/libcamera/camera_manager.cpp | 44 +++++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-)