[libcamera-devel,6/9] libcamera: timer: Forbid starting or stopping timer from another thread

Message ID 20191006053226.8976-7-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • Timer fixes and enhancements
Related show

Commit Message

Laurent Pinchart Oct. 6, 2019, 5:32 a.m. UTC
Starting or stopping a timer from a different thread than the one it
belongs to is inherently racy. Disallow it.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/libcamera/timer.cpp | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

Comments

Jacopo Mondi Oct. 6, 2019, 7:31 p.m. UTC | #1
Hi Laurent,

On Sun, Oct 06, 2019 at 08:32:23AM +0300, Laurent Pinchart wrote:
> Starting or stopping a timer from a different thread than the one it
> belongs to is inherently racy. Disallow it.
>

Looks good
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

Thanks
   j

> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  src/libcamera/timer.cpp | 24 +++++++++++++++++++++---
>  1 file changed, 21 insertions(+), 3 deletions(-)
>
> diff --git a/src/libcamera/timer.cpp b/src/libcamera/timer.cpp
> index 5d4e52713e6e..8749d66c8662 100644
> --- a/src/libcamera/timer.cpp
> +++ b/src/libcamera/timer.cpp
> @@ -36,6 +36,11 @@ LOG_DEFINE_CATEGORY(Timer)
>   * Once started the timer will run until it times out. It can be stopped with
>   * stop(), and once it times out or is stopped, can be started again with
>   * start().
> + *
> + * Timers run in the thread they belong to, and thus emit the \a ref timeout
> + * signal from that thread. To avoid race conditions they must not be started
> + * or stopped from a different thread, attempts to do so will be rejected and
> + * logged, and may cause undefined behaviour.
>   */
>
>  /**
> @@ -57,17 +62,24 @@ Timer::~Timer()
>   * \brief Start or restart the timer with a timeout of \a msec
>   * \param[in] msec The timer duration in milliseconds
>   *
> - * If the timer is already running it will be stopped and restarted.
> + * This method shall be called from the thread the timer is associated with. If
> + * the timer is already running it will be stopped and restarted.
>   */
>
>  /**
>   * \brief Start or restart the timer with a timeout of \a duration
>   * \param[in] duration The timer duration in milliseconds
>   *
> - * If the timer is already running it will be stopped and restarted.
> + * This method shall be called from the thread the timer is associated with. If
> + * the timer is already running it will be stopped and restarted.
>   */
>  void Timer::start(std::chrono::milliseconds duration)
>  {
> +	if (Thread::current() != thread()) {
> +		LOG(Timer, Error) << "Timer can't be started from another thread";
> +		return;
> +	}
> +
>  	deadline_ = utils::clock::now() + duration;
>
>  	LOG(Timer, Debug)
> @@ -87,13 +99,19 @@ void Timer::start(std::chrono::milliseconds duration)
>   * After this function returns the timer is guaranteed not to emit the
>   * \ref timeout signal.
>   *
> - * If the timer is not running this function performs no operation.
> + * This method shall be called from the thread the timer is associated with. If
> + * the timer is not running this function performs no operation.
>   */
>  void Timer::stop()
>  {
>  	if (!isRunning())
>  		return;
>
> +	if (Thread::current() != thread()) {
> +		LOG(Timer, Error) << "Timer can't be stopped from another thread";
> +		return;
> +	}
> +
>  	unregisterTimer();
>  }
>
> --
> Regards,
>
> Laurent Pinchart
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/src/libcamera/timer.cpp b/src/libcamera/timer.cpp
index 5d4e52713e6e..8749d66c8662 100644
--- a/src/libcamera/timer.cpp
+++ b/src/libcamera/timer.cpp
@@ -36,6 +36,11 @@  LOG_DEFINE_CATEGORY(Timer)
  * Once started the timer will run until it times out. It can be stopped with
  * stop(), and once it times out or is stopped, can be started again with
  * start().
+ *
+ * Timers run in the thread they belong to, and thus emit the \a ref timeout
+ * signal from that thread. To avoid race conditions they must not be started
+ * or stopped from a different thread, attempts to do so will be rejected and
+ * logged, and may cause undefined behaviour.
  */
 
 /**
@@ -57,17 +62,24 @@  Timer::~Timer()
  * \brief Start or restart the timer with a timeout of \a msec
  * \param[in] msec The timer duration in milliseconds
  *
- * If the timer is already running it will be stopped and restarted.
+ * This method shall be called from the thread the timer is associated with. If
+ * the timer is already running it will be stopped and restarted.
  */
 
 /**
  * \brief Start or restart the timer with a timeout of \a duration
  * \param[in] duration The timer duration in milliseconds
  *
- * If the timer is already running it will be stopped and restarted.
+ * This method shall be called from the thread the timer is associated with. If
+ * the timer is already running it will be stopped and restarted.
  */
 void Timer::start(std::chrono::milliseconds duration)
 {
+	if (Thread::current() != thread()) {
+		LOG(Timer, Error) << "Timer can't be started from another thread";
+		return;
+	}
+
 	deadline_ = utils::clock::now() + duration;
 
 	LOG(Timer, Debug)
@@ -87,13 +99,19 @@  void Timer::start(std::chrono::milliseconds duration)
  * After this function returns the timer is guaranteed not to emit the
  * \ref timeout signal.
  *
- * If the timer is not running this function performs no operation.
+ * This method shall be called from the thread the timer is associated with. If
+ * the timer is not running this function performs no operation.
  */
 void Timer::stop()
 {
 	if (!isRunning())
 		return;
 
+	if (Thread::current() != thread()) {
+		LOG(Timer, Error) << "Timer can't be stopped from another thread";
+		return;
+	}
+
 	unregisterTimer();
 }