From fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:14:29 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- ipc/ipdl/test/gtest/TestHangs.cpp | 133 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 ipc/ipdl/test/gtest/TestHangs.cpp (limited to 'ipc/ipdl/test/gtest/TestHangs.cpp') diff --git a/ipc/ipdl/test/gtest/TestHangs.cpp b/ipc/ipdl/test/gtest/TestHangs.cpp new file mode 100644 index 0000000000..ddc4a0c7d0 --- /dev/null +++ b/ipc/ipdl/test/gtest/TestHangs.cpp @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test various cases of behavior when a synchronous method call times out. + */ + +#include "gtest/gtest.h" + +#include "mozilla/_ipdltest/IPDLUnitTest.h" +#include "mozilla/_ipdltest/PTestHangsChild.h" +#include "mozilla/_ipdltest/PTestHangsParent.h" +#include "mozilla/ipc/CrossProcessSemaphore.h" + +using namespace mozilla::ipc; + +namespace mozilla::_ipdltest { + +enum class HangMode : uint32_t { + /// No hang should occur. + None, + /// The synchronous call should time out. + Hang, + /// The synchronous call should time out but the response should still be + /// received + /// (racing with the reply timeout logic). + HangButReceive, + /// The synchronous call should time out but the response should still be + /// received because the child indicates that processing should continue after + /// timeout. + HangPermitted +}; + +class TestHangsChild : public PTestHangsChild { + NS_INLINE_DECL_REFCOUNTING(TestHangsChild, override) + + TestHangsChild() : timeout(CrossProcessSemaphore::Create("timeout", 0)) {} + + private: + IPCResult RecvStart(const uint32_t& hangMode, + StartResolver&& resolve) final override { + // Minimum possible, 2 ms. We want to detect a hang to race with the reply + // coming in, as reliably as possible. After 1ms the wait for a response + // will be retried. + SetReplyTimeoutMs(2); + + this->hangMode = (HangMode)hangMode; + + auto result = SendHang(hangMode, timeout->CloneHandle()); + if (this->hangMode == HangMode::Hang) { + // Only the `Hang` mode should actually fail. + EXPECT_FALSE(result); + } else { + EXPECT_TRUE(result); + } + + resolve(detectedHang); + + return IPC_OK(); + } + + bool ShouldContinueFromReplyTimeout() final override { + timeout->Signal(); + detectedHang = true; + + if (hangMode == HangMode::HangButReceive) { + // Wait until the transaction is complete so that we can still receive the + // result after returning. + while (!GetIPCChannel()->TestOnlyIsTransactionComplete()) { + PR_Sleep(ticksPerSecond / 1000); + } + } + + // Return true if `HangPermitted` mode (allowing the receive loop to + // continue). + return hangMode == HangMode::HangPermitted; + } + + ~TestHangsChild() = default; + + HangMode hangMode = HangMode::None; + uint32_t ticksPerSecond = PR_TicksPerSecond(); + UniquePtr timeout; + + public: + bool detectedHang = false; +}; + +class TestHangsParent : public PTestHangsParent { + NS_INLINE_DECL_REFCOUNTING(TestHangsParent, override) + + private: + IPCResult RecvHang( + const uint32_t& hangMode, + CrossProcessSemaphoreHandle&& timeout_handle) final override { + UniquePtr timeout( + CrossProcessSemaphore::Create(std::move(timeout_handle))); + if (hangMode != (uint32_t)HangMode::None) { + // Wait to ensure the child process has called + // ShouldContinueFromReplyTimeout(). + timeout->Wait(); + } + return IPC_OK(); + } + + ~TestHangsParent() = default; +}; + +// We can verify that the Start message callbacks are run with the `Close()` +// calls; without a `Close()`, the test will hang. + +#define TEST_HANGS(mode) \ + IPDL_TEST(TestHangs, mode) { \ + mActor->SendStart( \ + (uint32_t)HangMode::mode, \ + [=](bool detectedHang) { \ + EXPECT_EQ(detectedHang, HangMode::mode != HangMode::None); \ + mActor->Close(); \ + }, \ + [](auto&& reason) { FAIL() << "failed to send start"; }); \ + } + +TEST_HANGS(None) +TEST_HANGS(Hang) +TEST_HANGS(HangButReceive) +TEST_HANGS(HangPermitted) + +#undef TEST_HANGS + +} // namespace mozilla::_ipdltest -- cgit v1.2.3