From 5092ab20c959525a55c4f2a8e74502e5e40d28e4 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 25 Jan 2013 15:50:08 -0800 Subject: [PATCH] Do logging on a background thread Makes logging actually thread-safe and slightly improves performance. --- aegisub/libaegisub/common/log.cpp | 29 ++++++++++++++++----- aegisub/libaegisub/include/libaegisub/log.h | 7 +++-- aegisub/src/dialog_log.cpp | 9 +++++-- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/aegisub/libaegisub/common/log.cpp b/aegisub/libaegisub/common/log.cpp index cd7d04407..80fbf2680 100644 --- a/aegisub/libaegisub/common/log.cpp +++ b/aegisub/libaegisub/common/log.cpp @@ -22,6 +22,7 @@ #include "libaegisub/cajun/elements.h" #include "libaegisub/cajun/writer.h" +#include "libaegisub/dispatch.h" #include "libaegisub/io.h" #include "libaegisub/util.h" @@ -31,6 +32,7 @@ #include #include #include +#include namespace agi { namespace log { @@ -42,30 +44,45 @@ LogSink *log; /// Keep this ordered the same as Severity const char *Severity_ID = "EAWID"; +LogSink::LogSink() +: messages(250) +, queue(dispatch::Create()) +{ } + LogSink::~LogSink() { // The destructor for emitters may try to log messages, so disable all the // emitters before destructing any std::vector emitters_temp; - swap(emitters_temp, emitters); + queue->Sync([&]{ swap(emitters_temp, emitters); }); util::delete_clear(emitters_temp); } void LogSink::Log(SinkMessage const& sm) { - messages.push_back(sm); - boost::for_each(emitters, [=](Emitter *em) { em->log(&messages.back()); }); + queue->Async([=]{ + messages.push_back(sm); + boost::for_each(emitters, [=](Emitter *em) { em->log(&messages.back()); }); + }); } void LogSink::Subscribe(Emitter *em) { LOG_D("agi/log/emitter/subscribe") << "Subscribe: " << this; - emitters.push_back(em); + queue->Sync([=] { emitters.push_back(em); }); } void LogSink::Unsubscribe(Emitter *em) { - emitters.erase(remove(emitters.begin(), emitters.end(), em), emitters.end()); - delete em; + queue->Sync([=] { + emitters.erase(remove(emitters.begin(), emitters.end(), em), emitters.end()); + delete em; + }); LOG_D("agi/log/emitter/unsubscribe") << "Un-Subscribe: " << this; } +decltype(LogSink::messages) LogSink::GetMessages() const { + decltype(LogSink::messages) ret; + queue->Sync([&] { ret = messages; }); + return ret; +} + Message::Message(const char *section, Severity severity, const char *file, const char *func, int line) : msg(nullptr, 1024) { diff --git a/aegisub/libaegisub/include/libaegisub/log.h b/aegisub/libaegisub/include/libaegisub/log.h index 2c0c6d0b6..061faf297 100644 --- a/aegisub/libaegisub/include/libaegisub/log.h +++ b/aegisub/libaegisub/include/libaegisub/log.h @@ -47,6 +47,8 @@ #define LOG_D_IF(cond, section) if (cond) LOG_SINK(section, agi::log::Debug) namespace agi { + namespace dispatch { class Queue; } + namespace log { class LogSink; @@ -82,12 +84,13 @@ class Emitter; /// Log sink, single destination for all messages class LogSink { boost::circular_buffer messages; + std::unique_ptr queue; /// List of pointers to emitters std::vector emitters; public: - LogSink() : messages(250) { } + LogSink(); ~LogSink(); /// Insert a message into the sink. @@ -105,7 +108,7 @@ public: /// @brief @get the complete (current) log. /// @return Const pointer to internal sink. - decltype(messages) const& GetSink() const { return messages; } + decltype(messages) GetMessages() const; }; /// An emitter to produce human readable output for a log sink. diff --git a/aegisub/src/dialog_log.cpp b/aegisub/src/dialog_log.cpp index 8c258645e..e9dee32a0 100644 --- a/aegisub/src/dialog_log.cpp +++ b/aegisub/src/dialog_log.cpp @@ -39,6 +39,7 @@ #include "compat.h" #include "include/aegisub/context.h" +#include #include #include @@ -57,7 +58,7 @@ public: EmitLog(wxTextCtrl *t) : text_ctrl(t) { - for (auto sm : agi::log::log->GetSink()) + for (auto sm : agi::log::log->GetMessages()) log(&sm); } @@ -86,7 +87,11 @@ public: sm->line, to_wx(sm->message)); #endif - text_ctrl->AppendText(log); + + if (wxIsMainThread()) + text_ctrl->AppendText(log); + else + agi::dispatch::Main().Async([=]{ text_ctrl->AppendText(log); }); } };