[libcamera-devel] [PATCH v2 08/12] libcamera: request: Add Request::Private::prepare()
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Sun Nov 21 22:39:22 CET 2021
Hi Jacopo,
Thank you for the patch.
On Sat, Nov 20, 2021 at 12:13:09PM +0100, Jacopo Mondi wrote:
> Add a prepare() function to the Private Request representation.
>
> The prepare() function is used by the PipelineHandler class to
> prepare a Request to be queued to the hardware.
>
> The current implementation of prepare() handles the fences associated
> with the Framebuffers part of a Request. The function starts an event
> notifier for each of those and notifies the Request as Ready to be queued
> once all the fences have been signalled.
>
> An optional timeout allows to interrupt blocked waits and notify the
> Request as failed.
>
> Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> ---
> include/libcamera/internal/request.h | 21 ++++
> src/libcamera/request.cpp | 150 +++++++++++++++++++++++++++
> 2 files changed, 171 insertions(+)
>
> diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h
> index 59bddde3a090..26b25fb12261 100644
> --- a/include/libcamera/internal/request.h
> +++ b/include/libcamera/internal/request.h
> @@ -7,10 +7,16 @@
> #ifndef __LIBCAMERA_INTERNAL_REQUEST_H__
> #define __LIBCAMERA_INTERNAL_REQUEST_H__
>
> +#include <chrono>
> #include <memory>
>
> +#include <libcamera/base/event_notifier.h>
> +#include <libcamera/base/timer.h>
> +
> #include <libcamera/request.h>
>
> +using namespace std::chrono_literals;
> +
> namespace libcamera {
>
> class Camera;
> @@ -21,6 +27,12 @@ class Request::Private : public Extensible::Private
> LIBCAMERA_DECLARE_PUBLIC(Request)
>
> public:
> + enum class Status {
> + Pending,
> + Ready,
> + Failed
> + };
> +
> Private(Camera *camera);
> ~Private();
>
> @@ -29,21 +41,30 @@ public:
>
> uint64_t cookie() const;
> Request::Status status() const;
> + Status privateStatus() const { return status_; }
As mentioned in the cover letter, status and privateStatus isn't nice
indeed. I think one option to remove the privateStatus() could be to
call cancel() in Request::Private::timeout(), and then use
Request::status() in PipelineHandler::doQueueRequests(). There could be
other options too.
>
> bool completeBuffer(FrameBuffer *buffer);
> void complete();
> void cancel();
> void reuse();
>
> + Status prepare(std::chrono::milliseconds timeout = 0ms);
> + Signal<> prepared;
> +
> uint32_t sequence_ = 0;
>
> private:
> void _cancel();
> + void notifierActivated(const std::unique_ptr<EventNotifier> ¬ifier);
> + void timeout();
>
> Camera *camera_;
> bool cancelled_;
> + Status status_ = Status::Pending;
>
> std::unordered_set<FrameBuffer *> pending_;
> + std::unordered_set<std::unique_ptr<EventNotifier>> notifiers_;
> + std::unique_ptr<Timer> timer_;
> };
>
> } /* namespace libcamera */
> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> index 1d47698a6263..98f9719e5cf2 100644
> --- a/src/libcamera/request.cpp
> +++ b/src/libcamera/request.cpp
> @@ -43,6 +43,22 @@ LOG_DEFINE_CATEGORY(Request)
> * subclasses).
> */
>
> +/**
> + * \enum Request::Private::Status
> + * \brief Request private status
> + *
> + * The Request private status describes the lifecycle of the Request between
> + * the time it is queued to the Camera and the time it is queued to the device.
> + *
> + * A Request is created in Status::Pending state. Before actually queueing the
> + * Request the PipelineHandler base class prepare() the Request, whose status
> + * transitions to either Status::Ready or Status::Failed.
> + *
> + * \var Request::Private::Status::Pending
> + * \var Request::Private::Status::Ready
> + * \var Request::Private::Status::Failed
> + */
> +
> /**
> * \brief Create a Request::Private
> * \param camera The Camera that creates the request
> @@ -95,6 +111,17 @@ Request::Status Request::Private::status() const
> return _o<Request>()->status();
> }
>
> +/**
> + * \fn Request::Private::privateStatus()
> + * \brief Retrieve the Request private status
> + *
> + * The private status, as described by the Request::Private:Status enumeration,
> + * describes the Request status between the time it is queued to the Camera and
> + * the time the Request is applied to the hardware.
> + *
> + * \return The Request private state
> + */
> +
> /**
> * \brief Complete a buffer for the request
> * \param[in] buffer The buffer that has completed
> @@ -155,6 +182,8 @@ void Request::Private::_cancel()
>
> cancelled_ = true;
> pending_.clear();
> + notifiers_.clear();
> + timer_.reset();
> }
>
> /**
> @@ -182,8 +211,84 @@ void Request::Private::reuse()
> {
> sequence_ = 0;
> cancelled_ = false;
> + status_ = Status::Pending;
> pending_.clear();
> + notifiers_.clear();
> + timer_.reset();
> +}
> +
> +/**
> + * \brief Prepare the Request to be queued to the device
> + * \param[in] timeout Optional expiration timeout
> + *
> + * Prepare a Request to be queued to the hardware device it by ensuring it is
s/it by/by/
> + * ready for the incoming memory transfers.
> + *
> + * This currently means waiting on each frame buffer acquire fence to be
> + * signalled. An optional expiration timeout can be specified. If not all the
> + * fences have been signalled correctly before the timeout expires the Request
> + * is marked as Failed, otherwise it is set to the Ready state.
> + *
> + * \sa Request::Private::Status
> + *
> + * The function returns Status::Ready if all the prepare operations have been
> + * completed synchronously. If Status::Ready is returned the Request can be
> + * queued immediately and the prepared signal is not emitted. If instead the
> + * prepare operation requires to wait the completion of asynchronous events,
> + * such as fence notifications or timer expiration this function returns
> + * Status::Pending and the asynchronous event completion is notified by emitting
> + * the prepared signal.
> + *
> + * As we currently only handle fences, the function return Status::Ready if
> + * there are no fences to wait on. Status::Prepared is otherwise returned and
> + * the prepared signal is emitted when all fences have been signalled or the
> + * optional timeout has expired.
> + *
> + * The intended user of this function is the PipelineHandler base class, which
> + * 'prepares' a Request before queuing it to the hardware device.
> + *
> + * \return The Request status
> + */
> +Request::Private::Status Request::Private::prepare(std::chrono::milliseconds timeout)
> +{
> + FrameBuffer::Private *bufferData;
> +
> + /* Create and connect one notifier for each synchronization fence. */
> + for (FrameBuffer *buffer : pending_) {
> + bufferData = buffer->_d();
> +
> + if (!bufferData->fence())
> + continue;
> +
> + int fenceFd = bufferData->fence()->fd().fd();
> + notifiers_.emplace(new EventNotifier(fenceFd, EventNotifier::Read));
There's no bug in your code, but usage of new() indicates potential
memory leaks, so it's best to minimize its usage. I'd write
notifiers_.insert(std::make_unique<EventNotifier>(fenceFd, EventNotifier::Read));
> + }
> +
> + if (notifiers_.empty()) {
> + status_ = Status::Ready;
> + return status_;
> + }
> +
> + for (const std::unique_ptr<EventNotifier> ¬ifier : notifiers_)
> + notifier->activated.connect(this, [this, ¬ifier] {
> + notifierActivated(notifier);
> + });
> +
> + /* In case a timeout is specified, create a timer and set it up. */
Let's add
* The timer must be created here instead of in the Request constructor,
* in order to be bound to the pipeline handler thread.
> + if (timeout != 0ms) {
> + timer_ = std::make_unique<Timer>();
> + timer_->timeout.connect(this, &Request::Private::timeout);
> + timer_->start(timeout);
> + }
> +
> + return Status::Pending;
> }
> +
> +/**
> + * \var Request::Private::prepared
> + * \brief Request preparation completed Signal
> + */
> +
> /**
> * \var Request::Private::sequence_
> * \brief The request sequence number
> @@ -191,6 +296,51 @@ void Request::Private::reuse()
> * \copydoc Request::sequence()
> */
>
> +void Request::Private::notifierActivated(const std::unique_ptr<EventNotifier> ¬ifier)
> +{
> + auto it = notifiers_.find(notifier);
> + ASSERT(it != notifiers_.end());
> +
> + /* We need to close the fence if successfully signalled. */
> + int fd = notifier->fd();
> + bool found = false;
> + for (FrameBuffer *buffer : pending_) {
> + FrameBuffer::Private *bufferData = buffer->_d();
> +
> + if (!bufferData->fence())
> + continue;
> +
> + if (bufferData->fence()->fd().fd() != fd)
> + continue;
If you turned notifiers_ into a map indexed by FrameBuffer *, you could
pass the FrameBuffer pointer instead of the notifier to the
notifierActivated() function, and avoid this loop.
> +
> + bufferData->closeFence();
> + found = true;
> + break;
> + }
> + ASSERT(found);
> +
> + notifiers_.erase(it);
> + if (!notifiers_.empty())
> + return;
> +
> + /* All fences completed, delete the timer and move to state Ready. */
> + timer_.reset();
> + status_ = Status::Ready;
> + prepared.emit();
> +}
> +
> +void Request::Private::timeout()
> +{
> + /* A timeout can only happen if there are fences not yet signalled. */
> + ASSERT(!notifiers_.empty());
> + notifiers_.clear();
> +
> + LOG(Request, Error) << "Request prepare timeout";
Could you print the cookie to ease debugging ?
> +
> + status_ = Status::Failed;
> + prepared.emit();
> +}
> +
> /**
> * \enum Request::Status
> * Request completion status
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list