event/DeferredMonitor: make fully thread-safe

Instead of creating a new eventfd for each DeferredMonitor instance,
reuse EventLoop's eventfd, and add a std::list to EventLoop that
manages the list of pending DeferredMonitors.  This std::list is
protected by the same mutex as the "calls" list.

The bottom line is: reduced overhead because the per-instance eventfd
was eliminated, slightly added overhead due to Mutex usage (but
negligible), and we're thread-safe now.

This subsystem is now good enough to replace EventLoop::AddCall().
This commit is contained in:
Max Kellermann
2014-01-04 14:56:02 +01:00
parent 48c96bbaea
commit a357d84dce
4 changed files with 77 additions and 54 deletions

View File

@@ -26,6 +26,7 @@
#include "TimeoutMonitor.hxx"
#include "SocketMonitor.hxx"
#include "IdleMonitor.hxx"
#include "DeferredMonitor.hxx"
#include <algorithm>
@@ -204,6 +205,44 @@ EventLoop::AddCall(std::function<void()> &&f)
wake_fd.Write();
}
void
EventLoop::AddDeferred(DeferredMonitor &d)
{
mutex.lock();
if (d.pending) {
mutex.unlock();
return;
}
assert(std::find(deferred.begin(),
deferred.end(), &d) == deferred.end());
d.pending = true;
deferred.push_back(&d);
mutex.unlock();
wake_fd.Write();
}
void
EventLoop::RemoveDeferred(DeferredMonitor &d)
{
const ScopeLock protect(mutex);
if (!d.pending) {
assert(std::find(deferred.begin(),
deferred.end(), &d) == deferred.end());
return;
}
d.pending = false;
auto i = std::find(deferred.begin(), deferred.end(), &d);
assert(i != deferred.end());
deferred.erase(i);
}
bool
EventLoop::OnSocketReady(gcc_unused unsigned flags)
{
@@ -213,6 +252,18 @@ EventLoop::OnSocketReady(gcc_unused unsigned flags)
mutex.lock();
while (!deferred.empty() && !quit) {
DeferredMonitor &m = *deferred.front();
assert(m.pending);
deferred.pop_front();
m.pending = false;
mutex.unlock();
m.RunDeferred();
mutex.lock();
}
while (!calls.empty() && !quit) {
auto f = std::move(calls.front());
calls.pop_front();