From a014f56d8c988e3453a3d7a79a88caef4fc19218 Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Sat, 25 Feb 2023 18:39:10 +0800 Subject: [PATCH 1/8] async write for easylog --- include/easylog/easylog.h | 32 ++++++- include/easylog/thread_pool.h | 134 +++++++++++++++++++++++++++++ src/easylog/benchmark/main.cpp | 15 +++- src/easylog/tests/test_easylog.cpp | 11 +++ 4 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 include/easylog/thread_pool.h diff --git a/include/easylog/easylog.h b/include/easylog/easylog.h index af96e27b2..2f3ccea4f 100644 --- a/include/easylog/easylog.h +++ b/include/easylog/easylog.h @@ -20,6 +20,7 @@ #include #include "appender.hpp" +#include "thread_pool.h" namespace easylog { @@ -31,7 +32,22 @@ class logger { return instance; } - void operator+=(const record_t &record) { write(record); } + void async_write(const record_t &record) { + thread_pool::instance().async( + [this, async_record = std::make_shared(record)] { + write(*async_record); + return true; + }); + } + + void operator+=(const record_t &record) { + if (enbale_async_write_) { + async_write(record); + } + else { + write(record); + } + } void write(const record_t &record) { append_format(record); @@ -50,12 +66,13 @@ class logger { void init(Severity min_severity, bool enable_console, const std::string &filename, size_t max_file_size, size_t max_files, - bool flush_every_time) { + bool flush_every_time, bool enable_async_write) { static appender appender(filename, max_file_size, max_files, flush_every_time); appender_ = &appender; min_severity_ = min_severity; enable_console_ = enable_console; + enbale_async_write_ = enable_async_write; } bool check_severity(Severity severity) { return severity >= min_severity_; } @@ -120,6 +137,7 @@ class logger { Severity min_severity_; bool enable_console_ = true; + bool enbale_async_write_ = false; appender *appender_ = nullptr; std::vector> appenders_; }; @@ -127,9 +145,15 @@ class logger { template inline void init_log(Severity min_severity, const std::string &filename = "", bool enable_console = true, size_t max_file_size = 0, - size_t max_files = 0, bool flush_every_time = false) { + size_t max_files = 0, bool flush_every_time = false, + bool enable_async_write = false) { + if (enable_async_write) { + thread_pool::instance().init(1); + } + logger::instance().init(min_severity, enable_console, filename, - max_file_size, max_files, flush_every_time); + max_file_size, max_files, flush_every_time, + enable_async_write); } template diff --git a/include/easylog/thread_pool.h b/include/easylog/thread_pool.h new file mode 100644 index 000000000..c715106c5 --- /dev/null +++ b/include/easylog/thread_pool.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace easylog { + +template +class blocking_queue { + public: + blocking_queue() = default; + + void enqueue(T &&item) { + { + std::unique_lock lock(queue_mutex_); + queue_.push_back(std::move(item)); + } + push_cv_.notify_one(); + } + + bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { + { + std::unique_lock lock(queue_mutex_); + if (!push_cv_.wait_for(lock, wait_duration, [this] { + return !queue_.empty(); + })) { + return false; + } + + popped_item = std::move(queue_.front()); + queue_.pop_front(); + } + return true; + } + + size_t size() { + std::unique_lock lock(queue_mutex_); + return queue_.size(); + } + + private: + std::mutex queue_mutex_; + std::condition_variable push_cv_; + std::list queue_; +}; + +class thread_pool { + public: + static thread_pool &instance() { + static thread_pool thread_instance; + return thread_instance; + } + + public: + typedef std::function Task; + using item_type = std::unique_ptr; + + void init(size_t threads_n) { + std::lock_guard lock(mutex_); + + if (!threads_.empty()) { + return; + } + + for (size_t i = 0; i < threads_n; i++) { + threads_.emplace_back([this] { + thread_pool::worker_loop(); + }); + } + } + + void async(Task &&task) { + queue_.enqueue(std::make_unique(std::move(task))); + } + + private: + thread_pool() = default; + thread_pool(const thread_pool &) = default; + + ~thread_pool() { + for (size_t i = 0; i < threads_.size(); i++) { + async([]() { + return false; + }); + } + + for (auto &t : threads_) { + t.join(); + } + } + + blocking_queue queue_; + + std::vector threads_; + + std::mutex mutex_; + + private: + void worker_loop() { + while (process_next_msg()) { + } + } + + bool process_next_msg() { + std::unique_ptr record_task; + bool dequeued = queue_.dequeue_for(record_task, std::chrono::seconds(10)); + if (!dequeued) { + return true; + } + + return (*record_task)(); + } +}; + +} // namespace easylog diff --git a/src/easylog/benchmark/main.cpp b/src/easylog/benchmark/main.cpp index 91ee9875a..cf720bdb8 100644 --- a/src/easylog/benchmark/main.cpp +++ b/src/easylog/benchmark/main.cpp @@ -66,7 +66,8 @@ void test_glog() { void test_easylog() { std::filesystem::remove("easylog.txt"); - easylog::init_log(Severity::DEBUG, "easylog.txt", false, 1024 * 1024, 1); + easylog::init_log(Severity::DEBUG, "easylog.txt", false, 1024 * 1024, 1, + false); { ScopedTimer timer("easylog"); for (int i = 0; i < 5000; i++) @@ -74,7 +75,19 @@ void test_easylog() { } } +void test_async_easylog() { + std::filesystem::remove("async_easylog.txt"); + easylog::init_log<1>(Severity::DEBUG, "async_easylog.txt", false, 1024 * 1024, + 1, false, true); + { + ScopedTimer timer("async_easylog"); + for (int i = 0; i < 5000; i++) + ELOG(INFO, 1) << "Hello, it is a long string test! " << 42 << 21 << 2.5; + } +} + int main() { test_glog(); test_easylog(); + test_async_easylog(); } diff --git a/src/easylog/tests/test_easylog.cpp b/src/easylog/tests/test_easylog.cpp index 1a1f2a8e2..85952d374 100644 --- a/src/easylog/tests/test_easylog.cpp +++ b/src/easylog/tests/test_easylog.cpp @@ -154,3 +154,14 @@ TEST_CASE("file_roll") { "he string that should be saved in the file.") != std::string::npos); } + +TEST_CASE("async_write") { + std::string async_write_file = "async_write.txt"; + std::filesystem::remove(async_write_file); + constexpr size_t SeventhId = 7; + easylog::init_log(Severity::DEBUG, async_write_file, true, + 1024 * 1024, 1, false, true); + for (int i = 0; i < 50; i++) + ELOG(INFO, SeventhId) << "Hello, it is a long string test! " << 42 << 21 + << 2.5; +} From 038fbd1aa90766a3c53b620dfd06d516dd74ac89 Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Mon, 27 Feb 2023 16:24:05 +0800 Subject: [PATCH 2/8] fix the gcc compilation problem --- include/easylog/thread_pool.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/easylog/thread_pool.h b/include/easylog/thread_pool.h index c715106c5..b3251bc09 100644 --- a/include/easylog/thread_pool.h +++ b/include/easylog/thread_pool.h @@ -20,7 +20,9 @@ #include #include #include -#include +#include +#include +#include namespace easylog { From 77688fbe79c3d0822e19e949b36c7d30f4343345 Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Tue, 28 Feb 2023 17:43:02 +0800 Subject: [PATCH 3/8] Ensure that the thread exits before log destruction --- include/easylog/easylog.h | 2 ++ include/easylog/thread_pool.h | 28 ++++++++++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/include/easylog/easylog.h b/include/easylog/easylog.h index 2f3ccea4f..ec1b4cebe 100644 --- a/include/easylog/easylog.h +++ b/include/easylog/easylog.h @@ -85,6 +85,8 @@ class logger { logger() = default; logger(const logger &) = default; + ~logger() { thread_pool::instance().quit(); } + template size_t get_time_str(char (&buf)[N], const auto &now) { const auto nowAsTimeT = std::chrono::system_clock::to_time_t(now); diff --git a/include/easylog/thread_pool.h b/include/easylog/thread_pool.h index b3251bc09..139f88787 100644 --- a/include/easylog/thread_pool.h +++ b/include/easylog/thread_pool.h @@ -17,12 +17,12 @@ #pragma once #include +#include #include -#include -#include #include +#include #include -#include +#include namespace easylog { @@ -90,15 +90,13 @@ class thread_pool { } } - void async(Task &&task) { - queue_.enqueue(std::make_unique(std::move(task))); - } + void quit() { + std::lock_guard lock(mutex_); - private: - thread_pool() = default; - thread_pool(const thread_pool &) = default; + if (threads_.empty()) { + return; + } - ~thread_pool() { for (size_t i = 0; i < threads_.size(); i++) { async([]() { return false; @@ -108,8 +106,18 @@ class thread_pool { for (auto &t : threads_) { t.join(); } + + threads_.clear(); + } + + void async(Task &&task) { + queue_.enqueue(std::make_unique(std::move(task))); } + private: + thread_pool() = default; + thread_pool(const thread_pool &) = default; + blocking_queue queue_; std::vector threads_; From d0a70d07d2f52ca3a25a1c1c107dfebe9c85cd0a Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Tue, 28 Feb 2023 19:18:55 +0800 Subject: [PATCH 4/8] The appender is a static variable. When the program exits, the thread is still writing asynchronously, but the appender has been released. The appender lifetime follows the logger. --- include/easylog/easylog.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/easylog/easylog.h b/include/easylog/easylog.h index ec1b4cebe..8efc70561 100644 --- a/include/easylog/easylog.h +++ b/include/easylog/easylog.h @@ -67,9 +67,8 @@ class logger { void init(Severity min_severity, bool enable_console, const std::string &filename, size_t max_file_size, size_t max_files, bool flush_every_time, bool enable_async_write) { - static appender appender(filename, max_file_size, max_files, - flush_every_time); - appender_ = &appender; + appender_ = std::make_unique(filename, max_file_size, max_files, + flush_every_time); min_severity_ = min_severity; enable_console_ = enable_console; enbale_async_write_ = enable_async_write; @@ -140,7 +139,7 @@ class logger { Severity min_severity_; bool enable_console_ = true; bool enbale_async_write_ = false; - appender *appender_ = nullptr; + std::unique_ptr appender_ = nullptr; std::vector> appenders_; }; From cbb01f1f93bc01eea9b3fd03515b42730ab344bb Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Wed, 1 Mar 2023 12:41:45 +0800 Subject: [PATCH 5/8] The thread safety problem of asynchronous writing when the program exits --- include/easylog/easylog.h | 55 ++++++++++++++++++++--------------- include/easylog/thread_pool.h | 18 ++++++------ 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/include/easylog/easylog.h b/include/easylog/easylog.h index 8efc70561..368157b39 100644 --- a/include/easylog/easylog.h +++ b/include/easylog/easylog.h @@ -24,6 +24,14 @@ namespace easylog { +struct log : public std::enable_shared_from_this { + Severity min_severity; + bool enable_console = true; + bool enbale_async_write = false; + std::unique_ptr append = nullptr; + std::vector> appenders; +}; + template class logger { public: @@ -33,15 +41,19 @@ class logger { } void async_write(const record_t &record) { + auto weak_ptr = log_->weak_from_this(); thread_pool::instance().async( - [this, async_record = std::make_shared(record)] { - write(*async_record); + [this, weak_ptr, async_record = std::make_shared(record)] { + auto strong_ptr = weak_ptr.lock(); + if (strong_ptr) { + write(*async_record); + } return true; }); } void operator+=(const record_t &record) { - if (enbale_async_write_) { + if (log_->enbale_async_write) { async_write(record); } else { @@ -59,33 +71,34 @@ class logger { } void flush() { - if (appender_) { - appender_->flush(); + if (log_->append) { + log_->append->flush(); } } void init(Severity min_severity, bool enable_console, const std::string &filename, size_t max_file_size, size_t max_files, bool flush_every_time, bool enable_async_write) { - appender_ = std::make_unique(filename, max_file_size, max_files, - flush_every_time); - min_severity_ = min_severity; - enable_console_ = enable_console; - enbale_async_write_ = enable_async_write; + log_ = std::make_shared(); + log_->append = std::make_unique(filename, max_file_size, + max_files, flush_every_time); + log_->min_severity = min_severity; + log_->enable_console = enable_console; + log_->enbale_async_write = enable_async_write; } - bool check_severity(Severity severity) { return severity >= min_severity_; } + bool check_severity(Severity severity) { + return severity >= log_->min_severity; + } void add_appender(std::function fn) { - appenders_.emplace_back(std::move(fn)); + log_->appenders.emplace_back(std::move(fn)); } private: logger() = default; logger(const logger &) = default; - ~logger() { thread_pool::instance().quit(); } - template size_t get_time_str(char (&buf)[N], const auto &now) { const auto nowAsTimeT = std::chrono::system_clock::to_time_t(now); @@ -122,25 +135,21 @@ class logger { str.append(record.get_file_str()); str.append(record.get_message()).append("\n"); - if (appender_) { - appender_->write(str); + if (log_->append) { + log_->append->write(str); } - if (enable_console_) { + if (log_->enable_console) { std::cout << str; std::cout << std::flush; } - for (auto &fn : appenders_) { + for (auto &fn : log_->appenders) { fn(std::string_view(str)); } } - Severity min_severity_; - bool enable_console_ = true; - bool enbale_async_write_ = false; - std::unique_ptr appender_ = nullptr; - std::vector> appenders_; + std::shared_ptr log_; }; template diff --git a/include/easylog/thread_pool.h b/include/easylog/thread_pool.h index 139f88787..231c48ff0 100644 --- a/include/easylog/thread_pool.h +++ b/include/easylog/thread_pool.h @@ -90,7 +90,15 @@ class thread_pool { } } - void quit() { + void async(Task &&task) { + queue_.enqueue(std::make_unique(std::move(task))); + } + + private: + thread_pool() = default; + thread_pool(const thread_pool &) = default; + + ~thread_pool() { std::lock_guard lock(mutex_); if (threads_.empty()) { @@ -110,14 +118,6 @@ class thread_pool { threads_.clear(); } - void async(Task &&task) { - queue_.enqueue(std::make_unique(std::move(task))); - } - - private: - thread_pool() = default; - thread_pool(const thread_pool &) = default; - blocking_queue queue_; std::vector threads_; From 2ca4d0063bd0b2364cf37142a8ed8813bfcbdc8d Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Wed, 1 Mar 2023 17:28:58 +0800 Subject: [PATCH 6/8] Fix null pointer problem --- include/easylog/easylog.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/easylog/easylog.h b/include/easylog/easylog.h index 368157b39..fa38d2013 100644 --- a/include/easylog/easylog.h +++ b/include/easylog/easylog.h @@ -71,7 +71,7 @@ class logger { } void flush() { - if (log_->append) { + if (log_ && log_->append) { log_->append->flush(); } } @@ -88,11 +88,16 @@ class logger { } bool check_severity(Severity severity) { - return severity >= log_->min_severity; + if (log_) { + return severity >= log_->min_severity; + } + return false; } void add_appender(std::function fn) { - log_->appenders.emplace_back(std::move(fn)); + if (log_) { + log_->appenders.emplace_back(std::move(fn)); + } } private: @@ -149,7 +154,7 @@ class logger { } } - std::shared_ptr log_; + std::shared_ptr log_ = nullptr; }; template From 6f9cd95993d8e348fdab006f3701385d927222be Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Fri, 3 Mar 2023 21:38:39 +0800 Subject: [PATCH 7/8] asynchronous write record uses heap memory --- include/easylog/easylog.h | 72 +++++++++++++++++++--------------- include/easylog/thread_pool.h | 1 - src/easylog/benchmark/main.cpp | 4 +- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/include/easylog/easylog.h b/include/easylog/easylog.h index fa38d2013..775cc2df9 100644 --- a/include/easylog/easylog.h +++ b/include/easylog/easylog.h @@ -40,26 +40,20 @@ class logger { return instance; } - void async_write(const record_t &record) { + void async_write(std::shared_ptr record) { auto weak_ptr = log_->weak_from_this(); - thread_pool::instance().async( - [this, weak_ptr, async_record = std::make_shared(record)] { - auto strong_ptr = weak_ptr.lock(); - if (strong_ptr) { - write(*async_record); - } - return true; - }); + thread_pool::instance().async([this, weak_ptr, record] { + auto strong_ptr = weak_ptr.lock(); + if (strong_ptr) { + write(*record); + } + return true; + }); } - void operator+=(const record_t &record) { - if (log_->enbale_async_write) { - async_write(record); - } - else { - write(record); - } - } + void operator+=(const record_t &record) { write(record); } + + void operator+=(std::shared_ptr record) { async_write(record); } void write(const record_t &record) { append_format(record); @@ -94,6 +88,13 @@ class logger { return false; } + bool check_async_write() { + if (log_) { + return log_->enbale_async_write; + } + return false; + } + void add_appender(std::function fn) { if (log_) { log_->appenders.emplace_back(std::move(fn)); @@ -194,19 +195,28 @@ inline void add_appender(std::function fn) { #define ELOG(severity, ...) ELOG_IMPL(Severity::severity, __VA_ARGS__, 0) -#define ELOGV_IMPL(severity, Id, fmt, ...) \ - if (!easylog::logger::instance().check_severity(severity)) { \ - ; \ - } \ - else { \ - easylog::logger::instance() += \ - easylog::record_t(std::chrono::system_clock::now(), severity, \ - GET_STRING(__FILE__, __LINE__)) \ - .sprintf(fmt, __VA_ARGS__); \ - if (severity == Severity::CRITICAL) { \ - easylog::flush(); \ - std::exit(EXIT_FAILURE); \ - } \ +#define ELOGV_IMPL(severity, Id, fmt, ...) \ + if (!easylog::logger::instance().check_severity(severity)) { \ + ; \ + } \ + else { \ + if (!easylog::logger::instance().check_async_write()) { \ + easylog::logger::instance() += \ + easylog::record_t(std::chrono::system_clock::now(), severity, \ + GET_STRING(__FILE__, __LINE__)) \ + .sprintf(fmt, __VA_ARGS__); \ + } \ + else { \ + auto record = std::make_shared( \ + std::chrono::system_clock::now(), severity, \ + GET_STRING(__FILE__, __LINE__)); \ + record->sprintf(fmt, __VA_ARGS__); \ + easylog::logger::instance() += record; \ + } \ + if (severity == Severity::CRITICAL) { \ + easylog::flush(); \ + std::exit(EXIT_FAILURE); \ + } \ } #define ELOGV(severity, ...) \ @@ -234,4 +244,4 @@ inline void add_appender(std::function fn) { #define ELOGI ELOG_INFO #define ELOGW ELOG_WARN #define ELOGE ELOG_ERROR -#define ELOGC ELOG_CRITICAL +#define ELOGC ELOG_CRITICAL \ No newline at end of file diff --git a/include/easylog/thread_pool.h b/include/easylog/thread_pool.h index 231c48ff0..96910cc6c 100644 --- a/include/easylog/thread_pool.h +++ b/include/easylog/thread_pool.h @@ -96,7 +96,6 @@ class thread_pool { private: thread_pool() = default; - thread_pool(const thread_pool &) = default; ~thread_pool() { std::lock_guard lock(mutex_); diff --git a/src/easylog/benchmark/main.cpp b/src/easylog/benchmark/main.cpp index cf720bdb8..02ce1bd13 100644 --- a/src/easylog/benchmark/main.cpp +++ b/src/easylog/benchmark/main.cpp @@ -71,7 +71,7 @@ void test_easylog() { { ScopedTimer timer("easylog"); for (int i = 0; i < 5000; i++) - ELOG(INFO) << "Hello, it is a long string test! " << 42 << 21 << 2.5; + MELOGV(INFO, 0, "Hello, it is a long string test! Hello World"); } } @@ -82,7 +82,7 @@ void test_async_easylog() { { ScopedTimer timer("async_easylog"); for (int i = 0; i < 5000; i++) - ELOG(INFO, 1) << "Hello, it is a long string test! " << 42 << 21 << 2.5; + MELOGV(INFO, 1, "Hello, it is a long string test! Hello World"); } } From cdc039b297b303f6c463204b069ab9d51c68f641 Mon Sep 17 00:00:00 2001 From: UniverseParticle Date: Mon, 6 Mar 2023 12:48:43 +0800 Subject: [PATCH 8/8] Clear the execution queue of the thread pool in the easilog destructor --- include/easylog/easylog.h | 67 ++++++++++++----------------------- include/easylog/thread_pool.h | 23 +++++++----- 2 files changed, 37 insertions(+), 53 deletions(-) diff --git a/include/easylog/easylog.h b/include/easylog/easylog.h index 775cc2df9..e3635eec9 100644 --- a/include/easylog/easylog.h +++ b/include/easylog/easylog.h @@ -24,14 +24,6 @@ namespace easylog { -struct log : public std::enable_shared_from_this { - Severity min_severity; - bool enable_console = true; - bool enbale_async_write = false; - std::unique_ptr append = nullptr; - std::vector> appenders; -}; - template class logger { public: @@ -41,12 +33,8 @@ class logger { } void async_write(std::shared_ptr record) { - auto weak_ptr = log_->weak_from_this(); - thread_pool::instance().async([this, weak_ptr, record] { - auto strong_ptr = weak_ptr.lock(); - if (strong_ptr) { - write(*record); - } + thread_pool::instance().async([this, record] { + write(*record); return true; }); } @@ -64,47 +52,32 @@ class logger { } } - void flush() { - if (log_ && log_->append) { - log_->append->flush(); - } - } + void flush() { appender_->flush(); } void init(Severity min_severity, bool enable_console, const std::string &filename, size_t max_file_size, size_t max_files, bool flush_every_time, bool enable_async_write) { - log_ = std::make_shared(); - log_->append = std::make_unique(filename, max_file_size, - max_files, flush_every_time); - log_->min_severity = min_severity; - log_->enable_console = enable_console; - log_->enbale_async_write = enable_async_write; + appender_ = std::make_unique(filename, max_file_size, max_files, + flush_every_time); + min_severity_ = min_severity; + enable_console_ = enable_console; + enbale_async_write_ = enable_async_write; } - bool check_severity(Severity severity) { - if (log_) { - return severity >= log_->min_severity; - } - return false; - } + bool check_severity(Severity severity) { return severity >= min_severity_; } - bool check_async_write() { - if (log_) { - return log_->enbale_async_write; - } - return false; - } + bool check_async_write() { return enbale_async_write_; } void add_appender(std::function fn) { - if (log_) { - log_->appenders.emplace_back(std::move(fn)); - } + appenders_.emplace_back(std::move(fn)); } private: logger() = default; logger(const logger &) = default; + ~logger() { thread_pool::instance().quit(); } + template size_t get_time_str(char (&buf)[N], const auto &now) { const auto nowAsTimeT = std::chrono::system_clock::to_time_t(now); @@ -141,21 +114,25 @@ class logger { str.append(record.get_file_str()); str.append(record.get_message()).append("\n"); - if (log_->append) { - log_->append->write(str); + if (appender_) { + appender_->write(str); } - if (log_->enable_console) { + if (enable_console_) { std::cout << str; std::cout << std::flush; } - for (auto &fn : log_->appenders) { + for (auto &fn : appenders_) { fn(std::string_view(str)); } } - std::shared_ptr log_ = nullptr; + Severity min_severity_; + bool enable_console_ = true; + bool enbale_async_write_ = false; + std::unique_ptr appender_ = nullptr; + std::vector> appenders_; }; template diff --git a/include/easylog/thread_pool.h b/include/easylog/thread_pool.h index 96910cc6c..57acdcdb6 100644 --- a/include/easylog/thread_pool.h +++ b/include/easylog/thread_pool.h @@ -59,6 +59,11 @@ class blocking_queue { return queue_.size(); } + void clear() { + std::unique_lock lock(queue_mutex_); + queue_.clear(); + } + private: std::mutex queue_mutex_; std::condition_variable push_cv_; @@ -90,20 +95,15 @@ class thread_pool { } } - void async(Task &&task) { - queue_.enqueue(std::make_unique(std::move(task))); - } - - private: - thread_pool() = default; - - ~thread_pool() { + void quit() { std::lock_guard lock(mutex_); if (threads_.empty()) { return; } + queue_.clear(); + for (size_t i = 0; i < threads_.size(); i++) { async([]() { return false; @@ -117,6 +117,13 @@ class thread_pool { threads_.clear(); } + void async(Task &&task) { + queue_.enqueue(std::make_unique(std::move(task))); + } + + private: + thread_pool() = default; + blocking_queue queue_; std::vector threads_;