Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Unified Diff: net/base/tcp_listen_socket.cc

Issue 10389007: Change TCPListenSocket::SendInternal to use a non-blocking implementation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 {
@@ -75,7 +77,9 @@
: ListenSocket(del),
socket_(s),
reads_paused_(false),
- has_pending_reads_(false) {
+ has_pending_reads_(false),
+ has_pending_writes_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
#if defined(OS_WIN)
socket_event_ = WSACreateEvent();
// TODO(ibrar): error handling in case of socket_event_ == WSA_INVALID_EVENT
@@ -96,6 +100,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,13 +140,25 @@
}
void TCPListenSocket::SendInternal(const char* bytes, int len) {
michaeln 2012/05/08 18:17:12 Are there any consumers of this class that could b
Marshall 2012/05/08 20:27:07 From what I can tell TCPListenSocket is only used
mmenke 2012/05/08 20:51:43 It's also used by chrome_frame/test/test_server
- 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 (bytes == NULL && len == 0) {
+ // Executing the asynchronous send callback. Clear the pending flag.
+ DCHECK(has_pending_writes_);
+ has_pending_writes_ = false;
+
+ // Another send call may have already emptied the buffer.
+ if (send_data_.empty())
+ return;
+ }
+
+ if (send_data_.empty()) {
+ // Nothing in the buffer currently. Try sending the new data without adding
+ // to the buffer.
+ DCHECK(bytes);
+ DCHECK_GT(len, 0);
+ int sent = HANDLE_EINTR(send(socket_, bytes, len, 0));
+ if (sent == len) // All data has been sent.
+ return;
+
if (sent == kSocketError) {
#if defined(OS_WIN)
if (WSAGetLastError() != WSAEWOULDBLOCK) {
@@ -147,18 +167,68 @@
if (errno != EWOULDBLOCK && errno != EAGAIN) {
LOG(ERROR) << "send failed: errno==" << errno;
#endif
- break;
+ // Don't try to re-send data after a socket error.
+ return;
}
- // Otherwise we would block, and now we have to wait for a retry.
- // Fall through to PlatformThread::YieldCurrentThread()
+ }
+
+ if (sent > 0) {
+ // Add the remaining bytes to the send buffer.
+ send_data_.append(bytes + sent, len - sent);
mmenke 2012/05/08 15:51:57 nit: Since send_data_ is empty, suggest you just
Marshall 2012/05/08 20:27:07 Done.
} 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;
+ // Add all bytes to the send buffer.
+ send_data_.append(bytes, len);
}
- base::PlatformThread::YieldCurrentThread();
+
+ if (!has_pending_writes_) {
+ // Fire a new asychronous continuation callback.
+ has_pending_writes_ = true;
+ MessageLoop::current()->PostTask(FROM_HERE,
michaeln 2012/05/08 18:17:12 I would expect this to spin pretty hard when netwo
Marshall 2012/05/08 20:27:07 I've implemented an exponential back-off using net
+ base::Bind(&TCPListenSocket::SendInternal, weak_factory_.GetWeakPtr(),
+ static_cast<const char*>(NULL), 0));
mmenke 2012/05/08 15:51:57 I think you should at least use a different functi
Marshall 2012/05/08 20:27:07 Done.
+ }
+ return;
}
mmenke 2012/05/08 15:51:57 I wonder if we really need two copies of the send
Marshall 2012/05/08 20:27:07 Done.
+
+ if (bytes != NULL && len > 0) {
+ // Add the bytes to the end of the send data buffer.
+ send_data_.append(bytes, len);
michaeln 2012/05/08 18:17:12 What if no progress is made for a very long time,
Marshall 2012/05/08 20:27:07 I've implemented a maximum buffer size of 5MB. In
mmenke 2012/05/08 20:51:43 In the medium to long term, we do intend to eventu
mmenke 2012/05/08 20:51:43 "Tends to?" How strong of an assurance do we have
Marshall 2012/05/08 21:04:57 Not a very strong assurance at all, at least with
+ }
+
+ int len_left = static_cast<int>(send_data_.length());
+ int sent = HANDLE_EINTR(send(socket_, send_data_.c_str(), len_left, 0));
+ if (sent == len_left) {
+ // All data has been sent.
+ send_data_.clear();
+ return;
+ }
+
+ 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_data_.clear();
+ return;
+ }
+ }
+
+ if (sent > 0) {
+ // Remove the sent bytes from the buffer.
+ send_data_ = send_data_.substr(sent, len_left - sent);
mmenke 2012/05/08 17:08:46 This is so ugly...you may want to use a scoped_arr
Marshall 2012/05/08 20:27:07 The buffer can grow to > 1MB in size. Do we want t
mmenke 2012/05/08 20:51:43 I think that's reasonable. The one problem with a
Marshall 2012/05/08 21:04:57 Are there any currently existing ring buffer imple
mmenke 2012/05/08 21:21:35 I don't believe so. Our main socket class (TcpCli
Marshall 2012/05/08 21:53:44 Keeping a list of send buffers sounds like a good
+ }
+
+ if (!has_pending_writes_) {
mmenke 2012/05/08 15:51:57 May be a little simpler to use a OneShotTimer, rat
Marshall 2012/05/08 20:27:07 Done.
+ // Fire a new asychronous continuation callback.
+ has_pending_writes_ = true;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&TCPListenSocket::SendInternal, weak_factory_.GetWeakPtr(),
+ static_cast<const char*>(NULL), 0));
+ }
}
void TCPListenSocket::Listen() {

Powered by Google App Engine
This is Rietveld 408576698