MemoryIdlerTest.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * Copyright 2014-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <folly/detail/MemoryIdler.h>
  17. #include <folly/portability/GMock.h>
  18. #include <folly/portability/GTest.h>
  19. #include <folly/synchronization/Baton.h>
  20. #include <memory>
  21. #include <thread>
  22. using namespace folly;
  23. using namespace folly::detail;
  24. using namespace testing;
  25. TEST(MemoryIdler, releaseStack) {
  26. MemoryIdler::unmapUnusedStack();
  27. }
  28. TEST(MemoryIdler, releaseStackMinExtra) {
  29. MemoryIdler::unmapUnusedStack(0);
  30. }
  31. TEST(MemoryIdler, releaseStackLargeExtra) {
  32. MemoryIdler::unmapUnusedStack(30000000);
  33. }
  34. TEST(MemoryIdler, releaseMallocTLS) {
  35. auto p = new int[4];
  36. MemoryIdler::flushLocalMallocCaches();
  37. delete[] p;
  38. MemoryIdler::flushLocalMallocCaches();
  39. p = new int[4];
  40. MemoryIdler::flushLocalMallocCaches();
  41. delete[] p;
  42. }
  43. /// MockClock is a bit tricky because we are mocking a static function
  44. /// (now()), so we need to find the corresponding mock instance without
  45. /// extending its scope beyond that of the test. I generally avoid
  46. /// shared_ptr, but a weak_ptr is just the ticket here
  47. struct MockClock {
  48. using duration = std::chrono::steady_clock::duration;
  49. using time_point = std::chrono::time_point<MockClock, duration>;
  50. MOCK_METHOD0(nowImpl, time_point());
  51. /// Hold on to the returned shared_ptr until the end of the test
  52. static std::shared_ptr<StrictMock<MockClock>> setup() {
  53. auto rv = std::make_shared<StrictMock<MockClock>>();
  54. s_mockClockInstance = rv;
  55. return rv;
  56. }
  57. static time_point now() {
  58. return s_mockClockInstance.lock()->nowImpl();
  59. }
  60. static std::weak_ptr<StrictMock<MockClock>> s_mockClockInstance;
  61. };
  62. std::weak_ptr<StrictMock<MockClock>> MockClock::s_mockClockInstance;
  63. static auto const forever = MockClock::time_point::max();
  64. /// MockedAtom gives us a way to select a mocked Futex implementation
  65. /// inside Baton, even though the atom itself isn't exercised by the
  66. /// mocked futex
  67. ///
  68. /// Futex<MockAtom> is our mocked futex implementation. Note that the method
  69. /// signatures differ from the real Futex because we have elided unused default
  70. /// params and collapsed templated methods into the used type
  71. template <typename T>
  72. struct MockAtom : public std::atomic<T> {
  73. explicit MockAtom(T init = 0) : std::atomic<T>(init) {}
  74. MOCK_CONST_METHOD2(futexWait, FutexResult(uint32_t, uint32_t));
  75. MOCK_CONST_METHOD3(
  76. futexWaitUntil,
  77. FutexResult(uint32_t, const MockClock::time_point&, uint32_t));
  78. };
  79. FutexResult
  80. futexWait(const Futex<MockAtom>* futex, uint32_t expected, uint32_t waitMask) {
  81. return futex->futexWait(expected, waitMask);
  82. }
  83. template <typename Clock, typename Duration>
  84. FutexResult futexWaitUntil(
  85. const Futex<MockAtom>* futex,
  86. std::uint32_t expected,
  87. std::chrono::time_point<Clock, Duration> const& deadline,
  88. uint32_t waitMask) {
  89. return futex->futexWaitUntil(expected, deadline, waitMask);
  90. }
  91. TEST(MemoryIdler, futexWaitValueChangedEarly) {
  92. Futex<MockAtom> fut;
  93. auto clock = MockClock::setup();
  94. auto begin = MockClock::time_point(std::chrono::seconds(100));
  95. auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
  96. EXPECT_CALL(*clock, nowImpl()).WillOnce(Return(begin));
  97. EXPECT_CALL(
  98. fut,
  99. futexWaitUntil(
  100. 1, AllOf(Ge(begin + idleTimeout), Lt(begin + 2 * idleTimeout)), -1))
  101. .WillOnce(Return(FutexResult::VALUE_CHANGED));
  102. EXPECT_EQ(
  103. FutexResult::VALUE_CHANGED, MemoryIdler::futexWaitUntil(fut, 1, forever));
  104. }
  105. TEST(MemoryIdler, futexWaitValueChangedLate) {
  106. Futex<MockAtom> fut;
  107. auto clock = MockClock::setup();
  108. auto begin = MockClock::time_point(std::chrono::seconds(100));
  109. auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
  110. EXPECT_CALL(*clock, nowImpl()).WillOnce(Return(begin));
  111. EXPECT_CALL(
  112. fut,
  113. futexWaitUntil(
  114. 1, AllOf(Ge(begin + idleTimeout), Lt(begin + 2 * idleTimeout)), -1))
  115. .WillOnce(Return(FutexResult::TIMEDOUT));
  116. EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
  117. .WillOnce(Return(FutexResult::VALUE_CHANGED));
  118. EXPECT_EQ(
  119. FutexResult::VALUE_CHANGED, MemoryIdler::futexWaitUntil(fut, 1, forever));
  120. }
  121. TEST(MemoryIdler, futexWaitAwokenEarly) {
  122. Futex<MockAtom> fut;
  123. auto clock = MockClock::setup();
  124. auto begin = MockClock::time_point(std::chrono::seconds(100));
  125. auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
  126. EXPECT_CALL(*clock, nowImpl()).WillOnce(Return(begin));
  127. EXPECT_CALL(fut, futexWaitUntil(1, Ge(begin + idleTimeout), -1))
  128. .WillOnce(Return(FutexResult::AWOKEN));
  129. EXPECT_EQ(FutexResult::AWOKEN, MemoryIdler::futexWaitUntil(fut, 1, forever));
  130. }
  131. TEST(MemoryIdler, futexWaitAwokenLate) {
  132. Futex<MockAtom> fut;
  133. auto clock = MockClock::setup();
  134. auto begin = MockClock::time_point(std::chrono::seconds(100));
  135. auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
  136. EXPECT_CALL(*clock, nowImpl()).WillOnce(Return(begin));
  137. EXPECT_CALL(fut, futexWaitUntil(1, begin + idleTimeout, -1))
  138. .WillOnce(Return(FutexResult::TIMEDOUT));
  139. EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
  140. .WillOnce(Return(FutexResult::AWOKEN));
  141. EXPECT_EQ(
  142. FutexResult::AWOKEN,
  143. MemoryIdler::futexWaitUntil(fut, 1, forever, -1, idleTimeout, 100, 0.0f));
  144. }
  145. TEST(MemoryIdler, futexWaitImmediateFlush) {
  146. Futex<MockAtom> fut;
  147. auto clock = MockClock::setup();
  148. EXPECT_CALL(fut, futexWaitUntil(2, forever, 0xff))
  149. .WillOnce(Return(FutexResult::AWOKEN));
  150. EXPECT_EQ(
  151. FutexResult::AWOKEN,
  152. MemoryIdler::futexWaitUntil(
  153. fut, 2, forever, 0xff, std::chrono::seconds(0)));
  154. }
  155. TEST(MemoryIdler, futexWaitNeverFlush) {
  156. Futex<MockAtom> fut;
  157. auto clock = MockClock::setup();
  158. EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
  159. .WillOnce(Return(FutexResult::AWOKEN));
  160. EXPECT_EQ(
  161. FutexResult::AWOKEN,
  162. MemoryIdler::futexWaitUntil(
  163. fut, 1, forever, -1, std::chrono::seconds(-7)));
  164. }