summaryrefslogtreecommitdiffstats
path: root/ipc/ipdl/test/gtest/TestHangs.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
commitfbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch)
tree4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /ipc/ipdl/test/gtest/TestHangs.cpp
parentReleasing progress-linux version 124.0.1-1~progress7.99u1. (diff)
downloadfirefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz
firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/ipdl/test/gtest/TestHangs.cpp')
-rw-r--r--ipc/ipdl/test/gtest/TestHangs.cpp133
1 files changed, 133 insertions, 0 deletions
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<CrossProcessSemaphore> 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<CrossProcessSemaphore> 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