[RFC PATCH v2 8/9] libcamera: base: log: Protect log categories with lock
Barnabás Pőcze
pobrn at protonmail.com
Thu Feb 6 18:46:44 CET 2025
Hi
2025. február 6., csütörtök 18:11 keltezéssel, Laurent Pinchart <laurent.pinchart at ideasonboard.com> írta:
> Hi Barnabás,
>
> Thank you for the patch.
>
> On Thu, Jan 30, 2025 at 07:58:57PM +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: `Logger::findOrCreateCategory()` and `Logger::logSetLevel()`.
>
> findOrCreateCategory() is a new function, it's not a current user. You
> could write something along those lines:
>
> 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()).
>
> > To achieve that, `Logger::{find,register}Category()` are merged into
> > a single function: `Logger::findOrCreateCategory()`; and the mutex
> > from `LogCategory::create()` is moved into the `Logger` class.
> >
> > Furthermore, appropriate `MutexLocker`s are placed in
> > `Logger::findOrCreateCategory()` and `Logger::logSetLevel()`.
>
> It took me a bit of time to understand the patch, here's a proposal for
> an improved commit message.
>
> 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.
ACK
>
> > Signed-off-by: Barnabás Pőcze <pobrn at protonmail.com>
> > ---
> > include/libcamera/base/log.h | 2 +-
> > src/libcamera/base/log.cpp | 53 ++++++++++++------------------------
> > 2 files changed, 19 insertions(+), 36 deletions(-)
> >
> > diff --git a/include/libcamera/base/log.h b/include/libcamera/base/log.h
> > index 1fb92603f..b32ec4516 100644
> > --- a/include/libcamera/base/log.h
> > +++ b/include/libcamera/base/log.h
> > @@ -30,6 +30,7 @@ enum LogSeverity {
> > class LogCategory
> > {
> > public:
> > + explicit LogCategory(std::string_view name);
>
> It's not very nice to make this publicly available, as it shouldn't be
> used anywhere else than from the create() function (now) or the
> findOrCreateCategory() function (with this patch applied). Wouldn't it
> be better to keep the constructor private and make the Logger class a
> friend ?
I think this change might be here by mistake. I think it might only be needed
for the last patch. (It is unfortunate that having only private constructors
prevents the use of `make_{unique,shared}()`, `emplace_back()`, etc.)
>
> > static LogCategory *create(std::string_view name);
> >
> > const std::string &name() const { return name_; }
> > @@ -39,7 +40,6 @@ public:
> > static const LogCategory &defaultCategory();
> >
> > private:
> > - 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 19fc5cc67..69aa16c4c 100644
> > --- a/src/libcamera/base/log.cpp
> > +++ b/src/libcamera/base/log.cpp
> > @@ -402,11 +402,11 @@ private:
> > void parseLogFile();
> >
> > friend LogCategory;
> > - void registerCategory(LogCategory *category);
> > - LogCategory *findCategory(std::string_view name) const;
> > + LogCategory *findOrCreateCategory(std::string_view name);
> >
> > static bool destroyed_;
> >
> > + Mutex mutex_;
> > std::vector<LogCategory *> categories_;
>
> Can we add thread safety annotations to indicate that categories_ is
> protected by mutex_ ?
I think so.
>
> > std::list<std::pair<std::string, LogSeverity>> levels_;
> >
> > @@ -657,6 +657,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);
> > @@ -707,17 +709,21 @@ void Logger::parseLogFile()
> > }
> >
> > /**
> > - * \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_);
> > +
> > + for (LogCategory *category : categories_) {
> > + if (category->name() == name)
> > + return category;
> > + }
>
> So nobody likes std::find_if() ? :-( I can survive its removal.
>
> > +
> > + LogCategory *category = categories_.emplace_back(new LogCategory(name));
> >
> > - const std::string &name = category->name();
> > for (const std::pair<std::string, LogSeverity> &level : levels_) {
> > bool match = true;
> >
> > @@ -737,22 +743,8 @@ void Logger::registerCategory(LogCategory *category)
> > break;
> > }
> > }
> > -}
> >
> > -/**
> > - * \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;
> > - }
> > -
> > - return nullptr;
> > + return category;
> > }
> >
> > /**
> > @@ -790,16 +782,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);
> > }
> >
> > /**
>
> --
> Regards,
>
> Laurent Pinchart
>
More information about the libcamera-devel
mailing list