[v3,7/8] libcamera: base: log: Protect log categories with lock
diff mbox series

Message ID 20250217185433.306833-8-pobrn@protonmail.com
State Superseded
Headers show
Series
  • libcamera: base: log: Misc. changes
Related show

Commit Message

Barnabás Pőcze Feb. 17, 2025, 6:55 p.m. UTC
Log categories may be added from any thread, so it is important to
synchronize access to the `Logger::categories_` list between its two
users: category creation (by LogCategory::create(), which calls
Logger::findCategory() and Logger::registerCategory()); and log level
setting (by Logger::logSetLevel()).

The LogCategory::create() function uses a mutex to serialize category
creation, but Logger::logSetLevel() can access `Logger::categories_`
concurrently without any protection. To fix the issue, move the mutex to
the Logger class, and use it to protect all accesses to the categories
list. This requires moving all the logic of LogCategory::create() to a
new Logger::findOrCreateCategory() function that combines both
Logger::findCategory() and Logger::registerCategory() in order to make
the two operations exacute atomically.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 include/libcamera/base/log.h |  1 +
 src/libcamera/base/log.cpp   | 58 +++++++++++++-----------------------
 2 files changed, 22 insertions(+), 37 deletions(-)

Comments

Laurent Pinchart Feb. 23, 2025, 11:22 p.m. UTC | #1
Hi Barnabás,

Thank you for the patch.

On Mon, Feb 17, 2025 at 06:55:05PM +0000, Barnabás Pőcze wrote:
> Log categories may be added from any thread, so it is important to
> synchronize access to the `Logger::categories_` list between its two
> users: category creation (by LogCategory::create(), which calls
> Logger::findCategory() and Logger::registerCategory()); and log level
> setting (by Logger::logSetLevel()).
> 
> The LogCategory::create() function uses a mutex to serialize category
> creation, but Logger::logSetLevel() can access `Logger::categories_`
> concurrently without any protection. To fix the issue, move the mutex to
> the Logger class, and use it to protect all accesses to the categories
> list. This requires moving all the logic of LogCategory::create() to a
> new Logger::findOrCreateCategory() function that combines both
> Logger::findCategory() and Logger::registerCategory() in order to make
> the two operations exacute atomically.
> 
> Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> ---
>  include/libcamera/base/log.h |  1 +
>  src/libcamera/base/log.cpp   | 58 +++++++++++++-----------------------
>  2 files changed, 22 insertions(+), 37 deletions(-)
> 
> diff --git a/include/libcamera/base/log.h b/include/libcamera/base/log.h
> index 195426c41..8ae1b51d0 100644
> --- a/include/libcamera/base/log.h
> +++ b/include/libcamera/base/log.h
> @@ -39,6 +39,7 @@ public:
>  	static const LogCategory &defaultCategory();
>  
>  private:
> +	friend class Logger;
>  	explicit LogCategory(std::string_view name);
>  
>  	const std::string name_;
> diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp
> index 2c233be36..fd6c11716 100644
> --- a/src/libcamera/base/log.cpp
> +++ b/src/libcamera/base/log.cpp
> @@ -317,12 +317,12 @@ private:
>  	static LogSeverity parseLogLevel(std::string_view level);
>  
>  	friend LogCategory;
> -	void registerCategory(LogCategory *category);
> -	LogCategory *findCategory(std::string_view name) const;
> +	LogCategory *findOrCreateCategory(std::string_view name);
>  
>  	static bool destroyed_;
>  
> -	std::vector<LogCategory *> categories_;
> +	Mutex mutex_;
> +	std::vector<LogCategory *> categories_ LIBCAMERA_TSA_GUARDED_BY(mutex_);
>  	std::list<std::pair<std::string, LogSeverity>> levels_;
>  
>  	std::atomic<std::shared_ptr<LogOutput>> output_;
> @@ -572,6 +572,8 @@ void Logger::logSetLevel(const char *category, const char *level)
>  	if (severity == LogInvalid)
>  		return;
>  
> +	MutexLocker locker(mutex_);
> +
>  	for (LogCategory *c : categories_) {
>  		if (c->name() == category) {
>  			c->setSeverity(severity);
> @@ -708,37 +710,28 @@ LogSeverity Logger::parseLogLevel(std::string_view level)
>  }
>  
>  /**
> - * \brief Register a log category with the logger
> - * \param[in] category The log category
> - *
> - * Log categories must have unique names. It is invalid to call this function
> - * if a log category with the same name already exists.
> + * \brief Find an existing log category with the given name or create one
> + * \param[in] name Name of the log category
> + * \return The pointer to the log category found or created
>   */
> -void Logger::registerCategory(LogCategory *category)
> +LogCategory *Logger::findOrCreateCategory(std::string_view name)
>  {
> -	categories_.push_back(category);
> +	MutexLocker locker(mutex_);
>  
> -	const std::string &name = category->name();
> -	for (const auto &[pattern, severity] : levels_) {
> -		if (fnmatch(pattern.c_str(), name.c_str(), FNM_NOESCAPE) == 0)
> -			category->setSeverity(severity);
> +	for (LogCategory *category : categories_) {
> +		if (category->name() == name)
> +			return category;
>  	}
> -}
>  
> -/**
> - * \brief Find an existing log category with the given name
> - * \param[in] name Name of the log category
> - * \return The pointer to the found log category or nullptr if not found
> - */
> -LogCategory *Logger::findCategory(std::string_view name) const
> -{
> -	if (auto it = std::find_if(categories_.begin(), categories_.end(),
> -				   [name](auto c) { return c->name() == name; });
> -	    it != categories_.end()) {
> -		return *it;
> +	LogCategory *category = categories_.emplace_back(new LogCategory(name));
> +	const char *name_cstr = category->name().c_str();

camelCase please. You could name is categoryName.

Any reason to not keep

	const std::string &categoryName = category->name();

here ? Both should be fine, but if Category::name() were ever changef to
return a std::string by value, the code above would lead to a
use-after-free. Up to you.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> +
> +	for (const auto &[pattern, severity] : levels_) {
> +		if (fnmatch(pattern.c_str(), name_cstr, FNM_NOESCAPE) == 0)
> +			category->setSeverity(severity);
>  	}
>  
> -	return nullptr;
> +	return category;
>  }
>  
>  /**
> @@ -776,16 +769,7 @@ LogCategory *Logger::findCategory(std::string_view name) const
>   */
>  LogCategory *LogCategory::create(std::string_view name)
>  {
> -	static Mutex mutex_;
> -	MutexLocker locker(mutex_);
> -	LogCategory *category = Logger::instance()->findCategory(name);
> -
> -	if (!category) {
> -		category = new LogCategory(name);
> -		Logger::instance()->registerCategory(category);
> -	}
> -
> -	return category;
> +	return Logger::instance()->findOrCreateCategory(name);
>  }
>  
>  /**

Patch
diff mbox series

diff --git a/include/libcamera/base/log.h b/include/libcamera/base/log.h
index 195426c41..8ae1b51d0 100644
--- a/include/libcamera/base/log.h
+++ b/include/libcamera/base/log.h
@@ -39,6 +39,7 @@  public:
 	static const LogCategory &defaultCategory();
 
 private:
+	friend class Logger;
 	explicit LogCategory(std::string_view name);
 
 	const std::string name_;
diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp
index 2c233be36..fd6c11716 100644
--- a/src/libcamera/base/log.cpp
+++ b/src/libcamera/base/log.cpp
@@ -317,12 +317,12 @@  private:
 	static LogSeverity parseLogLevel(std::string_view level);
 
 	friend LogCategory;
-	void registerCategory(LogCategory *category);
-	LogCategory *findCategory(std::string_view name) const;
+	LogCategory *findOrCreateCategory(std::string_view name);
 
 	static bool destroyed_;
 
-	std::vector<LogCategory *> categories_;
+	Mutex mutex_;
+	std::vector<LogCategory *> categories_ LIBCAMERA_TSA_GUARDED_BY(mutex_);
 	std::list<std::pair<std::string, LogSeverity>> levels_;
 
 	std::atomic<std::shared_ptr<LogOutput>> output_;
@@ -572,6 +572,8 @@  void Logger::logSetLevel(const char *category, const char *level)
 	if (severity == LogInvalid)
 		return;
 
+	MutexLocker locker(mutex_);
+
 	for (LogCategory *c : categories_) {
 		if (c->name() == category) {
 			c->setSeverity(severity);
@@ -708,37 +710,28 @@  LogSeverity Logger::parseLogLevel(std::string_view level)
 }
 
 /**
- * \brief Register a log category with the logger
- * \param[in] category The log category
- *
- * Log categories must have unique names. It is invalid to call this function
- * if a log category with the same name already exists.
+ * \brief Find an existing log category with the given name or create one
+ * \param[in] name Name of the log category
+ * \return The pointer to the log category found or created
  */
-void Logger::registerCategory(LogCategory *category)
+LogCategory *Logger::findOrCreateCategory(std::string_view name)
 {
-	categories_.push_back(category);
+	MutexLocker locker(mutex_);
 
-	const std::string &name = category->name();
-	for (const auto &[pattern, severity] : levels_) {
-		if (fnmatch(pattern.c_str(), name.c_str(), FNM_NOESCAPE) == 0)
-			category->setSeverity(severity);
+	for (LogCategory *category : categories_) {
+		if (category->name() == name)
+			return category;
 	}
-}
 
-/**
- * \brief Find an existing log category with the given name
- * \param[in] name Name of the log category
- * \return The pointer to the found log category or nullptr if not found
- */
-LogCategory *Logger::findCategory(std::string_view name) const
-{
-	if (auto it = std::find_if(categories_.begin(), categories_.end(),
-				   [name](auto c) { return c->name() == name; });
-	    it != categories_.end()) {
-		return *it;
+	LogCategory *category = categories_.emplace_back(new LogCategory(name));
+	const char *name_cstr = category->name().c_str();
+
+	for (const auto &[pattern, severity] : levels_) {
+		if (fnmatch(pattern.c_str(), name_cstr, FNM_NOESCAPE) == 0)
+			category->setSeverity(severity);
 	}
 
-	return nullptr;
+	return category;
 }
 
 /**
@@ -776,16 +769,7 @@  LogCategory *Logger::findCategory(std::string_view name) const
  */
 LogCategory *LogCategory::create(std::string_view name)
 {
-	static Mutex mutex_;
-	MutexLocker locker(mutex_);
-	LogCategory *category = Logger::instance()->findCategory(name);
-
-	if (!category) {
-		category = new LogCategory(name);
-		Logger::instance()->registerCategory(category);
-	}
-
-	return category;
+	return Logger::instance()->findOrCreateCategory(name);
 }
 
 /**