From 422b8472fec9443250895a281b6b0a20190daa22 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 6 Nov 2013 20:36:37 +0100 Subject: event/FullyBufferedSocket: try to write without extra roundtrip Postpone the write using IdleMonitor instead of scheduling a write event. This reduces the number of system calls, because we don't need to register and unregister the write event in epoll. --- src/event/FullyBufferedSocket.cxx | 38 ++++++++++++++++++++++++-------------- src/event/FullyBufferedSocket.hxx | 12 +++++++++--- 2 files changed, 33 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx index 6a0df183d..3b8cf3765 100644 --- a/src/event/FullyBufferedSocket.cxx +++ b/src/event/FullyBufferedSocket.cxx @@ -42,7 +42,8 @@ FullyBufferedSocket::DirectWrite(const void *data, size_t length) if (IsSocketErrorAgain(code)) return 0; - Cancel(); + IdleMonitor::Cancel(); + BufferedSocket::Cancel(); if (IsSocketErrorClosed(code)) OnSocketClosed(); @@ -61,6 +62,7 @@ FullyBufferedSocket::Flush() size_t length; const void *data = output.Read(&length); if (data == nullptr) { + IdleMonitor::Cancel(); CancelWrite(); return true; } @@ -71,8 +73,10 @@ FullyBufferedSocket::Flush() output.Consume(nbytes); - if (output.IsEmpty()) + if (output.IsEmpty()) { + IdleMonitor::Cancel(); CancelWrite(); + } return true; } @@ -82,6 +86,9 @@ FullyBufferedSocket::Write(const void *data, size_t length) { assert(IsDefined()); + if (length == 0) + return true; + #if 0 /* TODO: disabled because this would add overhead on some callers (the ones that often), but it may be useful */ @@ -98,6 +105,8 @@ FullyBufferedSocket::Write(const void *data, size_t length) } #endif + const bool was_empty = output.IsEmpty(); + if (!output.Append(data, length)) { // TODO static constexpr Domain buffered_socket_domain("buffered_socket"); @@ -107,30 +116,31 @@ FullyBufferedSocket::Write(const void *data, size_t length) return false; } - ScheduleWrite(); + if (was_empty) + IdleMonitor::Schedule(); return true; } bool FullyBufferedSocket::OnSocketReady(unsigned flags) { - const bool was_empty = output.IsEmpty(); - if (!BufferedSocket::OnSocketReady(flags)) - return false; - - if (was_empty && !output.IsEmpty()) - /* just in case the OnSocketInput() method has added - data to the output buffer: try to send it now - instead of waiting for the next event loop - iteration */ - flags |= WRITE; - if (flags & WRITE) { assert(!output.IsEmpty()); + assert(!IdleMonitor::IsActive()); if (!Flush()) return false; } + if (!BufferedSocket::OnSocketReady(flags)) + return false; + return true; } + +void +FullyBufferedSocket::OnIdle() +{ + if (Flush() && !output.IsEmpty()) + ScheduleWrite(); +} diff --git a/src/event/FullyBufferedSocket.hxx b/src/event/FullyBufferedSocket.hxx index 90638e60b..c50bb5f61 100644 --- a/src/event/FullyBufferedSocket.hxx +++ b/src/event/FullyBufferedSocket.hxx @@ -22,24 +22,29 @@ #include "check.h" #include "BufferedSocket.hxx" +#include "IdleMonitor.hxx" #include "util/PeakBuffer.hxx" #include "Compiler.h" /** * A #BufferedSocket specialization that adds an output buffer. */ -class FullyBufferedSocket : protected BufferedSocket { +class FullyBufferedSocket : protected BufferedSocket, private IdleMonitor { PeakBuffer output; public: FullyBufferedSocket(int _fd, EventLoop &_loop, size_t normal_size, size_t peak_size=0) - :BufferedSocket(_fd, _loop), + :BufferedSocket(_fd, _loop), IdleMonitor(_loop), output(normal_size, peak_size) { } using BufferedSocket::IsDefined; - using BufferedSocket::Close; + + void Close() { + IdleMonitor::Cancel(); + BufferedSocket::Close(); + } private: ssize_t DirectWrite(const void *data, size_t length); @@ -58,6 +63,7 @@ protected: bool Write(const void *data, size_t length); virtual bool OnSocketReady(unsigned flags) override; + virtual void OnIdle() override; }; #endif -- cgit v1.2.3