This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 217f85edd Fix socket overcrowding state race (#3621)
217f85edd is described below

commit 217f85edd471b7ceff9fd878d87f1cca8727bc44
Author: nkysg <[email protected]>
AuthorDate: Sun Oct 25 17:50:15 2026 +0800

    Fix socket overcrowding state race (#3621)
    
    * Fix socket overcrowding state race
    
    * Clarify socket overcrowding limit behavior
    
    * Fix socket overcrowding flag consistency
---
 src/brpc/socket.cpp                      | 36 ++++++++++++++--------------
 src/brpc/socket.h                        |  5 +---
 test/brpc_http_rpc_protocol_unittest.cpp | 40 +++++++++++++++++++++++++++++++-
 3 files changed, 57 insertions(+), 24 deletions(-)

diff --git a/src/brpc/socket.cpp b/src/brpc/socket.cpp
index 57cfd2296..7756c9c3b 100644
--- a/src/brpc/socket.cpp
+++ b/src/brpc/socket.cpp
@@ -89,8 +89,8 @@ DEFINE_int32(ssl_handshake_timeout_ms, 5000,
 BRPC_VALIDATE_GFLAG(ssl_handshake_timeout_ms, PassValidate);
 
 DEFINE_int64(socket_max_unwritten_bytes, 64 * 1024 * 1024,
-             "Max unwritten bytes in each socket, if the limit is reached,"
-             " Socket.Write fails with EOVERCROWDED");
+             "Max unwritten bytes in each socket. Socket.Write fails with "
+             "EOVERCROWDED when the limit is reached.");
 
 DEFINE_int64(socket_max_streams_unconsumed_bytes, 0,
              "Max stream receivers' unconsumed bytes in one socket,"
@@ -399,11 +399,7 @@ void Socket::WriteRequest::Setup(Socket* s) {
                 return;
             }
         }
-        const int64_t before_write =
-            s->_unwritten_bytes.fetch_add(data.size(), 
butil::memory_order_relaxed);
-        if (before_write + (int64_t)data.size() >= 
FLAGS_socket_max_unwritten_bytes) {
-            s->_overcrowded = true;
-        }
+        s->_unwritten_bytes.fetch_add(data.size(), 
butil::memory_order_relaxed);
     }
     const uint32_t pc = pipelined_count();
     if (pc) {
@@ -480,7 +476,6 @@ Socket::Socket(Forbidden f)
     , _socket_mode(SOCKET_MODE_TCP)
     , _connection_type_for_progressive_read(CONNECTION_TYPE_UNKNOWN)
     , _controller_released_socket(false)
-    , _overcrowded(false)
     , _fail_me_at_server_stop(false)
     , _logoff_flag(false)
     , _error_code(0)
@@ -776,7 +771,6 @@ int Socket::OnCreated(const SocketOptions& options) {
     _ssl_ctx = options.initial_ssl_ctx;
     _connection_type_for_progressive_read = CONNECTION_TYPE_UNKNOWN;
     _controller_released_socket.store(false, butil::memory_order_relaxed);
-    _overcrowded = false;
     // Maybe non-zero for RTMP connections.
     _fail_me_at_server_stop = false;
     _logoff_flag.store(false, butil::memory_order_relaxed);
@@ -1663,7 +1657,7 @@ int Socket::Write(butil::IOBuf* data, const WriteOptions* 
options_in) {
         }
     }
 
-    if (!opt.ignore_eovercrowded && _overcrowded) {
+    if (!opt.ignore_eovercrowded && is_overcrowded()) {
         return SetError(opt.id_wait, EOVERCROWDED);
     }
 
@@ -1701,7 +1695,7 @@ int Socket::Write(SocketMessagePtr<>& msg, const 
WriteOptions* options_in) {
         }
     }
     
-    if (!opt.ignore_eovercrowded && _overcrowded) {
+    if (!opt.ignore_eovercrowded && is_overcrowded()) {
         return SetError(opt.id_wait, EOVERCROWDED);
     }
     
@@ -1878,8 +1872,9 @@ void* Socket::KeepWrite(void* void_arg) {
         //if (nw <= 0 || req->data.empty()/*note*/) {
         if (nw <= 0) {
             // NOTE: Waiting epollout within timeout is a must to force
-            // KeepWrite to check and setup pending WriteRequests periodically,
-            // which may turn on _overcrowded to stop pending requests from
+            // KeepWrite to check and setup pending WriteRequests periodically
+            // so their bytes contribute to _unwritten_bytes. Subsequent
+            // writes then observe the limit and stop the pending queue from
             // growing infinitely.
             const timespec duetime = 
butil::milliseconds_from_now(WAIT_EPOLLOUT_TIMEOUT_MS);
             bool pollin = s->_transport->HasOnEdgeTrigger();
@@ -2418,7 +2413,7 @@ void Socket::DebugSocket(std::ostream& os, SocketId id) {
        << "\nread_buf=" << input_processor.read_buf().size()
        << "\nlast_read_to_now=" << cpuwide_now - ptr->_last_readtime_us << "us"
        << "\nlast_write_to_now=" << cpuwide_now - ptr->_last_writetime_us << 
"us"
-       << "\novercrowded=" << ptr->_overcrowded;
+       << "\novercrowded=" << ptr->is_overcrowded();
     os << "\nid_wait_list={";
     for (size_t i = 0; i < nidsize; ++i) {
         if (i) {
@@ -3003,11 +2998,14 @@ void Socket::AddInputMessages(size_t count) {
     GetOrNewSharedPart()->in_num_messages.fetch_add(count, 
butil::memory_order_relaxed);
 }
 void Socket::CancelUnwrittenBytes(size_t bytes) {
-    const int64_t before_minus =
-        _unwritten_bytes.fetch_sub(bytes, butil::memory_order_relaxed);
-    if (before_minus < (int64_t)bytes + FLAGS_socket_max_unwritten_bytes) {
-        _overcrowded = false;
-    }
+    _unwritten_bytes.fetch_sub(bytes, butil::memory_order_relaxed);
+}
+
+bool Socket::is_overcrowded() const {
+    const int64_t limit = FLAGS_socket_max_unwritten_bytes;
+    const int64_t unwritten_bytes =
+        _unwritten_bytes.load(butil::memory_order_relaxed);
+    return limit > 0 && unwritten_bytes >= limit;
 }
 void Socket::AddOutputBytes(size_t bytes) {
     GetOrNewSharedPart()->out_size.fetch_add(bytes, 
butil::memory_order_relaxed);
diff --git a/src/brpc/socket.h b/src/brpc/socket.h
index 94f85880d..4e651e075 100644
--- a/src/brpc/socket.h
+++ b/src/brpc/socket.h
@@ -712,7 +712,7 @@ public:
     }
 
     // Returns true if the remote side is overcrowded.
-    bool is_overcrowded() const { return _overcrowded; }
+    bool is_overcrowded() const;
 
     bthread_keytable_pool_t* keytable_pool() const { return _keytable_pool; }
 
@@ -1012,9 +1012,6 @@ private:
     ConnectionType _connection_type_for_progressive_read;
     butil::atomic<bool> _controller_released_socket;
 
-    // True if the socket is too full to write.
-    volatile bool _overcrowded;
-
     bool _fail_me_at_server_stop;
 
     // Set by SetLogOff
diff --git a/test/brpc_http_rpc_protocol_unittest.cpp 
b/test/brpc_http_rpc_protocol_unittest.cpp
index fe02ba0a1..880caf06c 100644
--- a/test/brpc_http_rpc_protocol_unittest.cpp
+++ b/test/brpc_http_rpc_protocol_unittest.cpp
@@ -40,6 +40,7 @@
 #include "butil/logging.h"
 #include "butil/files/scoped_file.h"
 #include "butil/fd_guard.h"
+#include "butil/fd_utility.h"
 #include "butil/file_util.h"
 #include "brpc/socket.h"
 #include "brpc/acceptor.h"
@@ -2289,7 +2290,7 @@ TEST_F(HttpTest, h2_ping_ack_respects_socket_write_cap) {
     brpc::Socket::WriteOptions wopt;
     wopt.ignore_eovercrowded = false;
     ASSERT_EQ(0, sock->Write(&big, &wopt));
-    ASSERT_TRUE(sock->_overcrowded);
+    ASSERT_TRUE(sock->is_overcrowded());
 
     brpc::policy::H2Context* ctx =
         new brpc::policy::H2Context(sock.get(), nullptr);
@@ -2319,6 +2320,43 @@ TEST_F(HttpTest, h2_ping_ack_respects_socket_write_cap) {
     ASSERT_EQ(brpc::PARSE_ERROR_ABSOLUTELY_WRONG, pr.error());
 }
 
+TEST_F(HttpTest, socket_overcrowded_state_tracks_pending_writes) {
+    int fds[2];
+    ASSERT_EQ(0, pipe(fds));
+    butil::fd_guard read_fd(fds[0]);
+    ASSERT_EQ(0, butil::make_non_blocking(read_fd));
+    brpc::SocketOptions options;
+    options.fd = fds[1];
+    brpc::SocketId id;
+    ASSERT_EQ(0, brpc::Socket::Create(options, &id));
+    brpc::SocketUniquePtr sock;
+    ASSERT_EQ(0, brpc::Socket::Address(id, &sock));
+
+    GFLAGS_NAMESPACE::FlagSaver flag_saver;
+    brpc::FLAGS_socket_max_unwritten_bytes = 64;
+
+    butil::IOBuf data;
+    data.append(std::string(1024 * 1024, 'x'));
+    ASSERT_EQ(0, sock->Write(&data));
+    ASSERT_TRUE(sock->is_overcrowded());
+
+    brpc::FLAGS_socket_max_unwritten_bytes = 0;
+    ASSERT_FALSE(sock->is_overcrowded());
+    brpc::FLAGS_socket_max_unwritten_bytes = 64;
+
+    char buf[8192];
+    const int max_wait_iterations = 1000;
+    for (int i = 0;
+         i < max_wait_iterations && sock->is_overcrowded(); ++i) {
+        ssize_t nread;
+        while ((nread = read(read_fd, buf, sizeof(buf))) > 0) {}
+        ASSERT_EQ(-1, nread);
+        ASSERT_TRUE(errno == EAGAIN || errno == EWOULDBLOCK);
+        bthread_usleep(1000);
+    }
+    ASSERT_FALSE(sock->is_overcrowded());
+}
+
 TEST_F(HttpTest, http2_goaway_with_debug_data) {
     // GOAWAY payload is Last-Stream-ID(4) | Error Code(4) | Debug Data(*).
     const uint32_t payload_size = 16;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to