2 * Copyright 2014-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/detail/MemoryIdler.h>
19 #include <folly/portability/GMock.h>
20 #include <folly/portability/GTest.h>
21 #include <folly/synchronization/Baton.h>
26 using namespace folly;
27 using namespace folly::detail;
28 using namespace testing;
30 TEST(MemoryIdler, releaseStack) {
31 MemoryIdler::unmapUnusedStack();
34 TEST(MemoryIdler, releaseStackMinExtra) {
35 MemoryIdler::unmapUnusedStack(0);
38 TEST(MemoryIdler, releaseStackLargeExtra) {
39 MemoryIdler::unmapUnusedStack(30000000);
42 TEST(MemoryIdler, releaseMallocTLS) {
44 MemoryIdler::flushLocalMallocCaches();
46 MemoryIdler::flushLocalMallocCaches();
48 MemoryIdler::flushLocalMallocCaches();
53 /// MockedAtom gives us a way to select a mocked Futex implementation
54 /// inside Baton, even though the atom itself isn't exercised by the
57 struct MockAtom : public std::atomic<T> {
58 explicit MockAtom(T init = 0) : std::atomic<T>(init) {}
62 /// MockClock is a bit tricky because we are mocking a static function
63 /// (now()), so we need to find the corresponding mock instance without
64 /// extending its scope beyond that of the test. I generally avoid
65 /// shared_ptr, but a weak_ptr is just the ticket here
67 using duration = std::chrono::steady_clock::duration;
68 using time_point = std::chrono::time_point<MockClock, duration>;
70 MOCK_METHOD0(nowImpl, time_point());
72 /// Hold on to the returned shared_ptr until the end of the test
73 static std::shared_ptr<StrictMock<MockClock>> setup() {
74 auto rv = std::make_shared<StrictMock<MockClock>>();
75 s_mockClockInstance = rv;
79 static time_point now() {
80 return s_mockClockInstance.lock()->nowImpl();
83 static std::weak_ptr<StrictMock<MockClock>> s_mockClockInstance;
86 std::weak_ptr<StrictMock<MockClock>> MockClock::s_mockClockInstance;
90 namespace folly { namespace detail {
92 /// Futex<MockAtom> is our mocked futex implementation. Note that the
93 /// method signatures differ from the real Futex because we have elided
94 /// unused default params and collapsed templated methods into the
97 struct Futex<MockAtom> {
98 MOCK_METHOD2(futexWait, FutexResult(uint32_t, uint32_t));
99 MOCK_METHOD3(futexWaitUntil,
100 FutexResult(uint32_t, const MockClock::time_point&, uint32_t));
103 } // namespace detail
106 static auto const forever = MockClock::time_point::max();
108 TEST(MemoryIdler, futexWaitValueChangedEarly) {
109 StrictMock<Futex<MockAtom>> fut;
110 auto clock = MockClock::setup();
111 auto begin = MockClock::time_point(std::chrono::seconds(100));
112 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
114 EXPECT_CALL(*clock, nowImpl())
115 .WillOnce(Return(begin));
116 EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
117 Lt(begin + 2 * idleTimeout)), -1))
118 .WillOnce(Return(FutexResult::VALUE_CHANGED));
120 FutexResult::VALUE_CHANGED, MemoryIdler::futexWaitUntil(fut, 1, forever));
123 TEST(MemoryIdler, futexWaitValueChangedLate) {
124 StrictMock<Futex<MockAtom>> fut;
125 auto clock = MockClock::setup();
126 auto begin = MockClock::time_point(std::chrono::seconds(100));
127 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
129 EXPECT_CALL(*clock, nowImpl())
130 .WillOnce(Return(begin));
131 EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
132 Lt(begin + 2 * idleTimeout)), -1))
133 .WillOnce(Return(FutexResult::TIMEDOUT));
134 EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
135 .WillOnce(Return(FutexResult::VALUE_CHANGED));
137 FutexResult::VALUE_CHANGED, MemoryIdler::futexWaitUntil(fut, 1, forever));
140 TEST(MemoryIdler, futexWaitAwokenEarly) {
141 StrictMock<Futex<MockAtom>> fut;
142 auto clock = MockClock::setup();
143 auto begin = MockClock::time_point(std::chrono::seconds(100));
144 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
146 EXPECT_CALL(*clock, nowImpl())
147 .WillOnce(Return(begin));
148 EXPECT_CALL(fut, futexWaitUntil(1, Ge(begin + idleTimeout), -1))
149 .WillOnce(Return(FutexResult::AWOKEN));
150 EXPECT_EQ(FutexResult::AWOKEN, MemoryIdler::futexWaitUntil(fut, 1, forever));
153 TEST(MemoryIdler, futexWaitAwokenLate) {
154 StrictMock<Futex<MockAtom>> fut;
155 auto clock = MockClock::setup();
156 auto begin = MockClock::time_point(std::chrono::seconds(100));
157 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
159 EXPECT_CALL(*clock, nowImpl())
160 .WillOnce(Return(begin));
161 EXPECT_CALL(fut, futexWaitUntil(1, begin + idleTimeout, -1))
162 .WillOnce(Return(FutexResult::TIMEDOUT));
163 EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
164 .WillOnce(Return(FutexResult::AWOKEN));
167 MemoryIdler::futexWaitUntil(fut, 1, forever, -1, idleTimeout, 100, 0.0f));
170 TEST(MemoryIdler, futexWaitImmediateFlush) {
171 StrictMock<Futex<MockAtom>> fut;
172 auto clock = MockClock::setup();
174 EXPECT_CALL(fut, futexWaitUntil(2, forever, 0xff))
175 .WillOnce(Return(FutexResult::AWOKEN));
178 MemoryIdler::futexWaitUntil(
179 fut, 2, forever, 0xff, std::chrono::seconds(0)));
182 TEST(MemoryIdler, futexWaitNeverFlush) {
183 StrictMock<Futex<MockAtom>> fut;
184 auto clock = MockClock::setup();
186 EXPECT_CALL(fut, futexWaitUntil(1, forever, -1))
187 .WillOnce(Return(FutexResult::AWOKEN));
190 MemoryIdler::futexWaitUntil(
191 fut, 1, forever, -1, std::chrono::seconds(-7)));