diff options
-rw-r--r-- | src/libcamera/camera.cpp | 106 | ||||
-rw-r--r-- | src/libcamera/include/pipeline_handler.h | 4 |
2 files changed, 83 insertions, 27 deletions
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 802e7fd0..44f2d71b 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -7,6 +7,7 @@ #include <libcamera/camera.h> +#include <atomic> #include <iomanip> #include <libcamera/framebuffer_allocator.h> @@ -282,7 +283,7 @@ public: private: bool disconnected_; - State state_; + std::atomic<State> state_; }; Camera::Private::Private(PipelineHandler *pipe, const std::string &name, @@ -294,7 +295,7 @@ Camera::Private::Private(PipelineHandler *pipe, const std::string &name, Camera::Private::~Private() { - if (state_ != Private::CameraAvailable) + if (state_.load(std::memory_order_acquire) != Private::CameraAvailable) LOG(Camera, Error) << "Removing camera while still in use"; } @@ -310,12 +311,13 @@ int Camera::Private::isAccessAllowed(State state, bool allowDisconnected) const if (!allowDisconnected && disconnected_) return -ENODEV; - if (state_ == state) + State currentState = state_.load(std::memory_order_acquire); + if (currentState == state) return 0; ASSERT(static_cast<unsigned int>(state) < ARRAY_SIZE(camera_state_names)); - LOG(Camera, Debug) << "Camera in " << camera_state_names[state_] + LOG(Camera, Debug) << "Camera in " << camera_state_names[currentState] << " state trying operation requiring state " << camera_state_names[state]; @@ -328,13 +330,14 @@ int Camera::Private::isAccessAllowed(State low, State high, if (!allowDisconnected && disconnected_) return -ENODEV; - if (state_ >= low && state_ <= high) + State currentState = state_.load(std::memory_order_acquire); + if (currentState >= low && currentState <= high) return 0; ASSERT(static_cast<unsigned int>(low) < ARRAY_SIZE(camera_state_names) && static_cast<unsigned int>(high) < ARRAY_SIZE(camera_state_names)); - LOG(Camera, Debug) << "Camera in " << camera_state_names[state_] + LOG(Camera, Debug) << "Camera in " << camera_state_names[currentState] << " state trying operation requiring state between " << camera_state_names[low] << " and " << camera_state_names[high]; @@ -344,15 +347,20 @@ int Camera::Private::isAccessAllowed(State low, State high, void Camera::Private::disconnect() { - if (state_ == Private::CameraRunning) - state_ = Private::CameraConfigured; + /* + * If the camera was running when the hardware was removed force the + * state to Configured state to allow applications to free resources + * and call release() before deleting the camera. + */ + if (state_.load(std::memory_order_acquire) == Private::CameraRunning) + state_.store(Private::CameraConfigured, std::memory_order_release); disconnected_ = true; } void Camera::Private::setState(State state) { - state_ = state; + state_.store(state, std::memory_order_release); } /** @@ -384,12 +392,17 @@ void Camera::Private::setState(State state) * An application may start and stop a camera multiple times as long as it is * not released. The camera may also be reconfigured. * + * Functions that affect the camera state as defined below are generally not + * synchronized with each other by the Camera class. The caller is responsible + * for ensuring their synchronization if necessary. + * * \subsection Camera States * * To help manage the sequence of operations needed to control the camera a set * of states are defined. Each state describes which operations may be performed - * on the camera. Operations not listed in the state diagram are allowed in all - * states. + * on the camera. Performing an operation not allowed in the camera state + * results in undefined behaviour. Operations not listed at all in the state + * diagram are allowed in all states. * * \dot * digraph camera_state_machine { @@ -462,6 +475,7 @@ std::shared_ptr<Camera> Camera::create(PipelineHandler *pipe, /** * \brief Retrieve the name of the camera + * \context This function is \threadsafe. * \return Name of the camera device */ const std::string &Camera::name() const @@ -535,7 +549,9 @@ int Camera::exportFrameBuffers(Stream *stream, if (p_->activeStreams_.find(stream) == p_->activeStreams_.end()) return -EINVAL; - return p_->pipe_->exportFrameBuffers(this, stream, buffers); + return p_->pipe_->invokeMethod(&PipelineHandler::exportFrameBuffers, + ConnectionTypeBlocking, this, stream, + buffers); } int Camera::freeFrameBuffers(Stream *stream) @@ -544,7 +560,8 @@ int Camera::freeFrameBuffers(Stream *stream) if (ret < 0) return ret; - p_->pipe_->freeFrameBuffers(this, stream); + p_->pipe_->invokeMethod(&PipelineHandler::freeFrameBuffers, + ConnectionTypeBlocking, this, stream); return 0; } @@ -566,7 +583,8 @@ int Camera::freeFrameBuffers(Stream *stream) * Once exclusive access isn't needed anymore, the device should be released * with a call to the release() function. * - * This function affects the state of the camera, see \ref camera_operation. + * \context This function is \threadsafe. It may only be called when the camera + * is in the Available state as defined in \ref camera_operation. * * \return 0 on success or a negative error code otherwise * \retval -ENODEV The camera has been disconnected from the system @@ -574,6 +592,10 @@ int Camera::freeFrameBuffers(Stream *stream) */ int Camera::acquire() { + /* + * No manual locking is required as PipelineHandler::lock() is + * thread-safe. + */ int ret = p_->isAccessAllowed(Private::CameraAvailable); if (ret < 0) return ret == -EACCES ? -EBUSY : ret; @@ -595,7 +617,10 @@ int Camera::acquire() * Releasing the camera device allows other users to acquire exclusive access * with the acquire() function. * - * This function affects the state of the camera, see \ref camera_operation. + * \context This function may only be called when the camera is in the + * Available or Configured state as defined in \ref camera_operation, and shall + * be synchronized by the caller with other functions that affect the camera + * state. * * \return 0 on success or a negative error code otherwise * \retval -EBUSY The camera is running and can't be released @@ -629,6 +654,8 @@ int Camera::release() * * Camera controls remain constant through the lifetime of the camera. * + * \context This function is \threadsafe. + * * \return A ControlInfoMap listing the controls supported by the camera */ const ControlInfoMap &Camera::controls() @@ -643,6 +670,8 @@ const ControlInfoMap &Camera::controls() * information describes among other things how many streams the camera * supports and the capabilities of each stream. * + * \context This function is \threadsafe. + * * \return An array of all the camera's streams. */ const std::set<Stream *> &Camera::streams() const @@ -660,6 +689,8 @@ const std::set<Stream *> &Camera::streams() const * empty list of roles is valid, and will generate an empty configuration that * can be filled by the caller. * + * \context This function is \threadsafe. + * * \return A CameraConfiguration if the requested roles can be satisfied, or a * null pointer otherwise. The ownership of the returned configuration is * passed to the caller. @@ -710,7 +741,10 @@ std::unique_ptr<CameraConfiguration> Camera::generateConfiguration(const StreamR * Exclusive access to the camera shall be ensured by a call to acquire() prior * to calling this function, otherwise an -EACCES error will be returned. * - * This function affects the state of the camera, see \ref camera_operation. + * \context This function may only be called when the camera is in the Acquired + * or Configured state as defined in \ref camera_operation, and shall be + * synchronized by the caller with other functions that affect the camera + * state. * * Upon return the StreamConfiguration entries in \a config are associated with * Stream instances which can be retrieved with StreamConfiguration::stream(). @@ -749,7 +783,8 @@ int Camera::configure(CameraConfiguration *config) LOG(Camera, Info) << msg.str(); - ret = p_->pipe_->configure(this, config); + ret = p_->pipe_->invokeMethod(&PipelineHandler::configure, + ConnectionTypeBlocking, this, config); if (ret) return ret; @@ -784,8 +819,8 @@ int Camera::configure(CameraConfiguration *config) * The ownership of the returned request is passed to the caller, which is * responsible for either queueing the request or deleting it. * - * This function shall only be called when the camera is in the Configured - * or Running state, see \ref camera_operation. + * \context This function is \threadsafe. It may only be called when the camera + * is in the Configured or Running state as defined in \ref camera_operation. * * \return A pointer to the newly created request, or nullptr on error */ @@ -815,6 +850,9 @@ Request *Camera::createRequest(uint64_t cookie) * Ownership of the request is transferred to the camera. It will be deleted * automatically after it completes. * + * \context This function is \threadsafe. It may only be called when the camera + * is in the Running state as defined in \ref camera_operation. + * * \return 0 on success or a negative error code otherwise * \retval -ENODEV The camera has been disconnected from the system * \retval -EACCES The camera is not running so requests can't be queued @@ -827,6 +865,12 @@ int Camera::queueRequest(Request *request) if (ret < 0) return ret; + /* + * The camera state may chance until the end of the function. No locking + * is however needed as PipelineHandler::queueRequest() will handle + * this. + */ + if (request->buffers().empty()) { LOG(Camera, Error) << "Request contains no buffers"; return -EINVAL; @@ -841,7 +885,8 @@ int Camera::queueRequest(Request *request) } } - return p_->pipe_->queueRequest(this, request); + return p_->pipe_->invokeMethod(&PipelineHandler::queueRequest, + ConnectionTypeQueued, this, request); } /** @@ -851,7 +896,10 @@ int Camera::queueRequest(Request *request) * can queue requests to the camera to process and return to the application * until the capture session is terminated with \a stop(). * - * This function affects the state of the camera, see \ref camera_operation. + * \context This function may only be called when the camera is in the + * Configured state as defined in \ref camera_operation, and shall be + * synchronized by the caller with other functions that affect the camera + * state. * * \return 0 on success or a negative error code otherwise * \retval -ENODEV The camera has been disconnected from the system @@ -869,10 +917,12 @@ int Camera::start() if (allocator_ && !allocator_->buffers(stream).empty()) continue; - p_->pipe_->importFrameBuffers(this, stream); + p_->pipe_->invokeMethod(&PipelineHandler::importFrameBuffers, + ConnectionTypeDirect, this, stream); } - ret = p_->pipe_->start(this); + ret = p_->pipe_->invokeMethod(&PipelineHandler::start, + ConnectionTypeBlocking, this); if (ret) return ret; @@ -887,7 +937,9 @@ int Camera::start() * This method stops capturing and processing requests immediately. All pending * requests are cancelled and complete synchronously in an error state. * - * This function affects the state of the camera, see \ref camera_operation. + * \context This function may only be called when the camera is in the Running + * state as defined in \ref camera_operation, and shall be synchronized by the + * caller with other functions that affect the camera state. * * \return 0 on success or a negative error code otherwise * \retval -ENODEV The camera has been disconnected from the system @@ -903,13 +955,15 @@ int Camera::stop() p_->setState(Private::CameraConfigured); - p_->pipe_->stop(this); + p_->pipe_->invokeMethod(&PipelineHandler::stop, ConnectionTypeBlocking, + this); for (Stream *stream : p_->activeStreams_) { if (allocator_ && !allocator_->buffers(stream).empty()) continue; - p_->pipe_->freeFrameBuffers(this, stream); + p_->pipe_->invokeMethod(&PipelineHandler::freeFrameBuffers, + ConnectionTypeBlocking, this, stream); } return 0; diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index 97157dd7..4ed34511 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -17,6 +17,7 @@ #include <ipa/ipa_interface.h> #include <libcamera/controls.h> +#include <libcamera/object.h> #include <libcamera/stream.h> namespace libcamera { @@ -51,7 +52,8 @@ private: CameraData &operator=(const CameraData &) = delete; }; -class PipelineHandler : public std::enable_shared_from_this<PipelineHandler> +class PipelineHandler : public std::enable_shared_from_this<PipelineHandler>, + public Object { public: PipelineHandler(CameraManager *manager); |