2 * Copyright 2014 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>
18 #include <folly/Baton.h>
22 #include <semaphore.h>
23 #include <gflags/gflags.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include <folly/Benchmark.h>
28 using namespace folly;
29 using namespace folly::detail;
30 using namespace testing;
32 TEST(MemoryIdler, releaseStack) {
33 MemoryIdler::unmapUnusedStack();
36 TEST(MemoryIdler, releaseStackMinExtra) {
37 MemoryIdler::unmapUnusedStack(0);
40 TEST(MemoryIdler, releaseStackLargeExtra) {
41 MemoryIdler::unmapUnusedStack(30000000);
44 TEST(MemoryIdler, releaseMallocTLS) {
46 MemoryIdler::flushLocalMallocCaches();
48 MemoryIdler::flushLocalMallocCaches();
50 MemoryIdler::flushLocalMallocCaches();
55 /// MockedAtom gives us a way to select a mocked Futex implementation
56 /// inside Baton, even though the atom itself isn't exercised by the
59 struct MockAtom : public std::atomic<T> {
60 explicit MockAtom(T init = 0) : std::atomic<T>(init) {}
64 /// MockClock is a bit tricky because we are mocking a static function
65 /// (now()), so we need to find the corresponding mock instance without
66 /// extending its scope beyond that of the test. I generally avoid
67 /// shared_ptr, but a weak_ptr is just the ticket here
69 typedef std::chrono::steady_clock::duration duration;
70 typedef std::chrono::steady_clock::time_point time_point;
72 MOCK_METHOD0(nowImpl, time_point(void));
74 /// Hold on to the returned shared_ptr until the end of the test
75 static std::shared_ptr<StrictMock<MockClock>> setup() {
76 auto rv = std::make_shared<StrictMock<MockClock>>();
77 s_mockClockInstance = rv;
81 static time_point now() {
82 return s_mockClockInstance.lock()->nowImpl();
85 static std::weak_ptr<StrictMock<MockClock>> s_mockClockInstance;
88 std::weak_ptr<StrictMock<MockClock>> MockClock::s_mockClockInstance;
92 namespace folly { namespace detail {
94 /// Futex<MockAtom> is our mocked futex implementation. Note that the
95 /// method signatures differ from the real Futex because we have elided
96 /// unused default params and collapsed templated methods into the
99 struct Futex<MockAtom> {
100 MOCK_METHOD2(futexWait, bool(uint32_t, uint32_t));
101 MOCK_METHOD3(futexWaitUntil,
102 FutexResult(uint32_t, const MockClock::time_point&, uint32_t));
107 TEST(MemoryIdler, futexWaitValueChangedEarly) {
108 StrictMock<Futex<MockAtom>> fut;
109 auto clock = MockClock::setup();
110 auto begin = MockClock::time_point(std::chrono::seconds(100));
111 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
113 EXPECT_CALL(*clock, nowImpl())
114 .WillOnce(Return(begin));
115 EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
116 Lt(begin + 2 * idleTimeout)), -1))
117 .WillOnce(Return(FutexResult::VALUE_CHANGED));
118 EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
121 TEST(MemoryIdler, futexWaitValueChangedLate) {
122 StrictMock<Futex<MockAtom>> fut;
123 auto clock = MockClock::setup();
124 auto begin = MockClock::time_point(std::chrono::seconds(100));
125 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
127 EXPECT_CALL(*clock, nowImpl())
128 .WillOnce(Return(begin));
129 EXPECT_CALL(fut, futexWaitUntil(1, AllOf(Ge(begin + idleTimeout),
130 Lt(begin + 2 * idleTimeout)), -1))
131 .WillOnce(Return(FutexResult::TIMEDOUT));
132 EXPECT_CALL(fut, futexWait(1, -1))
133 .WillOnce(Return(false));
134 EXPECT_FALSE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
137 TEST(MemoryIdler, futexWaitAwokenEarly) {
138 StrictMock<Futex<MockAtom>> fut;
139 auto clock = MockClock::setup();
140 auto begin = MockClock::time_point(std::chrono::seconds(100));
141 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
143 EXPECT_CALL(*clock, nowImpl())
144 .WillOnce(Return(begin));
145 EXPECT_CALL(fut, futexWaitUntil(1, Ge(begin + idleTimeout), -1))
146 .WillOnce(Return(FutexResult::AWOKEN));
147 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(fut, 1)));
150 TEST(MemoryIdler, futexWaitAwokenLate) {
151 StrictMock<Futex<MockAtom>> fut;
152 auto clock = MockClock::setup();
153 auto begin = MockClock::time_point(std::chrono::seconds(100));
154 auto idleTimeout = MemoryIdler::defaultIdleTimeout.load();
156 EXPECT_CALL(*clock, nowImpl())
157 .WillOnce(Return(begin));
158 EXPECT_CALL(fut, futexWaitUntil(1, begin + idleTimeout, -1))
159 .WillOnce(Return(FutexResult::TIMEDOUT));
160 EXPECT_CALL(fut, futexWait(1, -1))
161 .WillOnce(Return(true));
162 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
163 fut, 1, -1, idleTimeout, 100, 0.0f)));
166 TEST(MemoryIdler, futexWaitImmediateFlush) {
167 StrictMock<Futex<MockAtom>> fut;
168 auto clock = MockClock::setup();
170 EXPECT_CALL(fut, futexWait(2, 0xff))
171 .WillOnce(Return(true));
172 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
173 fut, 2, 0xff, std::chrono::seconds(0))));
176 TEST(MemoryIdler, futexWaitNeverFlush) {
177 StrictMock<Futex<MockAtom>> fut;
178 auto clock = MockClock::setup();
180 EXPECT_CALL(fut, futexWait(1, -1))
181 .WillOnce(Return(true));
182 EXPECT_TRUE((MemoryIdler::futexWait<MockAtom, MockClock>(
183 fut, 1, -1, MockClock::duration::max())));
187 BENCHMARK(releaseStack, iters) {
188 for (size_t i = 0; i < iters; ++i) {
189 MemoryIdler::unmapUnusedStack();
193 BENCHMARK(releaseMallocTLS, iters) {
194 for (size_t i = 0; i < iters; ++i) {
195 MemoryIdler::flushLocalMallocCaches();
199 int main(int argc, char** argv) {
200 testing::InitGoogleTest(&argc, argv);
201 gflags::ParseCommandLineFlags(&argc, &argv, true);
203 auto rv = RUN_ALL_TESTS();
204 if (!rv && FLAGS_benchmark) {
205 folly::runBenchmarks();