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

Side by Side Diff: net/socket/tcp_client_socket_win.cc

Issue 10916016: Switch the TCP reads on Windows to use non-blocking/non-async I/O. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 8 years, 2 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/socket/tcp_client_socket_win.h" 5 #include "net/socket/tcp_client_socket_win.h"
6 6
7 #include <mstcpip.h> 7 #include <mstcpip.h>
8 8
9 #include "base/basictypes.h" 9 #include "base/basictypes.h"
10 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 181
182 // The TCPClientSocketWin is going away. 182 // The TCPClientSocketWin is going away.
183 void Detach() { socket_ = NULL; } 183 void Detach() { socket_ = NULL; }
184 184
185 // The separate OVERLAPPED variables for asynchronous operation. 185 // The separate OVERLAPPED variables for asynchronous operation.
186 // |read_overlapped_| is used for both Connect() and Read(). 186 // |read_overlapped_| is used for both Connect() and Read().
187 // |write_overlapped_| is only used for Write(); 187 // |write_overlapped_| is only used for Write();
188 OVERLAPPED read_overlapped_; 188 OVERLAPPED read_overlapped_;
189 OVERLAPPED write_overlapped_; 189 OVERLAPPED write_overlapped_;
190 190
191 // Flag indicating if we are in the experiment that enables non-blocking read
192 static bool disable_overlapped_reads_;
Ryan Sleevi 2012/10/11 18:53:16 No need to make this a class static. Just have it
193
191 // The buffers used in Read() and Write(). 194 // The buffers used in Read() and Write().
192 scoped_refptr<IOBuffer> read_iobuffer_; 195 scoped_refptr<IOBuffer> read_iobuffer_;
193 scoped_refptr<IOBuffer> write_iobuffer_; 196 scoped_refptr<IOBuffer> write_iobuffer_;
197 int read_buffer_length_;
194 int write_buffer_length_; 198 int write_buffer_length_;
195 199
196 // Throttle the read size based on our current slow start state. 200 // Throttle the read size based on our current slow start state.
197 // Returns the throttled read size. 201 // Returns the throttled read size.
198 int ThrottleReadSize(int size) { 202 int ThrottleReadSize(int size) {
199 if (slow_start_throttle_ < kMaxSlowStartThrottle) { 203 if (slow_start_throttle_ < kMaxSlowStartThrottle) {
200 size = std::min(size, slow_start_throttle_); 204 size = std::min(size, slow_start_throttle_);
201 slow_start_throttle_ *= 2; 205 slow_start_throttle_ *= 2;
202 } 206 }
203 return size; 207 return size;
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 // When doing reads from the socket, we try to mirror TCP's slow start. 252 // When doing reads from the socket, we try to mirror TCP's slow start.
249 // We do this because otherwise the async IO subsystem artifically delays 253 // We do this because otherwise the async IO subsystem artifically delays
250 // returning data to the application. 254 // returning data to the application.
251 static const int kInitialSlowStartThrottle = 1 * 1024; 255 static const int kInitialSlowStartThrottle = 1 * 1024;
252 static const int kMaxSlowStartThrottle = 32 * kInitialSlowStartThrottle; 256 static const int kMaxSlowStartThrottle = 32 * kInitialSlowStartThrottle;
253 int slow_start_throttle_; 257 int slow_start_throttle_;
254 258
255 DISALLOW_COPY_AND_ASSIGN(Core); 259 DISALLOW_COPY_AND_ASSIGN(Core);
256 }; 260 };
257 261
262 bool TCPClientSocketWin::Core::disable_overlapped_reads_ = false;
263
258 TCPClientSocketWin::Core::Core( 264 TCPClientSocketWin::Core::Core(
259 TCPClientSocketWin* socket) 265 TCPClientSocketWin* socket)
260 : write_buffer_length_(0), 266 : write_buffer_length_(0),
267 read_buffer_length_(0),
261 socket_(socket), 268 socket_(socket),
262 ALLOW_THIS_IN_INITIALIZER_LIST(reader_(this)), 269 ALLOW_THIS_IN_INITIALIZER_LIST(reader_(this)),
263 ALLOW_THIS_IN_INITIALIZER_LIST(writer_(this)), 270 ALLOW_THIS_IN_INITIALIZER_LIST(writer_(this)),
264 slow_start_throttle_(kInitialSlowStartThrottle) { 271 slow_start_throttle_(kInitialSlowStartThrottle) {
265 memset(&read_overlapped_, 0, sizeof(read_overlapped_)); 272 memset(&read_overlapped_, 0, sizeof(read_overlapped_));
266 memset(&write_overlapped_, 0, sizeof(write_overlapped_)); 273 memset(&write_overlapped_, 0, sizeof(write_overlapped_));
267 274
268 read_overlapped_.hEvent = WSACreateEvent(); 275 read_overlapped_.hEvent = WSACreateEvent();
269 write_overlapped_.hEvent = WSACreateEvent(); 276 write_overlapped_.hEvent = WSACreateEvent();
270 } 277 }
(...skipping 23 matching lines...) Expand all
294 write_watcher_.StartWatching(write_overlapped_.hEvent, &writer_); 301 write_watcher_.StartWatching(write_overlapped_.hEvent, &writer_);
295 } 302 }
296 303
297 void TCPClientSocketWin::Core::ReadDelegate::OnObjectSignaled( 304 void TCPClientSocketWin::Core::ReadDelegate::OnObjectSignaled(
298 HANDLE object) { 305 HANDLE object) {
299 DCHECK_EQ(object, core_->read_overlapped_.hEvent); 306 DCHECK_EQ(object, core_->read_overlapped_.hEvent);
300 if (core_->socket_) { 307 if (core_->socket_) {
301 if (core_->socket_->waiting_connect()) { 308 if (core_->socket_->waiting_connect()) {
302 core_->socket_->DidCompleteConnect(); 309 core_->socket_->DidCompleteConnect();
303 } else { 310 } else {
304 core_->socket_->DidCompleteRead(); 311 if (disable_overlapped_reads_) {
312 core_->socket_->DidSignalRead();
313 } else {
314 core_->socket_->DidCompleteRead();
315 }
305 } 316 }
306 } 317 }
307 318
308 core_->Release(); 319 core_->Release();
309 } 320 }
310 321
311 void TCPClientSocketWin::Core::WriteDelegate::OnObjectSignaled( 322 void TCPClientSocketWin::Core::WriteDelegate::OnObjectSignaled(
312 HANDLE object) { 323 HANDLE object) {
313 DCHECK_EQ(object, core_->write_overlapped_.hEvent); 324 DCHECK_EQ(object, core_->write_overlapped_.hEvent);
314 if (core_->socket_) 325 if (core_->socket_)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 DCHECK_EQ(socket_, INVALID_SOCKET); 358 DCHECK_EQ(socket_, INVALID_SOCKET);
348 359
349 int error = SetupSocket(socket); 360 int error = SetupSocket(socket);
350 if (error) 361 if (error)
351 return MapSystemError(error); 362 return MapSystemError(error);
352 363
353 socket_ = socket; 364 socket_ = socket;
354 SetNonBlocking(socket_); 365 SetNonBlocking(socket_);
355 366
356 core_ = new Core(this); 367 core_ = new Core(this);
368 if (core_->disable_overlapped_reads_) {
369 WSAEventSelect(socket_, core_->read_overlapped_.hEvent,
370 FD_READ | FD_CLOSE);
371 }
357 372
358 current_address_index_ = 0; 373 current_address_index_ = 0;
359 use_history_.set_was_ever_connected(); 374 use_history_.set_was_ever_connected();
360 375
361 return OK; 376 return OK;
362 } 377 }
363 378
364 int TCPClientSocketWin::Bind(const IPEndPoint& address) { 379 int TCPClientSocketWin::Bind(const IPEndPoint& address) {
365 if (current_address_index_ >= 0 || bind_address_.get()) { 380 if (current_address_index_ >= 0 || bind_address_.get()) {
366 // Cannot bind the socket if we are already connected or connecting. 381 // Cannot bind the socket if we are already connected or connecting.
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 return ERR_INVALID_ARGUMENT; 493 return ERR_INVALID_ARGUMENT;
479 if (bind(socket_, storage.addr, storage.addr_len)) 494 if (bind(socket_, storage.addr, storage.addr_len))
480 return MapSystemError(errno); 495 return MapSystemError(errno);
481 } 496 }
482 } 497 }
483 498
484 DCHECK(!core_); 499 DCHECK(!core_);
485 core_ = new Core(this); 500 core_ = new Core(this);
486 // WSAEventSelect sets the socket to non-blocking mode as a side effect. 501 // WSAEventSelect sets the socket to non-blocking mode as a side effect.
487 // Our connect() and recv() calls require that the socket be non-blocking. 502 // Our connect() and recv() calls require that the socket be non-blocking.
488 WSAEventSelect(socket_, core_->read_overlapped_.hEvent, FD_CONNECT); 503 if (core_->disable_overlapped_reads_) {
504 WSAEventSelect(socket_, core_->read_overlapped_.hEvent,
505 FD_CONNECT | FD_READ | FD_CLOSE);
506 } else {
507 WSAEventSelect(socket_, core_->read_overlapped_.hEvent, FD_CONNECT);
508 }
489 509
490 SockaddrStorage storage; 510 SockaddrStorage storage;
491 if (!endpoint.ToSockAddr(storage.addr, &storage.addr_len)) 511 if (!endpoint.ToSockAddr(storage.addr, &storage.addr_len))
492 return ERR_INVALID_ARGUMENT; 512 return ERR_INVALID_ARGUMENT;
493 connect_start_time_ = base::TimeTicks::Now(); 513 connect_start_time_ = base::TimeTicks::Now();
494 if (!connect(socket_, storage.addr, storage.addr_len)) { 514 if (!connect(socket_, storage.addr, storage.addr_len)) {
495 // Connected without waiting! 515 // Connected without waiting!
496 // 516 //
497 // The MSDN page for connect says: 517 // The MSDN page for connect says:
498 // With a nonblocking socket, the connection attempt cannot be completed 518 // With a nonblocking socket, the connection attempt cannot be completed
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
704 724
705 int TCPClientSocketWin::Read(IOBuffer* buf, 725 int TCPClientSocketWin::Read(IOBuffer* buf,
706 int buf_len, 726 int buf_len,
707 const CompletionCallback& callback) { 727 const CompletionCallback& callback) {
708 DCHECK(CalledOnValidThread()); 728 DCHECK(CalledOnValidThread());
709 DCHECK_NE(socket_, INVALID_SOCKET); 729 DCHECK_NE(socket_, INVALID_SOCKET);
710 DCHECK(!waiting_read_); 730 DCHECK(!waiting_read_);
711 DCHECK(read_callback_.is_null()); 731 DCHECK(read_callback_.is_null());
712 DCHECK(!core_->read_iobuffer_); 732 DCHECK(!core_->read_iobuffer_);
713 733
714 buf_len = core_->ThrottleReadSize(buf_len); 734 if (core_->disable_overlapped_reads_) {
715 735 int flags = 0;
716 WSABUF read_buffer; 736 int rv = recv(socket_, buf->data(), buf_len, flags);
717 read_buffer.len = buf_len; 737 if (rv == SOCKET_ERROR) {
718 read_buffer.buf = buf->data(); 738 int os_error = WSAGetLastError();
719 739 if (os_error != WSAEWOULDBLOCK) {
720 // TODO(wtc): Remove the assertion after enough testing. 740 int net_error = MapSystemError(os_error);
721 AssertEventNotSignaled(core_->read_overlapped_.hEvent); 741 net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
722 DWORD num, flags = 0; 742 CreateNetLogSocketErrorCallback(net_error, os_error));
723 int rv = WSARecv(socket_, &read_buffer, 1, &num, &flags, 743 return net_error;
724 &core_->read_overlapped_, NULL); 744 }
725 if (rv == 0) { 745 } else {
726 if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) {
727 base::StatsCounter read_bytes("tcp.read_bytes"); 746 base::StatsCounter read_bytes("tcp.read_bytes");
728 read_bytes.Add(num); 747 if (rv > 0) {
729 num_bytes_read_ += num;
730 if (num > 0)
731 use_history_.set_was_used_to_convey_data(); 748 use_history_.set_was_used_to_convey_data();
732 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, num, 749 read_bytes.Add(rv);
750 num_bytes_read_ += rv;
751 }
752 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv,
733 buf->data()); 753 buf->data());
734 return static_cast<int>(num); 754 return rv;
735 } 755 }
736 } else { 756 } else {
737 int os_error = WSAGetLastError(); 757 buf_len = core_->ThrottleReadSize(buf_len);
738 if (os_error != WSA_IO_PENDING) { 758
739 int net_error = MapSystemError(os_error); 759 WSABUF read_buffer;
740 net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR, 760 read_buffer.len = buf_len;
741 CreateNetLogSocketErrorCallback(net_error, os_error)); 761 read_buffer.buf = buf->data();
742 return net_error; 762
763 // TODO(wtc): Remove the assertion after enough testing.
764 AssertEventNotSignaled(core_->read_overlapped_.hEvent);
765 DWORD num, flags = 0;
766 int rv = WSARecv(socket_, &read_buffer, 1, &num, &flags,
767 &core_->read_overlapped_, NULL);
768 if (rv == 0) {
769 if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) {
770 base::StatsCounter read_bytes("tcp.read_bytes");
771 read_bytes.Add(num);
772 num_bytes_read_ += num;
773 if (num > 0)
774 use_history_.set_was_used_to_convey_data();
775 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, num,
776 buf->data());
777 return static_cast<int>(num);
778 }
779 } else {
780 int os_error = WSAGetLastError();
781 if (os_error != WSA_IO_PENDING) {
782 int net_error = MapSystemError(os_error);
783 net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
784 CreateNetLogSocketErrorCallback(net_error, os_error));
785 return net_error;
786 }
743 } 787 }
744 } 788 }
745 core_->WatchForRead(); 789
746 waiting_read_ = true; 790 waiting_read_ = true;
747 read_callback_ = callback; 791 read_callback_ = callback;
748 core_->read_iobuffer_ = buf; 792 core_->read_iobuffer_ = buf;
793 core_->read_buffer_length_ = buf_len;
794 core_->WatchForRead();
749 return ERR_IO_PENDING; 795 return ERR_IO_PENDING;
750 } 796 }
751 797
752 int TCPClientSocketWin::Write(IOBuffer* buf, 798 int TCPClientSocketWin::Write(IOBuffer* buf,
753 int buf_len, 799 int buf_len,
754 const CompletionCallback& callback) { 800 const CompletionCallback& callback) {
755 DCHECK(CalledOnValidThread()); 801 DCHECK(CalledOnValidThread());
756 DCHECK_NE(socket_, INVALID_SOCKET); 802 DCHECK_NE(socket_, INVALID_SOCKET);
757 DCHECK(!waiting_write_); 803 DCHECK(!waiting_write_);
758 DCHECK(write_callback_.is_null()); 804 DCHECK(write_callback_.is_null());
759 DCHECK_GT(buf_len, 0); 805 DCHECK_GT(buf_len, 0);
760 DCHECK(!core_->write_iobuffer_); 806 DCHECK(!core_->write_iobuffer_);
761 807
762 base::StatsCounter writes("tcp.writes"); 808 base::StatsCounter writes("tcp.writes");
763 writes.Increment(); 809 writes.Increment();
764 810
765 WSABUF write_buffer; 811 WSABUF write_buffer;
766 write_buffer.len = buf_len; 812 write_buffer.len = buf_len;
767 write_buffer.buf = buf->data(); 813 write_buffer.buf = buf->data();
768 core_->write_buffer_length_ = buf_len;
769 814
770 // TODO(wtc): Remove the assertion after enough testing. 815 // TODO(wtc): Remove the assertion after enough testing.
771 AssertEventNotSignaled(core_->write_overlapped_.hEvent); 816 AssertEventNotSignaled(core_->write_overlapped_.hEvent);
772 DWORD num; 817 DWORD num;
773 int rv = WSASend(socket_, &write_buffer, 1, &num, 0, 818 int rv = WSASend(socket_, &write_buffer, 1, &num, 0,
774 &core_->write_overlapped_, NULL); 819 &core_->write_overlapped_, NULL);
775 if (rv == 0) { 820 if (rv == 0) {
776 if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) { 821 if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) {
777 rv = static_cast<int>(num); 822 rv = static_cast<int>(num);
778 if (rv > buf_len || rv < 0) { 823 if (rv > buf_len || rv < 0) {
(...skipping 13 matching lines...) Expand all
792 } 837 }
793 } else { 838 } else {
794 int os_error = WSAGetLastError(); 839 int os_error = WSAGetLastError();
795 if (os_error != WSA_IO_PENDING) { 840 if (os_error != WSA_IO_PENDING) {
796 int net_error = MapSystemError(os_error); 841 int net_error = MapSystemError(os_error);
797 net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR, 842 net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
798 CreateNetLogSocketErrorCallback(net_error, os_error)); 843 CreateNetLogSocketErrorCallback(net_error, os_error));
799 return net_error; 844 return net_error;
800 } 845 }
801 } 846 }
802 core_->WatchForWrite();
803 waiting_write_ = true; 847 waiting_write_ = true;
804 write_callback_ = callback; 848 write_callback_ = callback;
805 core_->write_iobuffer_ = buf; 849 core_->write_iobuffer_ = buf;
850 core_->write_buffer_length_ = buf_len;
851 core_->WatchForWrite();
806 return ERR_IO_PENDING; 852 return ERR_IO_PENDING;
807 } 853 }
808 854
809 bool TCPClientSocketWin::SetReceiveBufferSize(int32 size) { 855 bool TCPClientSocketWin::SetReceiveBufferSize(int32 size) {
810 DCHECK(CalledOnValidThread()); 856 DCHECK(CalledOnValidThread());
811 return SetSocketReceiveBufferSize(socket_, size); 857 return SetSocketReceiveBufferSize(socket_, size);
812 } 858 }
813 859
814 bool TCPClientSocketWin::SetSendBufferSize(int32 size) { 860 bool TCPClientSocketWin::SetSendBufferSize(int32 size) {
815 DCHECK(CalledOnValidThread()); 861 DCHECK(CalledOnValidThread());
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
893 } 939 }
894 940
895 connect_os_error_ = os_error; 941 connect_os_error_ = os_error;
896 rv = DoConnectLoop(result); 942 rv = DoConnectLoop(result);
897 if (rv != ERR_IO_PENDING) { 943 if (rv != ERR_IO_PENDING) {
898 LogConnectCompletion(rv); 944 LogConnectCompletion(rv);
899 DoReadCallback(rv); 945 DoReadCallback(rv);
900 } 946 }
901 } 947 }
902 948
949 void TCPClientSocketWin::DidSignalRead() {
950 DCHECK(waiting_read_);
951 int rv;
952 int os_error = 0, flags = 0;
953 WSANETWORKEVENTS network_events;
954 rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent,
955 &network_events);
956 if (rv != SOCKET_ERROR) {
957 if (network_events.lNetworkEvents & FD_READ) {
958 rv = recv(socket_, core_->read_iobuffer_->data(),
959 core_->read_buffer_length_, flags);
960 if (rv == SOCKET_ERROR) {
961 os_error = WSAGetLastError();
962 if (os_error == WSAEWOULDBLOCK) {
963 core_->WatchForRead();
964 return;
965 } else {
966 rv = MapSystemError(os_error);
967 }
968 }
969 } else {
970 core_->WatchForRead();
971 return;
972 }
973 } else {
974 os_error = WSAGetLastError();
975 rv = MapSystemError(os_error);
976 }
977 if (rv > 0) {
978 base::StatsCounter read_bytes("tcp.read_bytes");
979 if (rv > 0) {
980 use_history_.set_was_used_to_convey_data();
981 read_bytes.Add(rv);
982 num_bytes_read_ += rv;
983 }
984 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv,
985 core_->read_iobuffer_->data());
986 } else {
987 rv = MapSystemError(os_error);
988 net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
989 CreateNetLogSocketErrorCallback(rv, os_error));
990 }
991 waiting_read_ = false;
992 core_->read_iobuffer_ = NULL;
993 core_->read_buffer_length_ = 0;
994 DoReadCallback(rv);
995 }
996
903 void TCPClientSocketWin::DidCompleteRead() { 997 void TCPClientSocketWin::DidCompleteRead() {
904 DCHECK(waiting_read_); 998 DCHECK(waiting_read_);
905 DWORD num_bytes, flags; 999 DWORD num_bytes, flags;
906 BOOL ok = WSAGetOverlappedResult(socket_, &core_->read_overlapped_, 1000 BOOL ok = WSAGetOverlappedResult(socket_, &core_->read_overlapped_,
907 &num_bytes, FALSE, &flags); 1001 &num_bytes, FALSE, &flags);
908 WSAResetEvent(core_->read_overlapped_.hEvent); 1002 WSAResetEvent(core_->read_overlapped_.hEvent);
909 waiting_read_ = false; 1003 waiting_read_ = false;
910 int rv; 1004 int rv;
911 if (ok) { 1005 if (ok) {
912 base::StatsCounter read_bytes("tcp.read_bytes"); 1006 base::StatsCounter read_bytes("tcp.read_bytes");
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
956 if (num_bytes > 0) 1050 if (num_bytes > 0)
957 use_history_.set_was_used_to_convey_data(); 1051 use_history_.set_was_used_to_convey_data();
958 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, num_bytes, 1052 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, num_bytes,
959 core_->write_iobuffer_->data()); 1053 core_->write_iobuffer_->data());
960 } 1054 }
961 } 1055 }
962 core_->write_iobuffer_ = NULL; 1056 core_->write_iobuffer_ = NULL;
963 DoWriteCallback(rv); 1057 DoWriteCallback(rv);
964 } 1058 }
965 1059
1060 void TCPClientSocketWin::DisableOverlappedReads() {
1061 TCPClientSocketWin::Core::disable_overlapped_reads_ = true;
1062 }
1063
966 } // namespace net 1064 } // namespace net
OLDNEW
« chrome/renderer/page_load_histograms.cc ('K') | « net/socket/tcp_client_socket_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698