Copilot commented on code in PR #3422:
URL: https://github.com/apache/brpc/pull/3422#discussion_r3696129413


##########
src/brpc/stream.cpp:
##########
@@ -72,285 +68,239 @@ Stream::~Stream() {
     CHECK(_host_socket == NULL);
     bthread_mutex_destroy(&_connect_mutex);
     bthread_mutex_destroy(&_congestion_control_mutex);
-    bthread_id_list_destroy(&_writable_wait_list);
 }
 
 int Stream::Create(const StreamOptions &options, 
-                   const StreamSettings *remote_settings,
+                   const StreamSettings* remote_settings,
                    StreamId *id, bool parse_rpc_response) {
-    Stream* s = new Stream();
-    s->_host_socket = NULL;
-    s->_fake_socket_weak_ref = NULL;
-    s->_connected = false;
-    s->_options = options;
-    s->_closed = false;
-    s->_error_code = 0;
-    s->_cur_buf_size = options.max_buf_size > 0 ? options.max_buf_size : 0;
+    return VersionedRefWithId<Stream>::Create(
+        id, options, remote_settings, parse_rpc_response);
+}
+
+int Stream::OnCreated(const StreamOptions& options,
+                      const StreamSettings* remote_settings,
+                      bool parse_rpc_response) {
+    _host_socket = NULL;
+    _connected.store(false, butil::memory_order_relaxed);
+    _options = options;
+    _error_code = 0;
+    _error_text.clear();
+    _pending_writes.clear();
+    _produced = 0;
+    _remote_consumed = 0;
+    _local_consumed = 0;
+    _atomic_local_consumed.store(0, butil::memory_order_relaxed);
+    _parse_rpc_response = parse_rpc_response;
+    _pending_buf = NULL;
+    _start_idle_timer_us = 0;
+    _idle_timer = 0;
+    _remote_settings.Clear();
+
+    _cur_buf_size = options.max_buf_size > 0 ? options.max_buf_size : 0;
     if (options.max_buf_size > 0 && options.min_buf_size > 
options.max_buf_size) {
         // set 0 if min_buf_size is invalid.
-        s->_options.min_buf_size = 0;
+        _options.min_buf_size = 0;
         LOG(WARNING) << "options.min_buf_size is larger than 
options.max_buf_size, it will be set to 0.";
     }
-    if (FLAGS_socket_max_streams_unconsumed_bytes > 0 && 
s->_options.min_buf_size > 0) {
-        s->_cur_buf_size = s->_options.min_buf_size;
+    if (FLAGS_socket_max_streams_unconsumed_bytes > 0 && _options.min_buf_size 
> 0) {
+        _cur_buf_size = _options.min_buf_size;
     }
 
     if (remote_settings != NULL) {
-        s->_remote_settings.MergeFrom(*remote_settings);
-    }
-    s->_parse_rpc_response = parse_rpc_response;
-    if (bthread_id_list_init(&s->_writable_wait_list, 8, 8/*FIXME*/)) {
-        delete s;
-        return -1;
+        _remote_settings.MergeFrom(*remote_settings);
     }
+
+    CHECK_EQ(0, bthread_id_list_init(&_writable_wait_list, 8, 8/*FIXME*/));
+
     bthread::ExecutionQueueOptions q_opt;
     q_opt.bthread_attr 
         = FLAGS_usercode_in_pthread ? BTHREAD_ATTR_PTHREAD : 
BTHREAD_ATTR_NORMAL;
-    if (bthread::execution_queue_start(&s->_consumer_queue, &q_opt, Consume, 
s) != 0) {
+    if (bthread::execution_queue_start(&_consumer_queue, &q_opt, Consume, 
this) != 0) {
         LOG(FATAL) << "Fail to create ExecutionQueue";
-        delete s;
-        return -1;
-    }
-    SocketOptions sock_opt;
-    sock_opt.conn = s;
-    SocketId fake_sock_id;
-    if (Socket::Create(sock_opt, &fake_sock_id) != 0) {
-        s->BeforeRecycle(NULL);
         return -1;
     }
-    SocketUniquePtr ptr;
-    CHECK_EQ(0, Socket::Address(fake_sock_id, &ptr));
-    s->_fake_socket_weak_ref = ptr.get();
-    s->_id = fake_sock_id;
-    *id = s->id();
+
+    // The consumer queue holds one reference to this Stream.
+    AddReference();
     return 0;
 }
 
-void Stream::BeforeRecycle(Socket *) {
-    // No one holds reference now, so we don't need lock here
-    bthread_id_list_reset(&_writable_wait_list, ECONNRESET);
-    if (_connected) {
-        // Send CLOSE frame
-        RPC_VLOG << "Send close frame";
-        CHECK(_host_socket != NULL);
-        policy::SendStreamClose(_host_socket,
-                                _remote_settings.stream_id(), id());
+void Stream::OnFailed(int error_code, const std::string& error_text) {
+    bool connected = false;
+    {
+        // Record the error for on_failed callback fired in Consume(), and 
discard
+        // any writes buffered before connecting.
+        BAIDU_SCOPED_LOCK(_connect_mutex);
+        _error_code = error_code;
+        _error_text = error_text;
+        connected = _connected.load(butil::memory_order_relaxed);
+        _pending_writes.clear();
     }
 
-    if (_host_socket) {
-        _host_socket->RemoveStream(id());
+    // Wake up all threads blocked on writable.
+    bthread_id_list_reset(&_writable_wait_list, ECONNRESET);
+
+    // Serialize the host Socket membership removal with SetHostSocket().
+    // SetFailed() marks this Stream failed before entering OnFailed(), so a
+    // later SetHostSocket() observes Failed() and cannot add it back.
+    {
+        BAIDU_SCOPED_LOCK(_connect_mutex);
+        if (connected) {
+            RPC_VLOG << "Send close frame";
+            CHECK(_host_socket != NULL);
+            policy::SendStreamClose(
+                _host_socket, _remote_settings.stream_id(), id());
+        }
+        if (_host_socket != NULL) {
+            _host_socket->RemoveStream(id());
+        }
     }

Review Comment:
   When a stream is SetFailed/closed while it still has outstanding unconsumed 
bytes, `_host_socket->_total_streams_unconsumed_size` is never decremented for 
this stream. Because later FEEDBACK frames won’t be processed (Stream::Address 
fails once the stream is failed), the socket-level total can remain 
artificially high and skew `FLAGS_socket_max_streams_unconsumed_bytes` 
congestion behavior for other streams on the same socket.



##########
test/brpc_streaming_rpc_unittest.cpp:
##########
@@ -866,11 +850,13 @@ TEST_F(StreamingRpcTest, 
segment_stream_data_automatically) {
 
     brpc::SocketUniquePtr host_socket_ptr;
     {
-      brpc::SocketUniquePtr ptr;
-      ASSERT_EQ(0, brpc::Socket::Address(request_stream, &ptr));
-      brpc::Stream *s = (brpc::Stream *)ptr->conn();
-      ASSERT_TRUE(s->_host_socket != NULL);
-      s->_host_socket->ReAddress(&host_socket_ptr);
+      brpc::StreamUniquePtr ptr;
+      ASSERT_EQ(0, brpc::Stream::Address(request_stream, &ptr));
+      brpc::Stream *s = ptr.get();
+      brpc::Socket* host_socket =
+          s->_host_socket;
+      ASSERT_TRUE(host_socket != NULL);

Review Comment:
   This test reads the non-atomic `s->_host_socket` without first performing 
the acquire load on `_connected` that the other updated tests rely on to safely 
publish `_host_socket`. This can become a data race if connection establishment 
is still in flight when this block runs.



##########
src/brpc/stream.cpp:
##########
@@ -42,23 +42,19 @@ BRPC_VALIDATE_GFLAG(stream_write_max_segment_size, 
PositiveInteger);
 
 const static butil::IOBuf *TIMEOUT_TASK = (butil::IOBuf*)-1L;
 
-Stream::Stream() 
-    : _host_socket(NULL)
-    , _fake_socket_weak_ref(NULL)
+Stream::Stream(Forbidden f)
+    : VersionedRefWithId<Stream>(f)
+    , _host_socket(NULL)
     , _connected(false)
-    , _closed(false)
     , _error_code(0)

Review Comment:
   `_pending_buf` is used in the destructor and in `BeforeRecycled()`, but it 
is no longer initialized in the constructor initializer list. Today 
`OnCreated()` sets it to NULL before any failure point, but leaving it 
uninitialized makes the class fragile (e.g., if future changes add an 
early-return before `_pending_buf` is set, recycling could delete an 
indeterminate pointer).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to