[libcamera-devel] libcamera: camera: Make stop() idempotent
diff mbox series

Message ID 20210423142412.460460-1-nfraprado@collabora.com
State Superseded
Headers show
Series
  • [libcamera-devel] libcamera: camera: Make stop() idempotent
Related show

Commit Message

Nícolas F. R. A. Prado April 23, 2021, 2:24 p.m. UTC
Make Camera::stop() idempotent so that it can be called in any state and
consecutive times. When called in any state other than CameraRunning, it
is a no-op. This simplifies the cleanup path for applications.

Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
---
This will be used to simplify the cleanup path in lc-compliance without having
error messages [1].

Also, I'm not sure if I should add the silent parameter to the other
isAccessAllowed variant as well. It would make sense for consistency's sake, but
it's not needed currently.

[1] https://lists.libcamera.org/pipermail/libcamera-devel/2021-April/019703.html

 src/libcamera/camera.cpp | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

Comments

Kieran Bingham April 23, 2021, 3:10 p.m. UTC | #1
Hi Nicolas,

On 23/04/2021 15:24, Nícolas F. R. A. Prado wrote:
> Make Camera::stop() idempotent so that it can be called in any state and
> consecutive times. When called in any state other than CameraRunning, it
> is a no-op. This simplifies the cleanup path for applications.
> 
> Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> ---
> This will be used to simplify the cleanup path in lc-compliance without having
> error messages [1].
> 
> Also, I'm not sure if I should add the silent parameter to the other
> isAccessAllowed variant as well. It would make sense for consistency's sake, but
> it's not needed currently.
> 
> [1] https://lists.libcamera.org/pipermail/libcamera-devel/2021-April/019703.html
> 
>  src/libcamera/camera.cpp | 29 ++++++++++++++++++-----------
>  1 file changed, 18 insertions(+), 11 deletions(-)
> 
> diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> index 763f3b9926fd..baffdafc8146 100644
> --- a/src/libcamera/camera.cpp
> +++ b/src/libcamera/camera.cpp
> @@ -350,7 +350,7 @@ public:
>  	int isAccessAllowed(State state, bool allowDisconnected = false,
>  			    const char *from = __builtin_FUNCTION()) const;
>  	int isAccessAllowed(State low, State high,
> -			    bool allowDisconnected = false,
> +			    bool allowDisconnected = false, bool silent = false,
>  			    const char *from = __builtin_FUNCTION()) const;
>  
>  	void disconnect();
> @@ -408,7 +408,7 @@ int Camera::Private::isAccessAllowed(State state, bool allowDisconnected,
>  }
>  
>  int Camera::Private::isAccessAllowed(State low, State high,
> -				     bool allowDisconnected,
> +				     bool allowDisconnected, bool silent,
>  				     const char *from) const
>  {
>  	if (!allowDisconnected && disconnected_)
> @@ -421,11 +421,12 @@ int Camera::Private::isAccessAllowed(State low, State high,
>  	ASSERT(static_cast<unsigned int>(low) < std::size(camera_state_names) &&
>  	       static_cast<unsigned int>(high) < std::size(camera_state_names));
>  
> -	LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
> -			   << " state trying " << from
> -			   << "() requiring state between "
> -			   << camera_state_names[low] << " and "
> -			   << camera_state_names[high];
> +	if (!silent)
> +		LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
> +				   << " state trying " << from
> +				   << "() requiring state between "
> +				   << camera_state_names[low] << " and "
> +				   << camera_state_names[high];
>  
>  	return -EACCES;
>  }
> @@ -1061,9 +1062,10 @@ int Camera::start(const ControlList *controls)
>   * This method stops capturing and processing requests immediately. All pending
>   * requests are cancelled and complete synchronously in an error state.
>   *
> - * \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.
> + * \context This function may be called even if the camera isn't in the Running
> + * state as defined in \ref camera_operation, in which cases it is a no-op. It
> + * 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
> @@ -1073,7 +1075,12 @@ int Camera::stop()
>  {
>  	Private *const d = LIBCAMERA_D_PTR();
>  
> -	int ret = d->isAccessAllowed(Private::CameraRunning);
> +	int ret = d->isAccessAllowed(Private::CameraAvailable,
> +			Private::CameraStopping, false, true);
> +	if (!ret)
> +		return 0;

This seems like a lot of complexity to add to be able to return out of
this function silently if the state is not running.

Effectively we're saying that this function 'Is allowed' in any state,
but it simply returns early if state < CameraRunning. That makes me
think using a function called 'isAccessAllowed()' isn't quite right -
It's always allowed.

Would it be better to provide an accessor on the private state to better
represent that?

Like isRunning()?

Then this simply becomes

	if (!d->isRunning())
		return 0;




> +
> +	ret = d->isAccessAllowed(Private::CameraRunning);
>  	if (ret < 0)
>  		return ret;
>  
>
Nícolas F. R. A. Prado April 23, 2021, 4:29 p.m. UTC | #2
Em 2021-04-23 12:10, Kieran Bingham escreveu:

> Hi Nicolas,
>
> On 23/04/2021 15:24, Nícolas F. R. A. Prado wrote:
> > Make Camera::stop() idempotent so that it can be called in any state and
> > consecutive times. When called in any state other than CameraRunning, it
> > is a no-op. This simplifies the cleanup path for applications.
> > 
> > Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> > ---
> > This will be used to simplify the cleanup path in lc-compliance without having
> > error messages [1].
> > 
> > Also, I'm not sure if I should add the silent parameter to the other
> > isAccessAllowed variant as well. It would make sense for consistency's sake, but
> > it's not needed currently.
> > 
> > [1] https://lists.libcamera.org/pipermail/libcamera-devel/2021-April/019703.html
> > 
> >  src/libcamera/camera.cpp | 29 ++++++++++++++++++-----------
> >  1 file changed, 18 insertions(+), 11 deletions(-)
> > 
> > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > index 763f3b9926fd..baffdafc8146 100644
> > --- a/src/libcamera/camera.cpp
> > +++ b/src/libcamera/camera.cpp
> > @@ -350,7 +350,7 @@ public:
> >  	int isAccessAllowed(State state, bool allowDisconnected = false,
> >  			    const char *from = __builtin_FUNCTION()) const;
> >  	int isAccessAllowed(State low, State high,
> > -			    bool allowDisconnected = false,
> > +			    bool allowDisconnected = false, bool silent = false,
> >  			    const char *from = __builtin_FUNCTION()) const;
> >  
> >  	void disconnect();
> > @@ -408,7 +408,7 @@ int Camera::Private::isAccessAllowed(State state, bool allowDisconnected,
> >  }
> >  
> >  int Camera::Private::isAccessAllowed(State low, State high,
> > -				     bool allowDisconnected,
> > +				     bool allowDisconnected, bool silent,
> >  				     const char *from) const
> >  {
> >  	if (!allowDisconnected && disconnected_)
> > @@ -421,11 +421,12 @@ int Camera::Private::isAccessAllowed(State low, State high,
> >  	ASSERT(static_cast<unsigned int>(low) < std::size(camera_state_names) &&
> >  	       static_cast<unsigned int>(high) < std::size(camera_state_names));
> >  
> > -	LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
> > -			   << " state trying " << from
> > -			   << "() requiring state between "
> > -			   << camera_state_names[low] << " and "
> > -			   << camera_state_names[high];
> > +	if (!silent)
> > +		LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
> > +				   << " state trying " << from
> > +				   << "() requiring state between "
> > +				   << camera_state_names[low] << " and "
> > +				   << camera_state_names[high];
> >  
> >  	return -EACCES;
> >  }
> > @@ -1061,9 +1062,10 @@ int Camera::start(const ControlList *controls)
> >   * This method stops capturing and processing requests immediately. All pending
> >   * requests are cancelled and complete synchronously in an error state.
> >   *
> > - * \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.
> > + * \context This function may be called even if the camera isn't in the Running
> > + * state as defined in \ref camera_operation, in which cases it is a no-op. It
> > + * 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
> > @@ -1073,7 +1075,12 @@ int Camera::stop()
> >  {
> >  	Private *const d = LIBCAMERA_D_PTR();
> >  
> > -	int ret = d->isAccessAllowed(Private::CameraRunning);
> > +	int ret = d->isAccessAllowed(Private::CameraAvailable,
> > +			Private::CameraStopping, false, true);
> > +	if (!ret)
> > +		return 0;
>
> This seems like a lot of complexity to add to be able to return out of
> this function silently if the state is not running.
>
> Effectively we're saying that this function 'Is allowed' in any state,
> but it simply returns early if state < CameraRunning. That makes me
> think using a function called 'isAccessAllowed()' isn't quite right -
> It's always allowed.
>
> Would it be better to provide an accessor on the private state to better
> represent that?
>
> Like isRunning()?
>
> Then this simply becomes
>
> if (!d->isRunning())
> return 0;

Yep, that sounds a lot better. I'll do that instead for v2.

Thanks,
Nícolas
Laurent Pinchart April 25, 2021, 8:21 p.m. UTC | #3
Hi Nícolas,

On Fri, Apr 23, 2021 at 01:29:47PM -0300, Nícolas F. R. A. Prado wrote:
> Em 2021-04-23 12:10, Kieran Bingham escreveu:
> > On 23/04/2021 15:24, Nícolas F. R. A. Prado wrote:
> > > Make Camera::stop() idempotent so that it can be called in any state and
> > > consecutive times. When called in any state other than CameraRunning, it
> > > is a no-op. This simplifies the cleanup path for applications.
> > > 
> > > Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> > > ---
> > > This will be used to simplify the cleanup path in lc-compliance without having
> > > error messages [1].
> > > 
> > > Also, I'm not sure if I should add the silent parameter to the other
> > > isAccessAllowed variant as well. It would make sense for consistency's sake, but
> > > it's not needed currently.
> > > 
> > > [1] https://lists.libcamera.org/pipermail/libcamera-devel/2021-April/019703.html
> > > 
> > >  src/libcamera/camera.cpp | 29 ++++++++++++++++++-----------
> > >  1 file changed, 18 insertions(+), 11 deletions(-)
> > > 
> > > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > > index 763f3b9926fd..baffdafc8146 100644
> > > --- a/src/libcamera/camera.cpp
> > > +++ b/src/libcamera/camera.cpp
> > > @@ -350,7 +350,7 @@ public:
> > >  	int isAccessAllowed(State state, bool allowDisconnected = false,
> > >  			    const char *from = __builtin_FUNCTION()) const;
> > >  	int isAccessAllowed(State low, State high,
> > > -			    bool allowDisconnected = false,
> > > +			    bool allowDisconnected = false, bool silent = false,

I know this will change in v2, but on a general note, bool parameters
are not great (this applies to the existing allowDisconnected parameter
too). When reading the caller

	int ret = d->isAccessAllowed(Private::CameraAvailable,
			Private::CameraStopping, false, true);

it's hard to tell what false and true refer to. Instead, the following
would be better:

	int ret = d->isAccessAllowed(Private::CameraAvailable,
				     Private::CameraStopping,
				     Private::AccessCheck::NoLogging);

with a new

	enum AccessCbeck {
		AllowDisconnected = 0x1,
		NoLogging = 0x2,
	};

in the Private class. An enum class would be even better, but that
wouldn't allow us to | multiple flags.
https://patchwork.libcamera.org/cover/8991/ could be a solution.

> > >  			    const char *from = __builtin_FUNCTION()) const;
> > >  
> > >  	void disconnect();
> > > @@ -408,7 +408,7 @@ int Camera::Private::isAccessAllowed(State state, bool allowDisconnected,
> > >  }
> > >  
> > >  int Camera::Private::isAccessAllowed(State low, State high,
> > > -				     bool allowDisconnected,
> > > +				     bool allowDisconnected, bool silent,
> > >  				     const char *from) const
> > >  {
> > >  	if (!allowDisconnected && disconnected_)
> > > @@ -421,11 +421,12 @@ int Camera::Private::isAccessAllowed(State low, State high,
> > >  	ASSERT(static_cast<unsigned int>(low) < std::size(camera_state_names) &&
> > >  	       static_cast<unsigned int>(high) < std::size(camera_state_names));
> > >  
> > > -	LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
> > > -			   << " state trying " << from
> > > -			   << "() requiring state between "
> > > -			   << camera_state_names[low] << " and "
> > > -			   << camera_state_names[high];
> > > +	if (!silent)
> > > +		LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
> > > +				   << " state trying " << from
> > > +				   << "() requiring state between "
> > > +				   << camera_state_names[low] << " and "
> > > +				   << camera_state_names[high];
> > >  
> > >  	return -EACCES;
> > >  }
> > > @@ -1061,9 +1062,10 @@ int Camera::start(const ControlList *controls)
> > >   * This method stops capturing and processing requests immediately. All pending
> > >   * requests are cancelled and complete synchronously in an error state.
> > >   *
> > > - * \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.
> > > + * \context This function may be called even if the camera isn't in the Running
> > > + * state as defined in \ref camera_operation, in which cases it is a no-op. It
> > > + * shall be synchronized by the caller with other functions that affect the
> > > + * camera state.

How about stating that the function may be called in any camera state ?

> > >   *
> > >   * \return 0 on success or a negative error code otherwise
> > >   * \retval -ENODEV The camera has been disconnected from the system
> > > @@ -1073,7 +1075,12 @@ int Camera::stop()
> > >  {
> > >  	Private *const d = LIBCAMERA_D_PTR();
> > >  
> > > -	int ret = d->isAccessAllowed(Private::CameraRunning);
> > > +	int ret = d->isAccessAllowed(Private::CameraAvailable,
> > > +			Private::CameraStopping, false, true);
> > > +	if (!ret)
> > > +		return 0;
> >
> > This seems like a lot of complexity to add to be able to return out of
> > this function silently if the state is not running.
> >
> > Effectively we're saying that this function 'Is allowed' in any state,
> > but it simply returns early if state < CameraRunning. That makes me
> > think using a function called 'isAccessAllowed()' isn't quite right -
> > It's always allowed.
> >
> > Would it be better to provide an accessor on the private state to better
> > represent that?
> >
> > Like isRunning()?
> >
> > Then this simply becomes
> >
> > if (!d->isRunning())
> > return 0;
> 
> Yep, that sounds a lot better. I'll do that instead for v2.

Patch
diff mbox series

diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index 763f3b9926fd..baffdafc8146 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -350,7 +350,7 @@  public:
 	int isAccessAllowed(State state, bool allowDisconnected = false,
 			    const char *from = __builtin_FUNCTION()) const;
 	int isAccessAllowed(State low, State high,
-			    bool allowDisconnected = false,
+			    bool allowDisconnected = false, bool silent = false,
 			    const char *from = __builtin_FUNCTION()) const;
 
 	void disconnect();
@@ -408,7 +408,7 @@  int Camera::Private::isAccessAllowed(State state, bool allowDisconnected,
 }
 
 int Camera::Private::isAccessAllowed(State low, State high,
-				     bool allowDisconnected,
+				     bool allowDisconnected, bool silent,
 				     const char *from) const
 {
 	if (!allowDisconnected && disconnected_)
@@ -421,11 +421,12 @@  int Camera::Private::isAccessAllowed(State low, State high,
 	ASSERT(static_cast<unsigned int>(low) < std::size(camera_state_names) &&
 	       static_cast<unsigned int>(high) < std::size(camera_state_names));
 
-	LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
-			   << " state trying " << from
-			   << "() requiring state between "
-			   << camera_state_names[low] << " and "
-			   << camera_state_names[high];
+	if (!silent)
+		LOG(Camera, Error) << "Camera in " << camera_state_names[currentState]
+				   << " state trying " << from
+				   << "() requiring state between "
+				   << camera_state_names[low] << " and "
+				   << camera_state_names[high];
 
 	return -EACCES;
 }
@@ -1061,9 +1062,10 @@  int Camera::start(const ControlList *controls)
  * This method stops capturing and processing requests immediately. All pending
  * requests are cancelled and complete synchronously in an error state.
  *
- * \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.
+ * \context This function may be called even if the camera isn't in the Running
+ * state as defined in \ref camera_operation, in which cases it is a no-op. It
+ * 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
@@ -1073,7 +1075,12 @@  int Camera::stop()
 {
 	Private *const d = LIBCAMERA_D_PTR();
 
-	int ret = d->isAccessAllowed(Private::CameraRunning);
+	int ret = d->isAccessAllowed(Private::CameraAvailable,
+			Private::CameraStopping, false, true);
+	if (!ret)
+		return 0;
+
+	ret = d->isAccessAllowed(Private::CameraRunning);
 	if (ret < 0)
 		return ret;