Skip to content
This repository has been archived by the owner on Dec 28, 2021. It is now read-only.

Commit

Permalink
licq: Drop log messages instead of deadlocking
Browse files Browse the repository at this point in the history
If something starts flooding log messages faster than a sink can
process them, the pipe in PluginLogSink will fill up and cause the
sender to block. The pipe is written while mutexes are held, this will
cause a deadlock as the log sinks need to take the same mutex to read
the pipe.

This deadlock could also be resolved by releasing the mutex in
PluginLogSink before accessing the pipe. However, the caller
(LogDistributor) also has a mutex for all the sinks which could also
deadlock if another source tries to log something at the same time, and
even if that is solved the second source would just hang on writing to
the pipe.
  • Loading branch information
flynd committed Jan 12, 2013
1 parent 12c8f4b commit fd62d36
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 8 deletions.
13 changes: 10 additions & 3 deletions licq/include/licq/pipe.h
@@ -1,6 +1,6 @@
/*
* This file is part of Licq, an instant messaging client for UNIX.
* Copyright (C) 2010 Licq developers
* Copyright (C) 2010-2012 Licq developers <licq-dev@googlegroups.com>
*
* Licq is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -45,6 +45,13 @@ class Pipe
*/
ssize_t write(const void* buf, size_t count);

/**
* (Un)block the write end of the pipe
*
* @param block False to make write fail if it would block
*/
void setWriteBlocking(bool block);

/**
* Reads one byte using read().
*/
Expand All @@ -58,8 +65,8 @@ class Pipe
/**
* Writes one byte using write().
*/
void putChar(char ch)
{ write(&ch, sizeof(ch)); }
bool putChar(char ch)
{ return (write(&ch, sizeof(ch)) > 0); }

int getReadFd() const { return myFds[0]; }
int getWriteFd() const { return myFds[1]; }
Expand Down
16 changes: 12 additions & 4 deletions licq/src/logging/pluginlogsink.cpp
@@ -1,6 +1,6 @@
/*
* This file is part of Licq, an instant messaging client for UNIX.
* Copyright (C) 2010 Licq Developers <licq-dev@googlegroups.com>
* Copyright (C) 2010-2012 Licq developers <licq-dev@googlegroups.com>
*
* Please refer to the COPYRIGHT file distributed with this source
* distribution for the names of the individual contributors.
Expand Down Expand Up @@ -37,8 +37,12 @@ class PluginLogSink::Private : public LicqDaemon::AdjustableLogSink
void log(Message::Ptr message)
{
MutexLocker locker(myMutex);
myMessages.push_back(message);
myPipe.putChar('M');

// The pipe is non-blocking so we won't risk hanging here while holding
// several mutexes. If putChar fails, don't push this message, the receiver
// is either overloaded or hanged and it's just a log message anyway.
if (myPipe.putChar('M'))
myMessages.push_back(message);
}

LogSink::Message::Ptr popMessage(bool readPipe)
Expand All @@ -62,7 +66,11 @@ class PluginLogSink::Private : public LicqDaemon::AdjustableLogSink
PluginLogSink::PluginLogSink() :
myPrivate(new Private())
{
// Empty
LICQ_D();

// Make the pipe non-blocking. If a logsink is too slow it's better to drop a
// few log messages than to block and risk deadlocking the application.
d->myPipe.setWriteBlocking(false);
}

PluginLogSink::~PluginLogSink()
Expand Down
10 changes: 9 additions & 1 deletion licq/src/utils/pipe.cpp
@@ -1,6 +1,6 @@
/*
* This file is part of Licq, an instant messaging client for UNIX.
* Copyright (C) 2010 Licq developers
* Copyright (C) 2010-2012 Licq developers <licq-dev@googlegroups.com>
*
* Licq is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -19,6 +19,8 @@

#include <licq/pipe.h>

#include <fcntl.h>

using namespace Licq;

Pipe::Pipe()
Expand All @@ -41,3 +43,9 @@ ssize_t Pipe::write(const void* buf, size_t count)
{
return ::write(getWriteFd(), buf, count);
}

void Pipe::setWriteBlocking(bool block)
{
int f = ::fcntl(getWriteFd(), F_GETFL);
::fcntl(getWriteFd(), F_SETFL, (block ? (f & ~O_NONBLOCK) : (f | O_NONBLOCK)));
}

0 comments on commit fd62d36

Please sign in to comment.