[libcamera-devel] [PATCH 03/10] libcamera: Introduce Fence class

Umang Jain umang.jain at ideasonboard.com
Wed Nov 10 10:01:33 CET 2021


Hi Jacopo,

On 10/28/21 4:45 PM, Jacopo Mondi wrote:
> Fences are a synchronization mechanism that allows to receive event
> notifications of a file descriptor with an optional expiration timeout.
>
> Introduce the Fence class to model such a mechanism in libcamera.
>
> Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> ---
>   include/libcamera/internal/fence.h     |  64 +++++++++
>   include/libcamera/internal/meson.build |   1 +
>   src/libcamera/fence.cpp                | 185 +++++++++++++++++++++++++
>   src/libcamera/meson.build              |   1 +
>   4 files changed, 251 insertions(+)
>   create mode 100644 include/libcamera/internal/fence.h
>   create mode 100644 src/libcamera/fence.cpp
>
> diff --git a/include/libcamera/internal/fence.h b/include/libcamera/internal/fence.h
> new file mode 100644
> index 000000000000..5a78e0f864c7
> --- /dev/null
> +++ b/include/libcamera/internal/fence.h
> @@ -0,0 +1,64 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * internal/fence.h - Synchronization fence
> + */
> +#ifndef __LIBCAMERA_INTERNAL_FENCE_H__
> +#define __LIBCAMERA_INTERNAL_FENCE_H__
> +
> +#include <mutex>
> +
> +#include <libcamera/base/class.h>
> +#include <libcamera/base/event_notifier.h>
> +#include <libcamera/base/timer.h>
> +#include <libcamera/file_descriptor.h>
> +
> +namespace libcamera {
> +
> +class Fence
> +{
> +public:
> +	explicit Fence(int fenceFd, unsigned int timeoutMs = kFenceTimeout);
> +	Fence(Fence &&other);
> +
> +	Fence &operator=(Fence &&other);
> +
> +	int fd() const { return fd_.fd(); }
> +
> +	unsigned int timeout() const { return timeout_; }
> +	bool completed() const { return completed_; }
> +	bool expired() const { return expired_; }
> +
> +	void enable();
> +	void disable();
> +
> +	Signal<> complete;
> +
> +private:
> +	LIBCAMERA_DISABLE_COPY(Fence)
> +
> +	/* 300 milliseconds default timeout. */
> +	static constexpr unsigned int kFenceTimeout = 300;
> +
> +	void moveFence(Fence &other);
> +
> +	void activated();
> +	void timedout();
> +
> +	FileDescriptor fd_;
> +	EventNotifier notifier_;
> +
> +	Timer timer_;
> +	unsigned int timeout_;
> +
> +	bool completed_ = false;
> +	bool expired_ = false;
> +
> +	std::mutex mutex_;
> +};
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_INTERNAL_FENCE_H__ */
> +
> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
> index cae65b0604ff..32d5c3387de3 100644
> --- a/include/libcamera/internal/meson.build
> +++ b/include/libcamera/internal/meson.build
> @@ -22,6 +22,7 @@ libcamera_internal_headers = files([
>       'device_enumerator.h',
>       'device_enumerator_sysfs.h',
>       'device_enumerator_udev.h',
> +    'fence.h',
>       'formats.h',
>       'framebuffer.h',
>       'ipa_manager.h',
> diff --git a/src/libcamera/fence.cpp b/src/libcamera/fence.cpp
> new file mode 100644
> index 000000000000..8fadb2c21f03
> --- /dev/null
> +++ b/src/libcamera/fence.cpp
> @@ -0,0 +1,185 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * fence.cpp - Synchronization fence
> + */
> +
> +#include <libcamera/base/log.h>
> +#include <libcamera/base/thread.h>
> +#include <libcamera/internal/fence.h>
> +
> +namespace libcamera {
> +
> +/**
> + * \file internal/fence.h
> + * \brief Synchronization fence
> + */
> +
> +/**
> + * \class Fence
> + * \brief Synchronization fence class
> + *
> + * A Fence is a synchronization mechanism that allows to wait for a read event
> + * to happen on a file descriptor before an optional timeout expires.
> + *
> + * A Fence is created with a default timeout of 300 milliseconds, which can
> + * be overridden through the 'timeout' constructor parameter. Passing a 0
> + * timeout to the constructor disables timeouts completely.
> + *
> + * A Fence wraps a FileDescriptor and implements a move-only semantic, as Fence
> + * instances cannot be copied but only moved, causing the moved Fence to be
> + * reset by invalidating the wrapped FileDescriptor by disabling its
> + * notification and timeout mechanisms.
> + *
> + * When a read event is notified, the Fence is said to be 'completed',
> + * alternatively if the timeout expires before any event is notified the Fence
> + * is said to be 'expired'.
> + *
> + * In both cases, when an event notification happens or a timeout expires, the
> + * class emits the 'complete' signal, to which users of the class can connect
> + * to and check if the Fence has completed or has expired by calling the
> + * completed() and expired() functions.


If this is the case, I would expect a value emitted as a signal 
parameter, to know whether it's completed or expired

> + *
> + * Fence instances are non-active by default (ie no events or timeout are
> + * generated) and need to be enabled by calling the enable() function. Likewise


Its inactive both when "constructed" and "moved" right ? It seems so, as 
per my reading below

> + * a Fence can be disabled by calling the disable() function.
> + *
> + * After a Fence has expired no events are generated and the Fence need to be
> + * manually re-enabled. Likewise, if the Fence is signalled the expiration
> + * timeout is cancelled.
> + */
> +
> +/**
> + * \brief Create a synchronization fence
> + * \param[in] fenceFd The synchronization fence file descriptor
> + * \param[in] timeoutMs The optional fence timeout. Set to 0 to disable timeout
> + */
> +Fence::Fence(int fenceFd, unsigned int timeoutMs)
> +	: fd_(std::move(fenceFd)),
> +	  notifier_(fd_.fd(), EventNotifier::Read, nullptr, false),
> +	  timeout_(timeoutMs)
> +{
> +	notifier_.activated.connect(this, &Fence::activated);
> +
> +	timer_.timeout.connect(this, &Fence::timedout);
> +}
> +
> +void Fence::moveFence(Fence &other)
> +{
> +	fd_ = std::move(other.fd_);
> +
> +	other.disable();
> +	if (other.timer_.isRunning())
> +		other.timer_.stop();
> +	other.timeout_ = 0;
> +}
> +
> +/**
> + * \brief Move-construct a Fence
> + * \param[in] other The other fence to move
> + */
> +Fence::Fence(Fence &&other)
> +	: notifier_(other.fd(), EventNotifier::Read, nullptr, false)
> +{
> +	moveFence(other);
> +
> +	notifier_.activated.connect(this, &Fence::activated);
> +	timer_.timeout.connect(this, &Fence::timedout);


I think Kieran has rightly pointed out in his replies to disconnect 
these from the 'other' Fence too.

> +}
> +
> +/**
> + * \brief Move-assign the value of the \a other fence to this
> + * \param[in] other The other fence to move
> + * \return This fence
> + */
> +Fence &Fence::operator=(Fence &&other)
> +{
> +	moveFence(other);
> +
> +	notifier_ = EventNotifier(fd_.fd(), EventNotifier::Read, nullptr, false);
> +
> +	return *this;
> +}


One fundamental question I am pondering is why a Fence needs to be moved 
after constructed. Hmm. I think I have a broad idea, that it happens 
while the iteration between FrameBuffer and Request class in subsequent 
patches, I'll try to keep on reading for exact answers.

> +
> +/**
> + * \fn Fence::fd() const
> + * \brief Return the synchronization fence file descriptor
> + * \return The synchronization fence file descriptor
> + */
> +
> +/**
> + * \fn Fence::timeout()
> + * \brief Retrieve the fence timeout
> + * \return The fence timeout in milliseconds
> + */
> +
> +/**
> + * \fn Fence::completed()
> + * \brief Check if the fence has completed before timing out
> + * \return True if the fence has completed
> + */
> +
> +/**
> + * \fn Fence::expired()
> + * \brief Check if the fence has expired before completing
> + * \return True if the fence has expired
> + */
> +
> +/**
> + * \brief Enable a fence by enabling events notifications
> + */
> +void Fence::enable()
> +{
> +	MutexLocker lock(mutex_);
> +
> +	expired_ = false;
> +	completed_ = false;
> +
> +	notifier_.setEnabled(true);
> +	if (timeout_)
> +		timer_.start(timeout_);
> +}
> +
> +/**
> + * \brief Disable a fence by disabling events notifications
> + */
> +void Fence::disable()
> +{
> +	notifier_.setEnabled(false);
> +}
> +
> +/**
> + * \var Fence::complete
> + * \brief The fence completion signal
> + *
> + * A Fence is completed if signalled or timeout.
> + */

Yep, I think it can emit with a value denoting whether it's a timeout or 
activated

> +
> +void Fence::activated()
> +{
> +	MutexLocker lock(mutex_);
> +
> +	if (timer_.isRunning())
> +		timer_.stop();
> +
> +	completed_ = true;
> +	expired_ = false;
> +
> +	complete.emit();
> +}
> +
> +void Fence::timedout()
> +{
> +	MutexLocker lock(mutex_);
> +
> +	expired_ = true;
> +	completed_ = false;
> +
> +	/* Disable events notification after timeout. */
> +	disable();
> +
> +	complete.emit();
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 6727a777d804..6fb0d5f49b63 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -14,6 +14,7 @@ libcamera_sources = files([
>       'delayed_controls.cpp',
>       'device_enumerator.cpp',
>       'device_enumerator_sysfs.cpp',
> +    'fence.cpp',
>       'file_descriptor.cpp',
>       'formats.cpp',
>       'framebuffer.cpp',


More information about the libcamera-devel mailing list