TestHangs.cpp (4569B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Test various cases of behavior when a synchronous method call times out. 9 */ 10 11 #include "gtest/gtest.h" 12 13 #include "mozilla/_ipdltest/IPDLUnitTest.h" 14 #include "mozilla/_ipdltest/PTestHangsChild.h" 15 #include "mozilla/_ipdltest/PTestHangsParent.h" 16 #include "mozilla/ipc/CrossProcessSemaphore.h" 17 18 using namespace mozilla::ipc; 19 20 namespace mozilla::_ipdltest { 21 22 enum class HangMode : uint32_t { 23 /// No hang should occur. 24 None, 25 /// The synchronous call should time out. 26 Hang, 27 /// The synchronous call should time out but the response should still be 28 /// received (racing with the reply timeout logic). 29 HangButReceive, 30 /// The synchronous call should time out but the response should still be 31 /// received because the child indicates that processing should continue after 32 /// timeout. 33 HangPermitted 34 }; 35 36 class TestHangsChild : public PTestHangsChild { 37 NS_INLINE_DECL_REFCOUNTING(TestHangsChild, override) 38 39 TestHangsChild() : timeout(CrossProcessSemaphore::Create("timeout", 0)) {} 40 41 private: 42 IPCResult RecvStart(const uint32_t& hangMode, 43 StartResolver&& resolve) final override { 44 // Minimum possible, 2 ms. We want to detect a hang to race with the reply 45 // coming in, as reliably as possible. After 1ms the wait for a response 46 // will be retried. 47 SetReplyTimeoutMs(2); 48 49 this->hangMode = (HangMode)hangMode; 50 51 auto result = SendHang(hangMode, timeout->CloneHandle()); 52 // Only the `Hang` mode should actually fail. 53 if (this->hangMode == HangMode::Hang) { 54 // See description in parent. 55 timeout->Signal(); 56 EXPECT_FALSE(result); 57 } else { 58 EXPECT_TRUE(result); 59 } 60 61 resolve(detectedHang); 62 63 return IPC_OK(); 64 } 65 66 bool ShouldContinueFromReplyTimeout() final override { 67 timeout->Signal(); 68 detectedHang = true; 69 70 if (hangMode == HangMode::HangButReceive) { 71 // Wait until the transaction is complete so that we can still receive the 72 // result after returning. 73 while (!GetIPCChannel()->TestOnlyIsTransactionComplete()) { 74 PR_Sleep(ticksPerSecond / 1000); 75 } 76 } 77 78 // Return true if `HangPermitted` mode (allowing the receive loop to 79 // continue). 80 return hangMode == HangMode::HangPermitted; 81 } 82 83 ~TestHangsChild() = default; 84 85 HangMode hangMode = HangMode::None; 86 uint32_t ticksPerSecond = PR_TicksPerSecond(); 87 UniquePtr<CrossProcessSemaphore> timeout; 88 89 public: 90 bool detectedHang = false; 91 }; 92 93 class TestHangsParent : public PTestHangsParent { 94 NS_INLINE_DECL_REFCOUNTING(TestHangsParent, override) 95 96 private: 97 IPCResult RecvHang( 98 const uint32_t& hangMode, 99 CrossProcessSemaphoreHandle&& timeout_handle) final override { 100 UniquePtr<CrossProcessSemaphore> timeout( 101 CrossProcessSemaphore::Create(std::move(timeout_handle))); 102 if (hangMode != (uint32_t)HangMode::None) { 103 // Wait to ensure the child process has called 104 // ShouldContinueFromReplyTimeout(). 105 timeout->Wait(); 106 107 if (hangMode == (uint32_t)HangMode::Hang) { 108 // Wait to ensure the child process has returned from `SendHang()`, 109 // otherwise the reply message can race with the processing after 110 // ShouldContinueFromReplyTimeout(). 111 timeout->Wait(); 112 } 113 } 114 return IPC_OK(); 115 } 116 117 ~TestHangsParent() = default; 118 }; 119 120 // We can verify that the Start message callbacks are run with the `Close()` 121 // calls; without a `Close()`, the test will hang. 122 123 #define TEST_HANGS(mode) \ 124 IPDL_TEST(TestHangs, mode) { \ 125 mActor->SendStart( \ 126 (uint32_t)HangMode::mode, \ 127 [=](bool detectedHang) { \ 128 EXPECT_EQ(detectedHang, HangMode::mode != HangMode::None); \ 129 mActor->Close(); \ 130 }, \ 131 [](auto&& reason) { FAIL() << "failed to send start"; }); \ 132 } 133 134 TEST_HANGS(None) 135 TEST_HANGS(Hang) 136 TEST_HANGS(HangButReceive) 137 TEST_HANGS(HangPermitted) 138 139 #undef TEST_HANGS 140 141 } // namespace mozilla::_ipdltest