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.
This commit is contained in:
Max Kellermann 2013-11-06 20:36:37 +01:00
parent 5b213b0504
commit 422b8472fe
2 changed files with 33 additions and 17 deletions

View File

@ -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();
}

View File

@ -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