Skip to content

Commit

Permalink
Refactor|Log|libdeng2: Keep a pool of shared log entry argument insta…
Browse files Browse the repository at this point in the history
…nces

To avoid repeatedly allocating and freeing memory for log entry
arguments, the existing ones are now kept in a shared pool, where
their values will simply be reset to new ones when taken into use
in new log entries.

Note that deep copying of string arguments still occurs.
  • Loading branch information
skyjake committed Jan 9, 2014
1 parent 916647c commit b6ee288
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 44 deletions.
50 changes: 32 additions & 18 deletions doomsday/libdeng2/include/de/core/log.h
Expand Up @@ -447,23 +447,28 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
};

public:
Arg() : _type(IntegerArgument) { _data.intValue = 0; }
Arg(dint i) : _type(IntegerArgument) { _data.intValue = i; }
Arg(duint i) : _type(IntegerArgument) { _data.intValue = i; }
Arg(long int i) : _type(IntegerArgument) { _data.intValue = i; }
Arg(long unsigned int i) : _type(IntegerArgument) { _data.intValue = i; }
Arg(duint64 i) : _type(IntegerArgument) { _data.intValue = dint64(i); }
Arg(dint64 i) : _type(IntegerArgument) { _data.intValue = i; }
Arg(ddouble d) : _type(FloatingPointArgument) { _data.floatValue = d; }
Arg(void const *p) : _type(IntegerArgument) { _data.intValue = dint64(p); }
Arg(char const *s) : _type(StringArgument) { _data.stringValue = new String(s); }
Arg(String const &s) : _type(StringArgument) { _data.stringValue = new String(s.data(), s.size()); }

Arg(Base const &arg);
Arg(Arg const &other);

Arg();
~Arg();

void clear();

void setValue(dint i);
void setValue(duint i);
void setValue(long int i);
void setValue(long unsigned int i);
void setValue(duint64 i);
void setValue(dint64 i);
void setValue(ddouble d);
void setValue(void const *p);
void setValue(char const *s);
void setValue(String const &s);
void setValue(Base const &arg);

template <typename ValueType>
Arg &set(ValueType const &s) { setValue(s); return *this; }

Arg &operator = (Arg const &other);

inline Type type() const { return _type; }
inline dint64 intValue() const {
DENG2_ASSERT(_type == IntegerArgument);
Expand All @@ -486,6 +491,15 @@ class DENG2_PUBLIC LogEntry : public Lockable, public ISerializable
void operator >> (Writer &to) const;
void operator << (Reader &from);

public:
static Arg *newFromPool();
static void returnToPool(Arg *arg);

template <typename ValueType>
static inline Arg *newFromPool(ValueType const &v) {
return &(newFromPool()->set(v));
}

private:
Type _type;
union Data {
Expand Down Expand Up @@ -702,16 +716,16 @@ class DENG2_PUBLIC LogEntryStager
/// Appends a new argument to the entry.
template <typename ValueType>
inline LogEntryStager &operator << (ValueType const &v) {
// Args are created only if the level is enabled.
if(!_disabled) {
// Args are created only if the level is enabled.
_args.append(new LogEntry::Arg(v));
_args << LogEntry::Arg::newFromPool(v);
}
return *this;
}

~LogEntryStager() {
if(!_disabled) {
// Ownership of the entries is transferred to the LogEntry.
// Ownership of the args is transferred to the LogEntry.
LOG().enter(_metadata, _format, _args);
}
}
Expand Down
150 changes: 125 additions & 25 deletions doomsday/libdeng2/src/core/log.cpp
Expand Up @@ -24,6 +24,7 @@
#include "de/Guard"
#include "de/Reader"
#include "de/Writer"
#include "de/FIFO"
#include "logtextstyle.h"

#include <QMap>
Expand Down Expand Up @@ -64,50 +65,133 @@ class Logs : public QMap<QThread *, Log *>, public Lockable
/// The logs table contains the log of each thread that uses logging.
static std::auto_ptr<internal::Logs> logsPtr;

LogEntry::Arg::Arg(const LogEntry::Arg::Base &arg) : _type(arg.logEntryArgType())
/// Unused entry arguments are stored here in the pool.
static FIFO<LogEntry::Arg> argPool;

LogEntry::Arg::Arg() : _type(IntegerArgument)
{
switch(_type)
{
case IntegerArgument:
_data.intValue = arg.asInt64();
break;
_data.intValue = 0;
}

case FloatingPointArgument:
_data.floatValue = arg.asDouble();
break;
LogEntry::Arg::~Arg()
{
clear();
}

case StringArgument: {
String s = arg.asText();
_data.stringValue = new String(s.data(), s.size());
break; }
void LogEntry::Arg::clear()
{
if(_type == StringArgument)
{
delete _data.stringValue;
_data.stringValue = 0;
_type = IntegerArgument;
}
}

LogEntry::Arg::Arg(Arg const &other)
: String::IPatternArg(), ISerializable(), _type(other._type)
void LogEntry::Arg::setValue(dint i)
{
clear();
_type = IntegerArgument;
_data.intValue = i;
}

void LogEntry::Arg::setValue(duint i)
{
clear();
_type = IntegerArgument;
_data.intValue = i;
}

void LogEntry::Arg::setValue(long int i)
{
clear();
_type = IntegerArgument;
_data.intValue = i;
}

void LogEntry::Arg::setValue(long unsigned int i)
{
clear();
_type = IntegerArgument;
_data.intValue = i;
}

void LogEntry::Arg::setValue(duint64 i)
{
clear();
_type = IntegerArgument;
_data.intValue = dint64(i);
}

void LogEntry::Arg::setValue(dint64 i)
{
clear();
_type = IntegerArgument;
_data.intValue = i;
}

void LogEntry::Arg::setValue(ddouble d)
{
clear();
_type = FloatingPointArgument;
_data.floatValue = d;
}

void LogEntry::Arg::setValue(void const *p)
{
clear();
_type = IntegerArgument;
_data.intValue = dint64(p);
}

void LogEntry::Arg::setValue(char const *s)
{
clear();
_type = StringArgument;
_data.stringValue = new String(s);
}

void LogEntry::Arg::setValue(String const &s)
{
clear();
_type = StringArgument;
// Ensure a deep copy of the string is taken.
_data.stringValue = new String(s.data(), s.size());
}

void LogEntry::Arg::setValue(LogEntry::Arg::Base const &arg)
{
switch(other._type)
clear();
_type = arg.logEntryArgType();
switch(_type)
{
case IntegerArgument:
_data.intValue = other._data.intValue;
_data.intValue = arg.asInt64();
break;

case FloatingPointArgument:
_data.floatValue = other._data.floatValue;
_data.floatValue = arg.asDouble();
break;

case StringArgument:
_data.stringValue = new String(*other._data.stringValue);
setValue(arg.asText()); // deep copy
break;
}
}

LogEntry::Arg::~Arg()
LogEntry::Arg &LogEntry::Arg::operator = (Arg const &other)
{
if(_type == StringArgument)
clear();
if(other._type == StringArgument)
{
delete _data.stringValue;
setValue(*other._data.stringValue); // deep copy
}
else
{
_type = other._type;
_data = other._data;
}
return *this;
}

ddouble LogEntry::Arg::asNumber() const
Expand Down Expand Up @@ -183,6 +267,20 @@ void LogEntry::Arg::operator << (Reader &from)
}
}

LogEntry::Arg *LogEntry::Arg::newFromPool()
{
Arg *arg = argPool.take();
if(arg) return arg;
// Need a new one.
return new LogEntry::Arg;
}

void LogEntry::Arg::returnToPool(Arg *arg)
{
arg->clear();
argPool.put(arg);
}

LogEntry::LogEntry() : _metadata(0), _sectionDepth(0), _disabled(true)
{}

Expand Down Expand Up @@ -213,18 +311,20 @@ LogEntry::LogEntry(LogEntry const &other, Flags extraFlags)
{
DENG2_FOR_EACH_CONST(Args, i, other._args)
{
_args.append(new Arg(**i));
Arg *a = Arg::newFromPool();
*a = **i;
_args.append(a);
}
}

LogEntry::~LogEntry()
{
DENG2_GUARD(this);

// The entry has ownership of its args.
// Put the arguments back to the shared pool.
for(Args::iterator i = _args.begin(); i != _args.end(); ++i)
{
delete *i;
Arg::returnToPool(*i);
}
}

Expand Down
2 changes: 1 addition & 1 deletion doomsday/tools/shell/shell-gui/src/linkwindow.cpp
Expand Up @@ -480,7 +480,7 @@ void LinkWindow::sendCommandToServer(de::String command)
{
// Echo the command locally.
LogEntry *e = new LogEntry(LogEntry::Generic | LogEntry::Note, "", 0, ">",
LogEntry::Args() << new LogEntry::Arg(command));
LogEntry::Args() << LogEntry::Arg::newFromPool(command));
d->logBuffer.add(e);

QScopedPointer<Packet> packet(d->link->protocol().newCommand(command));
Expand Down

0 comments on commit b6ee288

Please sign in to comment.