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() { |