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


##########
test/endpoint_unittest.cpp:
##########
@@ -16,19 +16,30 @@
 // under the License.
 
 #include <gtest/gtest.h>
+#include <atomic>
+#include <pthread.h>
+#include <signal.h>
 #include <butil/fd_guard.h>
 #include <butil/fd_utility.h>
 #include "butil/errno.h"
 #include "butil/endpoint.h"
 #include "butil/logging.h"
+#include "butil/memory/scope_guard.h"
 #include "butil/containers/flat_map.h"
 #include "butil/details/extended_endpoint.hpp"
 #include <netinet/tcp.h>
-#if defined(OS_MACOSX)
+#if defined(OS_LINUX)
+#include <sys/epoll.h>
+#elif defined(OS_MACOSX)
+#include <sys/event.h>
 #include <netinet/tcp_fsm.h>
 #endif
 
 namespace butil {
+int pthread_fd_wait(int fd, unsigned events, const timespec* abstime);
+#if defined(OS_MACOSX)
+int pthread_fd_wait(int fd, int events, const timespec* abstime);
+#endif
 int pthread_timed_connect(int sockfd, const struct sockaddr* serv_addr,
                           socklen_t addrlen, const timespec* abstime);

Review Comment:
   These tests rely on forward declarations of non-public functions 
(`pthread_fd_wait`, `pthread_timed_connect`). This is brittle: signatures can 
drift from the implementation without compiler errors if the real declaration 
changes elsewhere. Consider moving these declarations into a dedicated internal 
header (or a test helper header) that both production code and tests can 
include, so the tests compile against the authoritative signature.



##########
test/endpoint_unittest.cpp:
##########
@@ -500,119 +511,210 @@ TEST(EndPointTest, endpoint_concurrency) {
     }
 }
 
-const char* g_hostname1 = "github.com";
-const char* g_hostname2 = "baidu.com";
 TEST(EndPointTest, tcp_connect) {
-    butil::EndPoint ep1;
-    butil::EndPoint ep2;
-    ASSERT_EQ(0, butil::hostname2endpoint(g_hostname1, 80, &ep1));
-    ASSERT_EQ(0, butil::hostname2endpoint(g_hostname2, 80, &ep2));
+    butil::EndPoint ep;
+    ASSERT_EQ(0, butil::str2endpoint("127.0.0.1:0", &ep));
+    butil::fd_guard listener(butil::tcp_listen(ep));
+    ASSERT_LE(0, listener) << "errno=" << errno;
+    ASSERT_EQ(0, butil::get_local_side(listener, &ep));
+    // The local TCP handshake completes without a concurrent accept thread.
     {
-        butil::fd_guard sockfd(butil::tcp_connect(ep1, nullptr));
+        butil::fd_guard sockfd(butil::tcp_connect(ep, nullptr));
         ASSERT_LE(0, sockfd) << "errno=" << errno;
     }
     {
-        butil::fd_guard sockfd(butil::tcp_connect(ep1, nullptr, 1000));
+        butil::fd_guard sockfd(butil::tcp_connect(ep, nullptr, 5000));

Review Comment:
   On loopback, successful connects should complete quickly. Using a 5s timeout 
increases worst-case test runtime if something regresses or hangs, which can 
slow CI feedback. Consider using a smaller timeout (e.g., 500–1000ms) for the 
success-path connect, reserving larger timeouts only where strictly needed.



##########
test/endpoint_unittest.cpp:
##########
@@ -500,119 +511,210 @@ TEST(EndPointTest, endpoint_concurrency) {
     }
 }
 
-const char* g_hostname1 = "github.com";
-const char* g_hostname2 = "baidu.com";
 TEST(EndPointTest, tcp_connect) {
-    butil::EndPoint ep1;
-    butil::EndPoint ep2;
-    ASSERT_EQ(0, butil::hostname2endpoint(g_hostname1, 80, &ep1));
-    ASSERT_EQ(0, butil::hostname2endpoint(g_hostname2, 80, &ep2));
+    butil::EndPoint ep;
+    ASSERT_EQ(0, butil::str2endpoint("127.0.0.1:0", &ep));
+    butil::fd_guard listener(butil::tcp_listen(ep));
+    ASSERT_LE(0, listener) << "errno=" << errno;
+    ASSERT_EQ(0, butil::get_local_side(listener, &ep));
+    // The local TCP handshake completes without a concurrent accept thread.
     {
-        butil::fd_guard sockfd(butil::tcp_connect(ep1, nullptr));
+        butil::fd_guard sockfd(butil::tcp_connect(ep, nullptr));
         ASSERT_LE(0, sockfd) << "errno=" << errno;
     }
     {
-        butil::fd_guard sockfd(butil::tcp_connect(ep1, nullptr, 1000));
+        butil::fd_guard sockfd(butil::tcp_connect(ep, nullptr, 5000));
         ASSERT_LE(0, sockfd) << "errno=" << errno;
     }
-    {
-        butil::fd_guard sockfd(butil::tcp_connect(ep2, nullptr, 1));
-        ASSERT_EQ(-1, sockfd) << "errno=" << errno;
-        ASSERT_EQ(ETIMEDOUT, errno);
-    }
 
-    {
-        struct sockaddr_storage serv_addr{};
-        socklen_t serv_addr_size = 0;
-        ASSERT_EQ(0, endpoint2sockaddr(ep1, &serv_addr, &serv_addr_size));
-        butil::fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 0));
-        ASSERT_LE(0, sockfd);
-        bool is_blocking = butil::is_blocking(sockfd);
-        ASSERT_EQ(0, butil::pthread_timed_connect(
-            sockfd, (struct sockaddr*) &serv_addr, serv_addr_size, nullptr));
-        ASSERT_EQ(is_blocking, butil::is_blocking(sockfd));
+    sockaddr_storage serv_addr{};
+    socklen_t serv_addr_size = 0;
+    ASSERT_EQ(0, endpoint2sockaddr(ep, &serv_addr, &serv_addr_size));
+    for (bool timed : {false, true}) {
+        for (bool blocking : {false, true}) {
+            butil::fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 
0));
+            ASSERT_LE(0, sockfd);
+            if (!blocking) {
+                ASSERT_EQ(0, butil::make_non_blocking(sockfd));
+            }
+            timespec deadline = butil::seconds_from_now(5);
+            ASSERT_EQ(0, butil::pthread_timed_connect(
+                sockfd, reinterpret_cast<sockaddr*>(&serv_addr), 
serv_addr_size,
+                timed ? &deadline : nullptr)) << "errno=" << errno;
+            ASSERT_EQ(blocking, butil::is_blocking(sockfd));
+            ASSERT_EQ(0, butil::is_connected(sockfd));
+        }
     }
+}
 
+#if defined(OS_LINUX)
+TEST(EndPointTest, tcp_connect_timeout_with_full_accept_queue) {
+    butil::EndPoint ep;
+    ASSERT_EQ(0, butil::str2endpoint("127.0.0.1:0", &ep));
+    butil::fd_guard listener(butil::tcp_listen(ep));
+    ASSERT_LE(0, listener);

Review Comment:
   When `tcp_listen` fails, this assertion provides no errno context, making CI 
failures harder to diagnose. Align with the surrounding tests by appending `<< 
\"errno=\" << errno` (or capturing strerror) so failures indicate the 
underlying syscall error.



##########
test/endpoint_unittest.cpp:
##########
@@ -500,119 +511,210 @@ TEST(EndPointTest, endpoint_concurrency) {
     }
 }
 
-const char* g_hostname1 = "github.com";
-const char* g_hostname2 = "baidu.com";
 TEST(EndPointTest, tcp_connect) {
-    butil::EndPoint ep1;
-    butil::EndPoint ep2;
-    ASSERT_EQ(0, butil::hostname2endpoint(g_hostname1, 80, &ep1));
-    ASSERT_EQ(0, butil::hostname2endpoint(g_hostname2, 80, &ep2));
+    butil::EndPoint ep;
+    ASSERT_EQ(0, butil::str2endpoint("127.0.0.1:0", &ep));
+    butil::fd_guard listener(butil::tcp_listen(ep));
+    ASSERT_LE(0, listener) << "errno=" << errno;
+    ASSERT_EQ(0, butil::get_local_side(listener, &ep));
+    // The local TCP handshake completes without a concurrent accept thread.
     {
-        butil::fd_guard sockfd(butil::tcp_connect(ep1, nullptr));
+        butil::fd_guard sockfd(butil::tcp_connect(ep, nullptr));
         ASSERT_LE(0, sockfd) << "errno=" << errno;
     }
     {
-        butil::fd_guard sockfd(butil::tcp_connect(ep1, nullptr, 1000));
+        butil::fd_guard sockfd(butil::tcp_connect(ep, nullptr, 5000));
         ASSERT_LE(0, sockfd) << "errno=" << errno;
     }
-    {
-        butil::fd_guard sockfd(butil::tcp_connect(ep2, nullptr, 1));
-        ASSERT_EQ(-1, sockfd) << "errno=" << errno;
-        ASSERT_EQ(ETIMEDOUT, errno);
-    }
 
-    {
-        struct sockaddr_storage serv_addr{};
-        socklen_t serv_addr_size = 0;
-        ASSERT_EQ(0, endpoint2sockaddr(ep1, &serv_addr, &serv_addr_size));
-        butil::fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 0));
-        ASSERT_LE(0, sockfd);
-        bool is_blocking = butil::is_blocking(sockfd);
-        ASSERT_EQ(0, butil::pthread_timed_connect(
-            sockfd, (struct sockaddr*) &serv_addr, serv_addr_size, nullptr));
-        ASSERT_EQ(is_blocking, butil::is_blocking(sockfd));
+    sockaddr_storage serv_addr{};
+    socklen_t serv_addr_size = 0;
+    ASSERT_EQ(0, endpoint2sockaddr(ep, &serv_addr, &serv_addr_size));
+    for (bool timed : {false, true}) {
+        for (bool blocking : {false, true}) {
+            butil::fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 
0));
+            ASSERT_LE(0, sockfd);
+            if (!blocking) {
+                ASSERT_EQ(0, butil::make_non_blocking(sockfd));
+            }
+            timespec deadline = butil::seconds_from_now(5);
+            ASSERT_EQ(0, butil::pthread_timed_connect(
+                sockfd, reinterpret_cast<sockaddr*>(&serv_addr), 
serv_addr_size,
+                timed ? &deadline : nullptr)) << "errno=" << errno;
+            ASSERT_EQ(blocking, butil::is_blocking(sockfd));
+            ASSERT_EQ(0, butil::is_connected(sockfd));
+        }
     }
+}
 
+#if defined(OS_LINUX)
+TEST(EndPointTest, tcp_connect_timeout_with_full_accept_queue) {
+    butil::EndPoint ep;
+    ASSERT_EQ(0, butil::str2endpoint("127.0.0.1:0", &ep));
+    butil::fd_guard listener(butil::tcp_listen(ep));
+    ASSERT_LE(0, listener);
+    // Linux allows one queued connection with backlog=0. Keep it unaccepted
+    // so subsequent handshakes must wait instead of relying on Internet 
latency.
+    ASSERT_EQ(0, listen(listener, 0));
+    ASSERT_EQ(0, butil::get_local_side(listener, &ep));
+    butil::fd_guard queued(butil::tcp_connect(ep, nullptr, 5000));
+    ASSERT_LE(0, queued) << "errno=" << errno;
     {
-        struct sockaddr_storage serv_addr{};
-        socklen_t serv_addr_size = 0;
-        ASSERT_EQ(0, endpoint2sockaddr(ep2, &serv_addr, &serv_addr_size));
+        butil::fd_guard sockfd(butil::tcp_connect(ep, nullptr, 300));
+        const int error = errno;
+        ASSERT_EQ(-1, sockfd);
+        // tcp_abort_on_overflow may reject the connection rather than time 
out.
+        ASSERT_TRUE(error == ETIMEDOUT || error == ECONNREFUSED)
+            << "errno=" << error;
+    }
+    sockaddr_storage serv_addr{};
+    socklen_t serv_addr_size = 0;
+    ASSERT_EQ(0, endpoint2sockaddr(ep, &serv_addr, &serv_addr_size));
+    for (bool blocking : {false, true}) {
         butil::fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 0));
         ASSERT_LE(0, sockfd);
-        bool is_blocking = butil::is_blocking(sockfd);
-        // In most cases, 1 millisecond will result in a connection timeout.
-        timespec abstime = butil::milliseconds_from_now(1);
+        if (!blocking) {
+            ASSERT_EQ(0, butil::make_non_blocking(sockfd));
+        }
+        timespec abstime = butil::milliseconds_from_now(300);
         const int rc = butil::pthread_timed_connect(
-            sockfd, (struct sockaddr*) &serv_addr,
+            sockfd, reinterpret_cast<sockaddr*>(&serv_addr),
             serv_addr_size, &abstime);
+        const int error = errno;
         ASSERT_EQ(-1, rc);
-        ASSERT_EQ(ETIMEDOUT, errno);
-        ASSERT_EQ(is_blocking, butil::is_blocking(sockfd));
+        ASSERT_TRUE(error == ETIMEDOUT || error == ECONNREFUSED)
+            << "errno=" << error;
+        ASSERT_EQ(blocking, butil::is_blocking(sockfd));
     }
 }
+#endif
 
-bool g_connect_startd = false;
-
-void TestConnectInterruptImpl(bool timed) {
-    butil::EndPoint ep;
-    ASSERT_EQ(0, butil::hostname2endpoint(g_hostname1, 80, &ep));
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+int WaitForPipe(int fd, const timespec* abstime) {
+#if defined(OS_LINUX)
+    return butil::pthread_fd_wait(fd, EPOLLIN, abstime);
+#else
+    return butil::pthread_fd_wait(fd, EVFILT_READ, abstime);
+#endif
+}
 
-    struct sockaddr_storage serv_addr{};
-    socklen_t serv_addr_size = 0;
-    ASSERT_EQ(0, endpoint2sockaddr(ep, &serv_addr, &serv_addr_size));
-    butil::fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 0));
-    ASSERT_LE(0, sockfd);
-
-    int rc;
-    if (timed) {
-        int64_t start_ms = butil::cpuwide_time_ms();
-        butil::tcp_connect(ep, nullptr);
-        int64_t connect_ms = butil::cpuwide_time_ms() - start_ms;
-        LOG(INFO) << "Connect to " << ep << ", cost " << connect_ms << "ms";
-
-        timespec abstime = butil::milliseconds_from_now(connect_ms * 10);
-        rc = butil::pthread_timed_connect(
-            sockfd, (struct sockaddr*) &serv_addr,
-            serv_addr_size, &abstime);
-    } else {
-        rc = butil::pthread_timed_connect(
-            sockfd, (struct sockaddr*) &serv_addr,
-            serv_addr_size, nullptr);
-    }
-    ASSERT_EQ(0, rc) << "errno=" << errno;
-    ASSERT_EQ(0, butil::is_connected(sockfd));
+TEST(EndPointTest, fd_wait_timeout) {
+    int fds[2];
+    ASSERT_EQ(0, pipe(fds));
+    butil::fd_guard reader(fds[0]);
+    butil::fd_guard writer(fds[1]);
+    timespec deadline = butil::milliseconds_from_now(300);
+    ASSERT_EQ(-1, WaitForPipe(reader, &deadline));
+    ASSERT_EQ(ETIMEDOUT, errno);
 }
 
-void* ConnectThread(void* arg) {
-    bool timed = *(bool*)arg;
-    TestConnectInterruptImpl(timed);
-    return nullptr;
+#if defined(OS_MACOSX)
+TEST(EndPointTest, fd_wait_kqueue_events) {
+    int fds[2];
+    ASSERT_EQ(0, pipe(fds));
+    butil::fd_guard reader(fds[0]);
+    butil::fd_guard writer(fds[1]);
+    timespec deadline = butil::seconds_from_now(5);
+    ASSERT_EQ(0, butil::pthread_fd_wait(writer, EVFILT_WRITE, &deadline));
+    ASSERT_EQ(0, butil::pthread_fd_wait(
+        writer, static_cast<unsigned>(EVFILT_WRITE), &deadline));
+
+    const char byte = 'x';
+    ASSERT_EQ(1, write(writer, &byte, sizeof(byte)));
+    ASSERT_EQ(0, butil::pthread_fd_wait(reader, EVFILT_READ, &deadline));
+    ASSERT_EQ(0, butil::pthread_fd_wait(
+        reader, static_cast<unsigned>(EVFILT_READ), &deadline));
+
+    ASSERT_EQ(-1, butil::pthread_fd_wait(reader, 0, &deadline));
+    EXPECT_EQ(EINVAL, errno);
+    ASSERT_EQ(-1, butil::pthread_fd_wait(reader, 0u, &deadline));
+    EXPECT_EQ(EINVAL, errno);
 }
+#endif
+
+volatile sig_atomic_t g_sigurg_received = 0;

Review Comment:
   This global has external linkage as written. Since it’s only used within 
this translation unit, make it `static` (or place it in an unnamed namespace) 
to avoid exporting a symbol unnecessarily and reduce the chance of accidental 
cross-TU references.



##########
test/run_tests.sh:
##########
@@ -46,6 +46,18 @@ if [ $test_num -eq 0 ]; then
 fi
 
 print_bt () {
+    case "${2-}" in
+        ''|*[!0-9]*) return ;;
+    esac
+    # Assertion failures exit normally. A passing death test may have left a
+    # core in this binary, so only consider statuses that can represent a 
signal.
+    if [ "$2" -le 128 ] || [ "$2" -gt 255 ]; then
+        return
+    fi
+    signal_number=$(expr "$2" - 128)

Review Comment:
   `expr` spawns an external process and is unnecessary here. Prefer shell 
arithmetic expansion (e.g., `signal_number=$((...))`) to reduce overhead and 
avoid differences in `expr` behavior across environments.



-- 
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