chenBright commented on code in PR #3535:
URL: https://github.com/apache/brpc/pull/3535#discussion_r4463216090


##########
test/brpc_ubring_unittest.cpp:
##########
@@ -934,6 +1887,367 @@ TEST(UBRingRecvChunkHeaderTest, 
crafted_chunk_is_rejected) {
         chunk.header[UBR_MSG_LEN_INDEX], chunk.header[UBR_MSG_CUR_INDEX]));
 }
 
+TEST_F(UBShmHandshakeTest, 
negative_ack_after_server_allocation_serves_tcp_without_delay) {
+    ASSERT_NO_FATAL_FAILURE(PrepareUntilAck("negative_ack_", 1));
+
+    ASSERT_NO_FATAL_FAILURE(SendAndCheckUBFallbackRPC(peer, true, 301));
+    EXPECT_EQ(brpc::handshake::FALLBACK_TCP, adapter->handshake_phase());
+    auto* ep = brpc::UBShmTransport::Get(server_socket.get())->GetUBShmEp();
+    EXPECT_EQ(nullptr, ep->_ub_ring);
+    EXPECT_EQ(brpc::INVALID_SOCKET_ID, ep->_poller_sid);
+    ASSERT_NO_FATAL_FAILURE(SendAndCheckUBFallbackRPC(peer, false, 302));
+    client_transport->DeactivateUpgrade();
+}
+
+#if defined(OS_LINUX)
+TEST_F(UBShmEndpointTest, 
cancel_waits_for_callback_before_releasing_descriptor) {
+    using namespace brpc::ubring;
+    struct Context {
+        bthread::CountdownEvent entered;
+        bthread::CountdownEvent release;
+    } context;
+    itimerspec spec{};
+    spec.it_value.tv_nsec = 1;
+    const int fd = TimerStart(&spec, [](void* arg) -> void* {
+        auto* ctx = static_cast<Context*>(arg);
+        ctx->entered.signal();
+        ctx->release.wait();
+        return nullptr;
+    }, &context);
+    ASSERT_GE(fd, 0);
+    bool released = false;
+    BRPC_SCOPE_EXIT {
+        if (!released) { context.release.signal(); }
+        DeleteTimerSafe(fd);
+    };
+    ASSERT_EQ(0, 
context.entered.timed_wait(butil::milliseconds_from_now(1000)));
+    bthread::CountdownEvent cancel_started;
+    bthread::CountdownEvent canceled;
+    std::thread cancel([&] {
+        cancel_started.signal();
+        DeleteTimerSafe(fd);
+        canceled.signal();
+    });
+    cancel_started.wait();
+    const int wait_result = 
canceled.timed_wait(butil::milliseconds_from_now(100));
+    spec.it_value.tv_nsec = 0;
+    spec.it_value.tv_sec = 10;
+    const int replacement = TimerStart(&spec, [](void*) -> void* {
+        return nullptr;
+    }, nullptr);
+    context.release.signal();
+    released = true;
+    cancel.join();
+    EXPECT_EQ(ETIMEDOUT, wait_result);
+    EXPECT_NE(fd, replacement);
+    EXPECT_GE(fcntl(replacement, F_GETFD), 0);
+    DeleteTimerSafe(replacement);
+}
+
+TEST_F(UBShmEndpointTest, callback_can_cancel_its_own_periodic_timer) {
+    using namespace brpc::ubring;
+    struct Context {
+        bthread::CountdownEvent ready;
+        bthread::CountdownEvent done;
+        int fd;
+    } context;
+    itimerspec spec{};
+    spec.it_value.tv_nsec = 1;
+    spec.it_interval.tv_sec = 10;
+    context.fd = TimerStart(&spec, [](void* arg) -> void* {
+        auto* ctx = static_cast<Context*>(arg);
+        ctx->ready.wait();
+        DeleteTimerSafe(ctx->fd);
+        ctx->done.signal();
+        return nullptr;
+    }, &context);
+    ASSERT_GE(context.fd, 0);
+    context.ready.signal();
+    EXPECT_EQ(0, context.done.timed_wait(butil::milliseconds_from_now(1000)));
+    DeleteTimerSafe(context.fd);
+    EXPECT_EQ(-1, fcntl(context.fd, F_GETFD));
+}
+#endif
+
+
+namespace {
+struct UbsCleanupState {
+    uint8_t local = 0;
+    uint8_t remote = 0;
+    std::atomic<int> unmap_error{UBSM_ERR_NET};
+    std::atomic<int> deallocate_error{0};
+    std::atomic<int> unmaps{0};
+    std::atomic<int> remote_unmaps{0};
+    std::atomic<int> deallocates{0};
+    std::atomic<int> finalizes{0};
+    std::atomic<bool> block_unmap{false};
+    std::atomic<bool> in_unmap{false};
+    std::atomic<bool> finalized_during_unmap{false};
+    bthread::CountdownEvent entered;
+    bthread::CountdownEvent resume;
+};
+UbsCleanupState* ubs_cleanup = nullptr;
+}
+
+class UbsCleanupTest : public UBShmEndpointTest {
+protected:
+    void SetUp() override {
+        using namespace brpc::ubring;
+        UBShmEndpointTest::SetUp();
+        saved_type = GetShmType();
+        saved_list = g_shm_list;
+        saved_fd = g_shm_timer_fd;
+        saved_unmap = ubsmem_shmem_unmap;
+        saved_deallocate = ubsmem_shmem_deallocate;
+        saved_finalize = ubsmem_finalize;
+        saved_delay = FLAGS_ub_flying_io_timeout_s;
+        list = static_cast<ShmList*>(calloc(1, sizeof(ShmList)));
+        ASSERT_NE(nullptr, list);
+        ASSERT_EQ(0, pthread_mutex_init(&list->shm_lock, nullptr));
+        g_shm_list = list;
+        g_shm_timer_fd = 0;
+        FLAGS_ub_flying_io_timeout_s = 1;
+        ubs_cleanup = &state;
+        SetShmType(SHM_TYPE_UBS);
+        ubsmem_shmem_unmap = [](void* address, size_t) {
+            auto& state = *ubs_cleanup;
+            ++state.unmaps;
+            if (address == &state.remote) {
+                ++state.remote_unmaps;
+            }
+            if (state.block_unmap.load()) {
+                state.in_unmap.store(true);
+                state.entered.signal();
+                state.resume.wait();
+                state.in_unmap.store(false);
+            }
+            return state.unmap_error.load();
+        };
+        ubsmem_shmem_deallocate = [](const char*) {
+            ++ubs_cleanup->deallocates;
+            return ubs_cleanup->deallocate_error.load();
+        };
+        ubsmem_finalize = []() {
+            ++ubs_cleanup->finalizes;
+            ubs_cleanup->finalized_during_unmap.store(
+                ubs_cleanup->in_unmap.load());
+            return 0;
+        };
+    }
+    void TearDown() override {
+        using namespace brpc::ubring;
+        if (g_shm_list != nullptr) {
+            DestroyShmTimer(g_shm_list);
+        }
+        g_shm_list = saved_list;
+        g_shm_timer_fd = saved_fd;
+        FLAGS_ub_flying_io_timeout_s = saved_delay;
+        ubsmem_shmem_unmap = saved_unmap;
+        ubsmem_shmem_deallocate = saved_deallocate;
+        ubsmem_finalize = saved_finalize;
+        SetShmType(saved_type);
+        ubs_cleanup = nullptr;
+        UBShmEndpointTest::TearDown();
+    }
+    brpc::ubring::SHM Mapping(bool remote = false) {
+        brpc::ubring::SHM shm{};
+        shm.addr = remote ? &state.remote : &state.local;
+        shm.len = 4 * 1024 * 1024;
+        strcpy(shm.name, remote ? "ubs_remote" : "ubs_local");
+        return shm;
+    }
+    UbsCleanupState state;
+    brpc::ubring::ShmList* list = nullptr;
+    brpc::ubring::ShmList* saved_list = nullptr;
+    int saved_fd = 0;
+    int saved_delay = 0;
+    brpc::ubring::SHM_TYPE saved_type;
+    decltype(brpc::ubring::ubsmem_shmem_unmap) saved_unmap;
+    decltype(brpc::ubring::ubsmem_shmem_deallocate) saved_deallocate;
+    decltype(brpc::ubring::ubsmem_finalize) saved_finalize;
+};
+
+TEST_F(UbsCleanupTest, 
abort_hands_off_resources_before_reusing_transaction_slot) {
+    using namespace brpc::ubring;
+    UBRing ring;
+    ASSERT_EQ(UBRING_OK, UBRingManager::AcquireUbrTrxFromMgr(&ring._trx));
+    UbrTrx* old_trx = ring._trx;
+    BRPC_SCOPE_EXIT {
+        if (ring._trx != nullptr) {
+            ring.ReleaseTrx();
+        }
+    };
+    old_trx->local_shm = Mapping();
+    UbrEventQMsg event{};
+    event.flag = UBR_STATE_CONNECTED;
+    old_trx->ubr_tx.local_tx_event_q.addr = reinterpret_cast<uint8_t*>(&event);
+    ASSERT_EQ(UBRING_OK, ring.UbrTrxAbort());
+    EXPECT_EQ(nullptr, ring._trx);
+    EXPECT_EQ(0u, UBRingManager::g_ubr_mgr.trx_num);
+    ASSERT_EQ(1u, list->size);
+
+    UBRing replacement;
+    ASSERT_EQ(UBRING_OK, 
UBRingManager::AcquireUbrTrxFromMgr(&replacement._trx));
+    BRPC_SCOPE_EXIT { replacement.ReleaseTrx(); };
+    EXPECT_EQ(old_trx, replacement._trx);
+    state.unmap_error.store(0);
+    UbsShmCallback(list);
+    EXPECT_EQ(0u, list->size);
+    EXPECT_EQ(1, state.deallocates.load());
+    EXPECT_EQ(1u, UBRingManager::g_ubr_mgr.trx_num);
+    EXPECT_EQ(1, ATOMIC_LOAD(replacement._trx->ref_count));
+}
+
+TEST_F(UbsCleanupTest, passive_cleanup_retries_both_mappings_independently) {
+    using namespace brpc::ubring;
+    UBRing ring;
+    ASSERT_EQ(UBRING_OK, UBRingManager::AcquireUbrTrxFromMgr(&ring._trx));
+    ring._trx->local_shm = Mapping();
+    ring._trx->remote_shm = Mapping(true);
+    ring._trx->passive_close = true;
+    ring._trx->close_state = UBR_CLOSE_SECOND;
+    ring._trx->ref_count = 2;
+    UBRing::UbrAsynClearCallback(ring._trx);
+    ASSERT_EQ(UBRING_OK, ring.UbrTrxClose());
+    EXPECT_EQ(0u, UBRingManager::g_ubr_mgr.trx_num);
+    EXPECT_EQ(1, state.remote_unmaps.load());
+    ASSERT_EQ(2u, list->size);
+    // A completed remote task is removed even while the local task still 
fails.
+    state.unmap_error.store(0);
+    state.deallocate_error.store(UBSM_ERR_NET);
+    ubsmem_shmem_deallocate = [](const char* name) {
+        ++ubs_cleanup->deallocates;
+        return strcmp(name, "ubs_local") == 0
+            ? ubs_cleanup->deallocate_error.load() : 0;
+    };
+    UbsShmCallback(list);
+    EXPECT_EQ(1u, list->size);
+    state.deallocate_error.store(0);
+    UbsShmCallback(list);
+    EXPECT_EQ(0u, list->size);
+    EXPECT_EQ(4, state.unmaps.load());
+    EXPECT_EQ(3, state.deallocates.load());
+}
+
+TEST_F(UbsCleanupTest, 
retry_resumes_at_deallocate_and_accepts_already_deleted_names) {
+    using namespace brpc::ubring;
+    state.unmap_error.store(0);
+    state.deallocate_error.store(UBSM_ERR_IN_USING);
+    SHM shm = Mapping();
+    ASSERT_EQ(UBRING_OK, UbsShmLocalFree(&shm));
+    EXPECT_EQ(nullptr, shm.addr);
+    UbsShmCallback(list);
+    EXPECT_EQ(1u, list->size);
+    state.deallocate_error.store(UBSM_ERR_NOT_FOUND);
+    UbsShmCallback(list);
+    EXPECT_EQ(0u, list->size);
+    EXPECT_EQ(1, state.unmaps.load());
+    EXPECT_EQ(3, state.deallocates.load());
+}
+
+TEST_F(UbsCleanupTest, 
remote_retry_keeps_distinct_mappings_and_never_deallocates) {
+    using namespace brpc::ubring;
+    SHM first = Mapping();
+    SHM second = Mapping(true);
+    strcpy(second.name, first.name);
+    ASSERT_EQ(UBRING_OK, UbsShmRemoteFree(&first));
+    ASSERT_EQ(UBRING_OK, UbsShmRemoteFree(&second));
+    ASSERT_EQ(2u, list->size);
+    state.unmap_error.store(0);
+    UbsShmCallback(list);
+    EXPECT_EQ(0u, list->size);
+    EXPECT_EQ(4, state.unmaps.load());
+    EXPECT_EQ(0, state.deallocates.load());
+}
+
+TEST_F(UbsCleanupTest, duplicate_handoff_preserves_completed_operations) {
+    using namespace brpc::ubring;
+    SHM remote = Mapping();
+    SHM local = remote;
+    ASSERT_EQ(UBRING_OK, UbsShmRemoteFree(&remote));
+    state.unmap_error.store(0);
+    ASSERT_EQ(UBRING_OK, UbsShmLocalFree(&local));
+    EXPECT_EQ(1u, list->size);
+    EXPECT_EQ(1, state.unmaps.load());
+    UbsShmCallback(list);
+    EXPECT_EQ(0u, list->size);
+    EXPECT_EQ(2, state.unmaps.load());
+    EXPECT_EQ(1, state.deallocates.load());
+}
+
+TEST_F(UbsCleanupTest, rejected_handoff_keeps_a_cleanup_reference_and_retries) 
{
+    using namespace brpc::ubring;
+    UBRing ring;
+    ASSERT_EQ(UBRING_OK, UBRingManager::AcquireUbrTrxFromMgr(&ring._trx));
+    ring._trx->local_shm = Mapping();
+    UbrEventQMsg event{};
+    event.flag = UBR_STATE_CONNECTED;
+    ring._trx->ubr_tx.local_tx_event_q.addr = 
reinterpret_cast<uint8_t*>(&event);
+    g_shm_list = nullptr;
+    const RETURN_CODE rc = ring.UbrTrxAbort();
+    EXPECT_TRUE(WaitForUBCondition([&] { return state.unmaps.load() >= 2; }));
+    g_shm_list = list;
+    state.unmap_error.store(0);
+    EXPECT_EQ(UBRING_OK, rc);
+    EXPECT_EQ(nullptr, ring._trx);
+    EXPECT_EQ(1u, UBRingManager::g_ubr_mgr.trx_num);

Review Comment:
   `unmaps >= 2` only shows that the retry entered the stub function: the 
counter is incremented on line 2014, but the return value is not read until 
line 2024. The test thread may execute `unmap_error.store(0)` between these two 
steps, causing that retry to succeed immediately and release the last 
transaction reference. The `EXPECT_EQ(1u, trx_num)` here would then fail 
spuriously because the actual value is 0. Please use a barrier to pause the 
retry callback, first assert that the reference is still retained, then restore 
the success return value and release the callback, and finally synchronize 
before checking that the transaction count reaches zero.



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