support using a priority inheritance protocol

Provides initial support for utilizing a priority inheritance protocol
for mutexes if supported by the threading library. Developers can opt to
build with `SPDLOG_PRIORITY_INHERITANCE` enabled to help avoid
priority inversion scenarios.

Signed-off-by: James Knight <git@jdknight.me>
pull/3356/head
James Knight 6 months ago
parent 3335c380a0
commit 6209e31fef

@ -91,6 +91,7 @@ option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library." OFF)
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF) option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF) option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
option(SPDLOG_PRIORITY_INHERITANCE "Compile with priority inheritance protocols" OFF)
if (SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO) if (SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
@ -280,6 +281,7 @@ foreach (
SPDLOG_NO_TLS SPDLOG_NO_TLS
SPDLOG_NO_ATOMIC_LEVELS SPDLOG_NO_ATOMIC_LEVELS
SPDLOG_DISABLE_DEFAULT_LOGGER SPDLOG_DISABLE_DEFAULT_LOGGER
SPDLOG_PRIORITY_INHERITANCE
SPDLOG_USE_STD_FORMAT) SPDLOG_USE_STD_FORMAT)
if (${SPDLOG_OPTION}) if (${SPDLOG_OPTION})
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION}) target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})

@ -9,50 +9,50 @@
namespace spdlog { namespace spdlog {
namespace details { namespace details {
SPDLOG_INLINE backtracer::backtracer(const backtracer &other) { SPDLOG_INLINE backtracer::backtracer(const backtracer &other) {
std::lock_guard<std::mutex> lock(other.mutex_); std::lock_guard<spdlog_mutex> lock(other.mutex_);
enabled_ = other.enabled(); enabled_ = other.enabled();
messages_ = other.messages_; messages_ = other.messages_;
} }
SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT { SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT {
std::lock_guard<std::mutex> lock(other.mutex_); std::lock_guard<spdlog_mutex> lock(other.mutex_);
enabled_ = other.enabled(); enabled_ = other.enabled();
messages_ = std::move(other.messages_); messages_ = std::move(other.messages_);
} }
SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) { SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<spdlog_mutex> lock(mutex_);
enabled_ = other.enabled(); enabled_ = other.enabled();
messages_ = std::move(other.messages_); messages_ = std::move(other.messages_);
return *this; return *this;
} }
SPDLOG_INLINE void backtracer::enable(size_t size) { SPDLOG_INLINE void backtracer::enable(size_t size) {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<spdlog_mutex> lock{mutex_};
enabled_.store(true, std::memory_order_relaxed); enabled_.store(true, std::memory_order_relaxed);
messages_ = circular_q<log_msg_buffer>{size}; messages_ = circular_q<log_msg_buffer>{size};
} }
SPDLOG_INLINE void backtracer::disable() { SPDLOG_INLINE void backtracer::disable() {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<spdlog_mutex> lock{mutex_};
enabled_.store(false, std::memory_order_relaxed); enabled_.store(false, std::memory_order_relaxed);
} }
SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); } SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); }
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) { SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<spdlog_mutex> lock{mutex_};
messages_.push_back(log_msg_buffer{msg}); messages_.push_back(log_msg_buffer{msg});
} }
SPDLOG_INLINE bool backtracer::empty() const { SPDLOG_INLINE bool backtracer::empty() const {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<spdlog_mutex> lock{mutex_};
return messages_.empty(); return messages_.empty();
} }
// pop all items in the q and apply the given fun on each of them. // pop all items in the q and apply the given fun on each of them.
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun) { SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun) {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<spdlog_mutex> lock{mutex_};
while (!messages_.empty()) { while (!messages_.empty()) {
auto &front_msg = messages_.front(); auto &front_msg = messages_.front();
fun(front_msg); fun(front_msg);

@ -5,6 +5,7 @@
#include <spdlog/details/circular_q.h> #include <spdlog/details/circular_q.h>
#include <spdlog/details/log_msg_buffer.h> #include <spdlog/details/log_msg_buffer.h>
#include <spdlog/details/mutex.h>
#include <atomic> #include <atomic>
#include <functional> #include <functional>
@ -16,7 +17,7 @@
namespace spdlog { namespace spdlog {
namespace details { namespace details {
class SPDLOG_API backtracer { class SPDLOG_API backtracer {
mutable std::mutex mutex_; mutable spdlog_mutex mutex_;
std::atomic<bool> enabled_{false}; std::atomic<bool> enabled_{false};
circular_q<log_msg_buffer> messages_; circular_q<log_msg_buffer> messages_;

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <mutex> #include <mutex>
#include <spdlog/details/mutex.h>
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
namespace spdlog { namespace spdlog {
@ -12,8 +13,8 @@ namespace details {
struct console_mutex { struct console_mutex {
using mutex_t = std::mutex; using mutex_t = std::mutex;
static mutex_t &mutex() { static mutex_t &mutex() {
static mutex_t s_mutex; static spdlog_mutex s_mutex;
return s_mutex; return s_mutex.mtx();
} }
}; };

@ -11,6 +11,7 @@
// passed. // passed.
#include <spdlog/details/circular_q.h> #include <spdlog/details/circular_q.h>
#include <spdlog/details/mutex.h>
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
@ -30,7 +31,7 @@ public:
// try to enqueue and block if no room left // try to enqueue and block if no room left
void enqueue(T &&item) { void enqueue(T &&item) {
{ {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
pop_cv_.wait(lock, [this] { return !this->q_.full(); }); pop_cv_.wait(lock, [this] { return !this->q_.full(); });
q_.push_back(std::move(item)); q_.push_back(std::move(item));
} }
@ -40,7 +41,7 @@ public:
// enqueue immediately. overrun oldest message in the queue if no room left. // enqueue immediately. overrun oldest message in the queue if no room left.
void enqueue_nowait(T &&item) { void enqueue_nowait(T &&item) {
{ {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
q_.push_back(std::move(item)); q_.push_back(std::move(item));
} }
push_cv_.notify_one(); push_cv_.notify_one();
@ -49,7 +50,7 @@ public:
void enqueue_if_have_room(T &&item) { void enqueue_if_have_room(T &&item) {
bool pushed = false; bool pushed = false;
{ {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
if (!q_.full()) { if (!q_.full()) {
q_.push_back(std::move(item)); q_.push_back(std::move(item));
pushed = true; pushed = true;
@ -67,7 +68,7 @@ public:
// Return true, if succeeded dequeue item, false otherwise // Return true, if succeeded dequeue item, false otherwise
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) {
{ {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) { if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) {
return false; return false;
} }
@ -81,7 +82,7 @@ public:
// blocking dequeue without a timeout. // blocking dequeue without a timeout.
void dequeue(T &popped_item) { void dequeue(T &popped_item) {
{ {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
push_cv_.wait(lock, [this] { return !this->q_.empty(); }); push_cv_.wait(lock, [this] { return !this->q_.empty(); });
popped_item = std::move(q_.front()); popped_item = std::move(q_.front());
q_.pop_front(); q_.pop_front();
@ -95,7 +96,7 @@ public:
// try to enqueue and block if no room left // try to enqueue and block if no room left
void enqueue(T &&item) { void enqueue(T &&item) {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
pop_cv_.wait(lock, [this] { return !this->q_.full(); }); pop_cv_.wait(lock, [this] { return !this->q_.full(); });
q_.push_back(std::move(item)); q_.push_back(std::move(item));
push_cv_.notify_one(); push_cv_.notify_one();
@ -103,14 +104,14 @@ public:
// enqueue immediately. overrun oldest message in the queue if no room left. // enqueue immediately. overrun oldest message in the queue if no room left.
void enqueue_nowait(T &&item) { void enqueue_nowait(T &&item) {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
q_.push_back(std::move(item)); q_.push_back(std::move(item));
push_cv_.notify_one(); push_cv_.notify_one();
} }
void enqueue_if_have_room(T &&item) { void enqueue_if_have_room(T &&item) {
bool pushed = false; bool pushed = false;
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
if (!q_.full()) { if (!q_.full()) {
q_.push_back(std::move(item)); q_.push_back(std::move(item));
pushed = true; pushed = true;
@ -126,7 +127,7 @@ public:
// dequeue with a timeout. // dequeue with a timeout.
// Return true, if succeeded dequeue item, false otherwise // Return true, if succeeded dequeue item, false otherwise
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) { if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) {
return false; return false;
} }
@ -138,7 +139,7 @@ public:
// blocking dequeue without a timeout. // blocking dequeue without a timeout.
void dequeue(T &popped_item) { void dequeue(T &popped_item) {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_.mtx());
push_cv_.wait(lock, [this] { return !this->q_.empty(); }); push_cv_.wait(lock, [this] { return !this->q_.empty(); });
popped_item = std::move(q_.front()); popped_item = std::move(q_.front());
q_.pop_front(); q_.pop_front();
@ -148,26 +149,26 @@ public:
#endif #endif
size_t overrun_counter() { size_t overrun_counter() {
std::lock_guard<std::mutex> lock(queue_mutex_); std::lock_guard<std::mutex> lock(queue_mutex_.mtx());
return q_.overrun_counter(); return q_.overrun_counter();
} }
size_t discard_counter() { return discard_counter_.load(std::memory_order_relaxed); } size_t discard_counter() { return discard_counter_.load(std::memory_order_relaxed); }
size_t size() { size_t size() {
std::lock_guard<std::mutex> lock(queue_mutex_); std::lock_guard<std::mutex> lock(queue_mutex_.mtx());
return q_.size(); return q_.size();
} }
void reset_overrun_counter() { void reset_overrun_counter() {
std::lock_guard<std::mutex> lock(queue_mutex_); std::lock_guard<std::mutex> lock(queue_mutex_.mtx());
q_.reset_overrun_counter(); q_.reset_overrun_counter();
} }
void reset_discard_counter() { discard_counter_.store(0, std::memory_order_relaxed); } void reset_discard_counter() { discard_counter_.store(0, std::memory_order_relaxed); }
private: private:
std::mutex queue_mutex_; spdlog_mutex queue_mutex_;
std::condition_variable push_cv_; std::condition_variable push_cv_;
std::condition_variable pop_cv_; std::condition_variable pop_cv_;
spdlog::details::circular_q<T> q_; spdlog::details::circular_q<T> q_;

@ -0,0 +1,144 @@
// SPDX-License-Identifier: MIT
// Copyright(c) spdlog contributors
// Mutexes used to help support priority inheritance support
//
// Standard library implementations will typically create mutexes that do
// not take into consideration the priority and scheduling of the thread
// they claim ownership on. In (near) real-time implementations, this is
// not ideal since this may lead to priority inversion scenarios.
//
// The following provides mutex implementations that can help avoid priority
// inversion scenarios by ensuring the underlying mutexes used take advantage
// of priority inheritance protocols. spdlog uses `std::mutex` and
// `std::recursive_mutex`, which are typically not priority inversion "safe".
// Therefore, we introduces custom mutexes for them below. Calls such as
// `std::condition_variable` are fine if the managed mutex used by the
// `std::unique_lock` are safe. However, the use of
// `std::condition_variable_any` is not due their implementations relying on
// both a provided mutex and an internal `std::mutex`. This is why the
// implementation opts for using `std::condition_variable` with the help of
// `mtx()` methods over using `std::condition_variable_any`.
#pragma once
#include <mutex>
#include <spdlog/common.h>
namespace spdlog {
namespace details {
class spdlog_mutex {
public:
spdlog_mutex() {
#ifdef SPDLOG_PRIORITY_INHERITANCE
#if defined(_GLIBCXX_HAS_GTHREADS) || defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
pthread_mutexattr_t attr;
int ret = pthread_mutexattr_init(&attr);
if (ret != 0) {
return;
}
ret = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
if (ret != 0) {
return;
}
auto handle = mtx_.native_handle();
ret = pthread_mutex_destroy(handle);
if (ret < 0) {
throw_spdlog_ex("failed to tear down mutex", ret);
}
ret = pthread_mutex_init(handle, &attr);
if (ret < 0) {
throw_spdlog_ex("failed to build mutex", ret);
}
#else
#error Priority inheritance is not supported.
#endif
#endif
}
void lock() {
mtx_.lock();
}
bool try_lock() {
return mtx_.try_lock();
}
void unlock() {
mtx_.unlock();
}
std::mutex& mtx() {
return mtx_;
}
private:
std::mutex mtx_;
};
class spdlog_recursive_mutex {
public:
spdlog_recursive_mutex() {
#ifdef SPDLOG_PRIORITY_INHERITANCE
#if defined(_GLIBCXX_HAS_GTHREADS) || defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
pthread_mutexattr_t attr;
int ret = pthread_mutexattr_init(&attr);
if (ret != 0) {
return;
}
ret = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
if (ret != 0) {
return;
}
ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (ret != 0) {
return;
}
auto handle = mtx_.native_handle();
ret = pthread_mutex_destroy(handle);
if (ret < 0) {
throw_spdlog_ex("failed to tear down mutex", ret);
}
ret = pthread_mutex_init(handle, &attr);
if (ret < 0) {
throw_spdlog_ex("failed to build mutex", ret);
}
#else
#error Priority inheritance is not supported.
#endif
#endif
}
void lock() {
mtx_.lock();
}
bool try_lock() {
return mtx_.try_lock();
}
void unlock() {
mtx_.unlock();
}
std::recursive_mutex& mtx() {
return mtx_;
}
private:
std::recursive_mutex mtx_;
};
} // namespace details
} // namespace spdlog

@ -14,7 +14,7 @@ namespace details {
SPDLOG_INLINE periodic_worker::~periodic_worker() { SPDLOG_INLINE periodic_worker::~periodic_worker() {
if (worker_thread_.joinable()) { if (worker_thread_.joinable()) {
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<spdlog_mutex> lock(mutex_);
active_ = false; active_ = false;
} }
cv_.notify_one(); cv_.notify_one();

@ -10,6 +10,8 @@
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it // stops and joins the thread on destruction (if the thread is executing a callback, wait for it
// to finish first). // to finish first).
#include <spdlog/details/mutex.h>
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>
#include <functional> #include <functional>
@ -30,7 +32,7 @@ public:
worker_thread_ = std::thread([this, callback_fun, interval]() { worker_thread_ = std::thread([this, callback_fun, interval]() {
for (;;) { for (;;) {
std::unique_lock<std::mutex> lock(this->mutex_); std::unique_lock<std::mutex> lock(this->mutex_.mtx());
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) { if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) {
return; // active_ == false, so exit this thread return; // active_ == false, so exit this thread
} }
@ -47,7 +49,7 @@ public:
private: private:
bool active_; bool active_;
std::thread worker_thread_; std::thread worker_thread_;
std::mutex mutex_; spdlog_mutex mutex_;
std::condition_variable cv_; std::condition_variable cv_;
}; };
} // namespace details } // namespace details

@ -50,12 +50,12 @@ SPDLOG_INLINE registry::registry()
SPDLOG_INLINE registry::~registry() = default; SPDLOG_INLINE registry::~registry() = default;
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger) { SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
register_logger_(std::move(new_logger)); register_logger_(std::move(new_logger));
} }
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger) { SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
new_logger->set_formatter(formatter_->clone()); new_logger->set_formatter(formatter_->clone());
if (err_handler_) { if (err_handler_) {
@ -79,13 +79,13 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
} }
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name) { SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
auto found = loggers_.find(logger_name); auto found = loggers_.find(logger_name);
return found == loggers_.end() ? nullptr : found->second; return found == loggers_.end() ? nullptr : found->second;
} }
SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() { SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
return default_logger_; return default_logger_;
} }
@ -98,7 +98,7 @@ SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get()
// set default logger. // set default logger.
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map. // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) { SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
if (new_default_logger != nullptr) { if (new_default_logger != nullptr) {
loggers_[new_default_logger->name()] = new_default_logger; loggers_[new_default_logger->name()] = new_default_logger;
} }
@ -106,18 +106,18 @@ SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_defa
} }
SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp) { SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp) {
std::lock_guard<std::recursive_mutex> lock(tp_mutex_); std::lock_guard<spdlog_recursive_mutex> lock(tp_mutex_);
tp_ = std::move(tp); tp_ = std::move(tp);
} }
SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp() { SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp() {
std::lock_guard<std::recursive_mutex> lock(tp_mutex_); std::lock_guard<spdlog_recursive_mutex> lock(tp_mutex_);
return tp_; return tp_;
} }
// Set global formatter. Each sink in each logger will get a clone of this object // Set global formatter. Each sink in each logger will get a clone of this object
SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter) { SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
formatter_ = std::move(formatter); formatter_ = std::move(formatter);
for (auto &l : loggers_) { for (auto &l : loggers_) {
l.second->set_formatter(formatter_->clone()); l.second->set_formatter(formatter_->clone());
@ -125,7 +125,7 @@ SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
} }
SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages) { SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
backtrace_n_messages_ = n_messages; backtrace_n_messages_ = n_messages;
for (auto &l : loggers_) { for (auto &l : loggers_) {
@ -134,7 +134,7 @@ SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages) {
} }
SPDLOG_INLINE void registry::disable_backtrace() { SPDLOG_INLINE void registry::disable_backtrace() {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
backtrace_n_messages_ = 0; backtrace_n_messages_ = 0;
for (auto &l : loggers_) { for (auto &l : loggers_) {
l.second->disable_backtrace(); l.second->disable_backtrace();
@ -142,7 +142,7 @@ SPDLOG_INLINE void registry::disable_backtrace() {
} }
SPDLOG_INLINE void registry::set_level(level::level_enum log_level) { SPDLOG_INLINE void registry::set_level(level::level_enum log_level) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) { for (auto &l : loggers_) {
l.second->set_level(log_level); l.second->set_level(log_level);
} }
@ -150,7 +150,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level) {
} }
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) { SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) { for (auto &l : loggers_) {
l.second->flush_on(log_level); l.second->flush_on(log_level);
} }
@ -158,7 +158,7 @@ SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) {
} }
SPDLOG_INLINE void registry::set_error_handler(err_handler handler) { SPDLOG_INLINE void registry::set_error_handler(err_handler handler) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) { for (auto &l : loggers_) {
l.second->set_error_handler(handler); l.second->set_error_handler(handler);
} }
@ -167,21 +167,21 @@ SPDLOG_INLINE void registry::set_error_handler(err_handler handler) {
SPDLOG_INLINE void registry::apply_all( SPDLOG_INLINE void registry::apply_all(
const std::function<void(const std::shared_ptr<logger>)> &fun) { const std::function<void(const std::shared_ptr<logger>)> &fun) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) { for (auto &l : loggers_) {
fun(l.second); fun(l.second);
} }
} }
SPDLOG_INLINE void registry::flush_all() { SPDLOG_INLINE void registry::flush_all() {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) { for (auto &l : loggers_) {
l.second->flush(); l.second->flush();
} }
} }
SPDLOG_INLINE void registry::drop(const std::string &logger_name) { SPDLOG_INLINE void registry::drop(const std::string &logger_name) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
auto is_default_logger = default_logger_ && default_logger_->name() == logger_name; auto is_default_logger = default_logger_ && default_logger_->name() == logger_name;
loggers_.erase(logger_name); loggers_.erase(logger_name);
if (is_default_logger) { if (is_default_logger) {
@ -190,7 +190,7 @@ SPDLOG_INLINE void registry::drop(const std::string &logger_name) {
} }
SPDLOG_INLINE void registry::drop_all() { SPDLOG_INLINE void registry::drop_all() {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
loggers_.clear(); loggers_.clear();
default_logger_.reset(); default_logger_.reset();
} }
@ -198,27 +198,27 @@ SPDLOG_INLINE void registry::drop_all() {
// clean all resources and threads started by the registry // clean all resources and threads started by the registry
SPDLOG_INLINE void registry::shutdown() { SPDLOG_INLINE void registry::shutdown() {
{ {
std::lock_guard<std::mutex> lock(flusher_mutex_); std::lock_guard<spdlog_mutex> lock(flusher_mutex_);
periodic_flusher_.reset(); periodic_flusher_.reset();
} }
drop_all(); drop_all();
{ {
std::lock_guard<std::recursive_mutex> lock(tp_mutex_); std::lock_guard<spdlog_recursive_mutex> lock(tp_mutex_);
tp_.reset(); tp_.reset();
} }
} }
SPDLOG_INLINE std::recursive_mutex &registry::tp_mutex() { return tp_mutex_; } SPDLOG_INLINE std::recursive_mutex &registry::tp_mutex() { return tp_mutex_.mtx(); }
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration) { SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
automatic_registration_ = automatic_registration; automatic_registration_ = automatic_registration;
} }
SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level) { SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
log_levels_ = std::move(levels); log_levels_ = std::move(levels);
auto global_level_requested = global_level != nullptr; auto global_level_requested = global_level != nullptr;
global_log_level_ = global_level_requested ? *global_level : global_log_level_; global_log_level_ = global_level_requested ? *global_level : global_log_level_;
@ -239,7 +239,7 @@ SPDLOG_INLINE registry &registry::instance() {
} }
SPDLOG_INLINE void registry::apply_logger_env_levels(std::shared_ptr<logger> new_logger) { SPDLOG_INLINE void registry::apply_logger_env_levels(std::shared_ptr<logger> new_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<spdlog_mutex> lock(logger_map_mutex_);
auto it = log_levels_.find(new_logger->name()); auto it = log_levels_.find(new_logger->name());
auto new_level = it != log_levels_.end() ? it->second : global_log_level_; auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
new_logger->set_level(new_level); new_logger->set_level(new_level);

@ -10,6 +10,7 @@
#include <spdlog/common.h> #include <spdlog/common.h>
#include <spdlog/details/periodic_worker.h> #include <spdlog/details/periodic_worker.h>
#include <spdlog/details/mutex.h>
#include <chrono> #include <chrono>
#include <functional> #include <functional>
@ -65,13 +66,13 @@ public:
template <typename Rep, typename Period> template <typename Rep, typename Period>
void flush_every(std::chrono::duration<Rep, Period> interval) { void flush_every(std::chrono::duration<Rep, Period> interval) {
std::lock_guard<std::mutex> lock(flusher_mutex_); std::lock_guard<spdlog_mutex> lock(flusher_mutex_);
auto clbk = [this]() { this->flush_all(); }; auto clbk = [this]() { this->flush_all(); };
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval); periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
} }
std::unique_ptr<periodic_worker> &get_flusher() { std::unique_ptr<periodic_worker> &get_flusher() {
std::lock_guard<std::mutex> lock(flusher_mutex_); std::lock_guard<spdlog_mutex> lock(flusher_mutex_);
return periodic_flusher_; return periodic_flusher_;
} }
@ -106,8 +107,8 @@ private:
void throw_if_exists_(const std::string &logger_name); void throw_if_exists_(const std::string &logger_name);
void register_logger_(std::shared_ptr<logger> new_logger); void register_logger_(std::shared_ptr<logger> new_logger);
bool set_level_from_cfg_(logger *logger); bool set_level_from_cfg_(logger *logger);
std::mutex logger_map_mutex_, flusher_mutex_; spdlog_mutex logger_map_mutex_, flusher_mutex_;
std::recursive_mutex tp_mutex_; spdlog_recursive_mutex tp_mutex_;
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_; std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
log_levels log_levels_; log_levels log_levels_;
std::unique_ptr<formatter> formatter_; std::unique_ptr<formatter> formatter_;

Loading…
Cancel
Save