Do logging on a background thread

Makes logging actually thread-safe and slightly improves performance.
This commit is contained in:
Thomas Goyne 2013-01-25 15:50:08 -08:00
parent 2e99223977
commit 5092ab20c9
3 changed files with 35 additions and 10 deletions

View file

@ -22,6 +22,7 @@
#include "libaegisub/cajun/elements.h" #include "libaegisub/cajun/elements.h"
#include "libaegisub/cajun/writer.h" #include "libaegisub/cajun/writer.h"
#include "libaegisub/dispatch.h"
#include "libaegisub/io.h" #include "libaegisub/io.h"
#include "libaegisub/util.h" #include "libaegisub/util.h"
@ -31,6 +32,7 @@
#include <boost/range/algorithm.hpp> #include <boost/range/algorithm.hpp>
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <mutex>
namespace agi { namespace agi {
namespace log { namespace log {
@ -42,30 +44,45 @@ LogSink *log;
/// Keep this ordered the same as Severity /// Keep this ordered the same as Severity
const char *Severity_ID = "EAWID"; const char *Severity_ID = "EAWID";
LogSink::LogSink()
: messages(250)
, queue(dispatch::Create())
{ }
LogSink::~LogSink() { LogSink::~LogSink() {
// The destructor for emitters may try to log messages, so disable all the // The destructor for emitters may try to log messages, so disable all the
// emitters before destructing any // emitters before destructing any
std::vector<Emitter*> emitters_temp; std::vector<Emitter*> emitters_temp;
swap(emitters_temp, emitters); queue->Sync([&]{ swap(emitters_temp, emitters); });
util::delete_clear(emitters_temp); util::delete_clear(emitters_temp);
} }
void LogSink::Log(SinkMessage const& sm) { void LogSink::Log(SinkMessage const& sm) {
messages.push_back(sm); queue->Async([=]{
boost::for_each(emitters, [=](Emitter *em) { em->log(&messages.back()); }); messages.push_back(sm);
boost::for_each(emitters, [=](Emitter *em) { em->log(&messages.back()); });
});
} }
void LogSink::Subscribe(Emitter *em) { void LogSink::Subscribe(Emitter *em) {
LOG_D("agi/log/emitter/subscribe") << "Subscribe: " << this; LOG_D("agi/log/emitter/subscribe") << "Subscribe: " << this;
emitters.push_back(em); queue->Sync([=] { emitters.push_back(em); });
} }
void LogSink::Unsubscribe(Emitter *em) { void LogSink::Unsubscribe(Emitter *em) {
emitters.erase(remove(emitters.begin(), emitters.end(), em), emitters.end()); queue->Sync([=] {
delete em; emitters.erase(remove(emitters.begin(), emitters.end(), em), emitters.end());
delete em;
});
LOG_D("agi/log/emitter/unsubscribe") << "Un-Subscribe: " << this; 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) Message::Message(const char *section, Severity severity, const char *file, const char *func, int line)
: msg(nullptr, 1024) : msg(nullptr, 1024)
{ {

View file

@ -47,6 +47,8 @@
#define LOG_D_IF(cond, section) if (cond) LOG_SINK(section, agi::log::Debug) #define LOG_D_IF(cond, section) if (cond) LOG_SINK(section, agi::log::Debug)
namespace agi { namespace agi {
namespace dispatch { class Queue; }
namespace log { namespace log {
class LogSink; class LogSink;
@ -82,12 +84,13 @@ class Emitter;
/// Log sink, single destination for all messages /// Log sink, single destination for all messages
class LogSink { class LogSink {
boost::circular_buffer<SinkMessage> messages; boost::circular_buffer<SinkMessage> messages;
std::unique_ptr<dispatch::Queue> queue;
/// List of pointers to emitters /// List of pointers to emitters
std::vector<Emitter*> emitters; std::vector<Emitter*> emitters;
public: public:
LogSink() : messages(250) { } LogSink();
~LogSink(); ~LogSink();
/// Insert a message into the sink. /// Insert a message into the sink.
@ -105,7 +108,7 @@ public:
/// @brief @get the complete (current) log. /// @brief @get the complete (current) log.
/// @return Const pointer to internal sink. /// @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. /// An emitter to produce human readable output for a log sink.

View file

@ -39,6 +39,7 @@
#include "compat.h" #include "compat.h"
#include "include/aegisub/context.h" #include "include/aegisub/context.h"
#include <libaegisub/dispatch.h>
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include <algorithm> #include <algorithm>
@ -57,7 +58,7 @@ public:
EmitLog(wxTextCtrl *t) EmitLog(wxTextCtrl *t)
: text_ctrl(t) : text_ctrl(t)
{ {
for (auto sm : agi::log::log->GetSink()) for (auto sm : agi::log::log->GetMessages())
log(&sm); log(&sm);
} }
@ -86,7 +87,11 @@ public:
sm->line, sm->line,
to_wx(sm->message)); to_wx(sm->message));
#endif #endif
text_ctrl->AppendText(log);
if (wxIsMainThread())
text_ctrl->AppendText(log);
else
agi::dispatch::Main().Async([=]{ text_ctrl->AppendText(log); });
} }
}; };