Chromium Code Reviews| Index: net/base/tcp_listen_socket.cc |
| =================================================================== |
| --- net/base/tcp_listen_socket.cc (revision 135347) |
| +++ net/base/tcp_listen_socket.cc (working copy) |
| @@ -17,6 +17,7 @@ |
| #include "net/base/net_errors.h" |
| #endif |
| +#include "base/bind.h" |
| #include "base/eintr_wrapper.h" |
| #include "base/sys_byteorder.h" |
| #include "base/threading/platform_thread.h" |
| @@ -25,6 +26,7 @@ |
| #if defined(OS_WIN) |
| typedef int socklen_t; |
| +#include "net/base/winsock_init.h" |
| #endif // defined(OS_WIN) |
| namespace net { |
| @@ -32,7 +34,34 @@ |
| namespace { |
| const int kReadBufSize = 4096; |
| +const int kMaxSendBufSize = 1024 * 1024 * 5; // 5MB |
| +const net::BackoffEntry::Policy kSendBackoffPolicy = { |
| + // Number of initial errors (in sequence) to ignore before applying |
| + // exponential back-off rules. |
| + 0, |
| + |
| + // Initial delay for exponential back-off in ms. |
| + 25, |
| + |
| + // Factor by which the waiting time will be multiplied. |
| + 2, |
| + |
| + // Fuzzing percentage. ex: 10% will spread requests randomly |
| + // between 90%-100% of the calculated time. |
| + 0, |
| + |
| + // Maximum amount of time we are willing to delay our request in ms. |
| + 100, |
| + |
| + // Time to keep an entry from being discarded even when it |
| + // has no significant state, -1 to never discard. |
| + -1, |
| + |
| + // Don't use initial delay unless the last request was an error. |
| + false, |
| +}; |
| + |
| } // namespace |
| #if defined(OS_WIN) |
| @@ -75,7 +104,10 @@ |
| : ListenSocket(del), |
| socket_(s), |
| reads_paused_(false), |
| - has_pending_reads_(false) { |
| + has_pending_reads_(false), |
| + send_pending_size_(0), |
| + send_error_(false), |
| + send_backoff_(&kSendBackoffPolicy) { |
| #if defined(OS_WIN) |
| socket_event_ = WSACreateEvent(); |
| // TODO(ibrar): error handling in case of socket_event_ == WSA_INVALID_EVENT |
| @@ -96,6 +128,10 @@ |
| } |
| SOCKET TCPListenSocket::CreateAndBind(const std::string& ip, int port) { |
| +#if defined(OS_WIN) |
| + EnsureWinsockInit(); |
| +#endif |
| + |
| SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
| if (s != kInvalidSocket) { |
| #if defined(OS_POSIX) |
| @@ -132,33 +168,28 @@ |
| } |
| void TCPListenSocket::SendInternal(const char* bytes, int len) { |
| - char* send_buf = const_cast<char *>(bytes); |
| - int len_left = len; |
| - while (true) { |
| - int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0)); |
| - if (sent == len_left) { // A shortcut to avoid extraneous checks. |
| - break; |
| - } |
| - if (sent == kSocketError) { |
| -#if defined(OS_WIN) |
| - if (WSAGetLastError() != WSAEWOULDBLOCK) { |
| - LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); |
| -#elif defined(OS_POSIX) |
| - if (errno != EWOULDBLOCK && errno != EAGAIN) { |
| - LOG(ERROR) << "send failed: errno==" << errno; |
| -#endif |
| - break; |
| - } |
| - // Otherwise we would block, and now we have to wait for a retry. |
| - // Fall through to PlatformThread::YieldCurrentThread() |
| - } else { |
| - // sent != len_left according to the shortcut above. |
| - // Shift the buffer start and send the remainder after a short while. |
| - send_buf += sent; |
| - len_left -= sent; |
| - } |
| - base::PlatformThread::YieldCurrentThread(); |
| + DCHECK(bytes); |
| + DCHECK_GT(len, 0); |
| + |
| + if (send_error_) |
| + return; |
| + |
| + if (send_pending_size_ + len > kMaxSendBufSize) { |
| + // Too much of a backup, stop trying to add more data. |
|
michaeln
2012/05/10 16:52:21
nit: given the log error message immediately below
Marshall
2012/05/10 19:01:09
Done.
|
| + LOG(ERROR) << "send failed: buffer overrun"; |
| + send_buffers_.clear(); |
| + send_pending_size_ = 0; |
| + send_error_ = true; |
| + return; |
| } |
| + |
| + scoped_refptr<IOBuffer> buffer(new IOBuffer(len)); |
| + memcpy(buffer->data(), bytes, len); |
| + send_buffers_.push_back(new DrainableIOBuffer(buffer, len)); |
| + send_pending_size_ += len; |
| + |
| + if (!send_timer_.IsRunning()) |
| + SendData(); |
| } |
| void TCPListenSocket::Listen() { |
| @@ -319,4 +350,67 @@ |
| #endif |
| +void TCPListenSocket::SendData() { |
| + // This method should never be called when the send buffer is empty. |
|
michaeln
2012/05/10 16:52:21
nit: the dcheck effectively says this
Marshall
2012/05/10 19:01:09
Done.
|
| + DCHECK(!send_buffers_.empty()); |
| + |
| + int total_sent = 0; |
| + |
| + // Send data until all buffers have been sent or a call would block. |
| + while (!send_buffers_.empty()) { |
| + scoped_refptr<DrainableIOBuffer> buffer = send_buffers_.front(); |
| + |
| + int len_left = buffer->BytesRemaining(); |
| + int sent = HANDLE_EINTR(send(socket_, buffer->data(), len_left, 0)); |
| + DCHECK(sent > 0 || sent == kSocketError); |
| + |
| + if (sent > 0) { |
| + if (sent == len_left) { |
| + // All data has been sent. Remove the buffer from the list and move on |
| + // to the next buffer, if any. |
| + send_buffers_.erase(send_buffers_.begin()); |
|
mmenke
2012/05/10 17:17:19
nit: send_buffers_.pop_front()
Marshall
2012/05/10 19:01:09
Done.
|
| + } else { |
| + // The buffer was partially sent. |
| + buffer->DidConsume(sent); |
|
michaeln
2012/05/10 16:52:21
style nit: would less text be more readable?
if (
Marshall
2012/05/10 19:01:09
Done.
|
| + } |
| + |
| + total_sent += sent; |
| + } else if (sent == kSocketError) { |
| +#if defined(OS_WIN) |
| + if (WSAGetLastError() != WSAEWOULDBLOCK) { |
| + LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); |
| +#elif defined(OS_POSIX) |
| + if (errno != EWOULDBLOCK && errno != EAGAIN) { |
| + LOG(ERROR) << "send failed: errno==" << errno; |
| +#endif |
| + // Don't try to re-send data after a socket error. |
| + send_buffers_.clear(); |
| + send_pending_size_ = 0; |
| + send_error_ = true; |
| + return; |
| + } |
| + |
| + // The call would block. Don't send any more data at this time. |
| + break; |
| + } |
| + } |
| + |
| + if (total_sent > 0) { |
| + send_pending_size_ -= total_sent; |
| + DCHECK_GE(send_pending_size_, 0); |
| + |
| + // Clear the back-off delay. |
| + send_backoff_.Reset(); |
| + } else { |
| + // Increase the back-off delay. |
| + send_backoff_.InformOfRequest(false); |
|
michaeln
2012/05/10 16:52:21
the comments about how these affect the back-off d
|
| + } |
| + |
| + if (!send_buffers_.empty()) { |
|
michaeln
2012/05/10 16:52:21
maybe DCHECK_GT(send_pending_size_, 0)
Marshall
2012/05/10 19:01:09
That's probably not necessary because we DCHECK at
|
| + DCHECK(!send_timer_.IsRunning()); |
| + send_timer_.Start(FROM_HERE, send_backoff_.GetTimeUntilRelease(), |
| + this, &TCPListenSocket::SendData); |
| + } |
| +} |
| + |
| } // namespace net |