2 * Copyright 2016 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/Baton.h>
20 #include <folly/portability/Windows.h>
25 #include <semaphore.h>
27 #include <gmock/gmock.h>
28 #include <gtest/gtest.h>
30 using namespace folly;
31 using namespace folly::detail;
32 using namespace testing;
34 TEST(MemoryIdler, releaseStack) {
35 MemoryIdler::unmapUnusedStack();
38 TEST(MemoryIdler, releaseStackMinExtra) {
39 MemoryIdler::unmapUnusedStack(0);
42 TEST(MemoryIdler, releaseStackLargeExtra) {
43 MemoryIdler::unmapUnusedStack(30000000);
46 TEST(MemoryIdler, releaseMallocTLS) {
48 MemoryIdler::flushLocalMallocCaches();
50 MemoryIdler::flushLocalMallocCaches();
52 MemoryIdler::flushLocalMallocCaches();
57 /// MockedAtom gives us a way to select a mocked Futex implementation
58 /// inside Baton, even though the atom itself isn't exercised by the
61 struct MockAtom : public std::atomic<T> {
62 explicit MockAtom(T init = 0) : std::atomic<T>(init) {}
66 /// MockClock is a bit tricky because we are mocking a static function
67 /// (now()), so we need to find the corresponding mock instance without
68 /// extending its scope beyond that of the test. I generally avoid
69 /// shared_ptr, but a weak_ptr is just the ticket here
71 typedef std::chrono::steady_clock::duration duration;
72 typedef std::chrono::steady_clock::time_point time_point;
74 MOCK_METHOD0(nowImpl, time_point(void));
76 /// Hold on to the returned shared_ptr until the end of the test
77 static std::shared_ptr<StrictMock<MockClock>> setup() {
78 auto rv = std::make_shared<StrictMock<MockClock>>();
79 s_mockClockInstance = rv;
83 static time_point now() {
84 return s_mockClockInstance.lock()->nowImpl();
87 static std::weak_ptr<StrictMock<MockClock>> s_mockClockInstance;
90 std::weak_ptr<StrictMock<MockClock>> MockClock::s_mockClockInstance;
94 namespace folly { namespace detail {
96 /// Futex<MockAtom> is our mocked futex implementation. Note that the
97 /// method signatures differ from the real Futex because we have elided
98 /// unused default params and collapsed templated methods into the
101 struct Futex<MockAtom> {
102 MOCK_METHOD2(futexWait, bool(uint32_t, uint32_t));
103 MOCK_METHOD3(futexWaitUntil,
104 FutexResult(uint32_t, const MockClock::time_point&, uint32_t));
109 TEST(MemoryIdler, futexWaitValueChangedEarly) {
110 StrictMock<Futex<MockAtom>> fut;
111 auto clock = MockClock::setup();
112 auto begin = MockClock::time_point(std::chrono::seconds(100));
113 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
115 EXPECT_CALL(*clock, nowImpl())
116 .WillOnce(Return(begin));
117 EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
118 Lt(begin + 2 * idleTimeout)), -1))
119 .WillOnce(Return(FutexResult::VALUE_CHANGED));
120 EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
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, futexWait(1, -1))
135 .WillOnce(Return(false));
136 EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
139 TEST(MemoryIdler, futexWaitAwokenEarly) {
140 StrictMock<Futex<MockAtom>> fut;
141 auto clock = MockClock::setup();
142 auto begin = MockClock::time_point(std::chrono::seconds(100));
143 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
145 EXPECT_CALL(*clock, nowImpl())
146 .WillOnce(Return(begin));
147 EXPECT_CALL(fut, futexWaitUntil(1, Ge(begin + idleTimeout), -1))
148 .WillOnce(Return(FutexResult::AWOKEN));
149 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
152 TEST(MemoryIdler, futexWaitAwokenLate) {
153 StrictMock<Futex<MockAtom>> fut;
154 auto clock = MockClock::setup();
155 auto begin = MockClock::time_point(std::chrono::seconds(100));
156 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
158 EXPECT_CALL(*clock, nowImpl())
159 .WillOnce(Return(begin));
160 EXPECT_CALL(fut, futexWaitUntil(1, begin + idleTimeout, -1))
161 .WillOnce(Return(FutexResult::TIMEDOUT));
162 EXPECT_CALL(fut, futexWait(1, -1))
163 .WillOnce(Return(true));
164 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
165 fut, 1, -1, idleTimeout, 100, 0.0f)));
168 TEST(MemoryIdler, futexWaitImmediateFlush) {
169 StrictMock<Futex<MockAtom>> fut;
170 auto clock = MockClock::setup();
172 EXPECT_CALL(fut, futexWait(2, 0xff))
173 .WillOnce(Return(true));
174 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
175 fut, 2, 0xff, std::chrono::seconds(0))));
178 TEST(MemoryIdler, futexWaitNeverFlush) {
179 StrictMock<Futex<MockAtom>> fut;
180 auto clock = MockClock::setup();
182 EXPECT_CALL(fut, futexWait(1, -1))
183 .WillOnce(Return(true));
184 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
185 fut, 1, -1, MockClock::duration::max())));