1/*
2 * Copyright 2013-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
17#include <cstdlib>
18
19#include <folly/FileUtil.h>
20#include <folly/Random.h>
21#include <folly/portability/GTest.h>
22#include <folly/portability/SysMman.h>
23#include <folly/system/MemoryMapping.h>
24
25static constexpr double kSomeDouble = 3.14;
26
27namespace folly {
28
29TEST(MemoryMapping, Basic) {
30 File f = File::temporary();
31 {
32 MemoryMapping m(File(f.fd()), 0, sizeof(double), MemoryMapping::writable());
33 double* d = m.asWritableRange<double>().data();
34 *d = 37 * kSomeDouble;
35 }
36 {
37 MemoryMapping m(File(f.fd()), 0, 3);
38 EXPECT_EQ(0, m.asRange<int>().size()); // not big enough
39 }
40 {
41 MemoryMapping m(File(f.fd()), 0, sizeof(double));
42 const double* d = m.asRange<double>().data();
43 EXPECT_EQ(*d, 37 * kSomeDouble);
44 }
45}
46
47TEST(MemoryMapping, Move) {
48 File f = File::temporary();
49 {
50 MemoryMapping m(
51 File(f.fd()), 0, sizeof(double) * 2, MemoryMapping::writable());
52 double* d = m.asWritableRange<double>().data();
53 d[0] = 37 * kSomeDouble;
54 MemoryMapping m2(std::move(m));
55 double* d2 = m2.asWritableRange<double>().data();
56 d2[1] = 39 * kSomeDouble;
57 }
58 {
59 MemoryMapping m(File(f.fd()), 0, sizeof(double));
60 const double* d = m.asRange<double>().data();
61 EXPECT_EQ(d[0], 37 * kSomeDouble);
62 MemoryMapping m2(std::move(m));
63 const double* d2 = m2.asRange<double>().data();
64 EXPECT_EQ(d2[1], 39 * kSomeDouble);
65 }
66}
67
68TEST(MemoryMapping, DoublyMapped) {
69 File f = File::temporary();
70 // two mappings of the same memory, different addresses.
71 MemoryMapping mw(File(f.fd()), 0, sizeof(double), MemoryMapping::writable());
72 MemoryMapping mr(File(f.fd()), 0, sizeof(double));
73
74 double* dw = mw.asWritableRange<double>().data();
75 const double* dr = mr.asRange<double>().data();
76
77 // Show that it's truly the same value, even though the pointers differ
78 EXPECT_NE(dw, dr);
79 *dw = 42 * kSomeDouble;
80 EXPECT_EQ(*dr, 42 * kSomeDouble);
81 *dw = 43 * kSomeDouble;
82 EXPECT_EQ(*dr, 43 * kSomeDouble);
83}
84
85namespace {
86
87void writeStringToFileOrDie(const std::string& str, int fd) {
88 const char* b = str.c_str();
89 size_t count = str.size();
90 ssize_t total_bytes = 0;
91 ssize_t r;
92 do {
93 r = write(fd, b, count);
94 if (r == -1) {
95 if (errno == EINTR) {
96 continue;
97 }
98 PCHECK(r) << "write";
99 }
100
101 total_bytes += r;
102 b += r;
103 count -= r;
104 } while (r != 0 && count);
105}
106
107} // namespace
108
109TEST(MemoryMapping, Simple) {
110 File f = File::temporary();
111 writeStringToFileOrDie("hello", f.fd());
112
113 {
114 MemoryMapping m(File(f.fd()));
115 EXPECT_EQ("hello", m.data());
116 }
117 {
118 MemoryMapping m(File(f.fd()), 1, 2);
119 EXPECT_EQ("el", m.data());
120 }
121}
122
123TEST(MemoryMapping, LargeFile) {
124 std::string fileData;
125 size_t fileSize = sysconf(_SC_PAGESIZE) * 3 + 10;
126 fileData.reserve(fileSize);
127 for (size_t i = 0; i < fileSize; i++) {
128 fileData.push_back(0xff & Random::rand32());
129 }
130
131 File f = File::temporary();
132 writeStringToFileOrDie(fileData, f.fd());
133
134 {
135 MemoryMapping m(File(f.fd()));
136 EXPECT_EQ(fileData, m.data());
137 }
138 {
139 size_t size = sysconf(_SC_PAGESIZE) * 2;
140 StringPiece s(fileData.data() + 9, size - 9);
141 MemoryMapping m(File(f.fd()), 9, size - 9);
142 EXPECT_EQ(s.toString(), m.data());
143 }
144}
145
146TEST(MemoryMapping, ZeroLength) {
147 File f = File::temporary();
148 MemoryMapping m(File(f.fd()));
149 EXPECT_TRUE(m.mlock(MemoryMapping::LockMode::MUST_LOCK));
150 EXPECT_TRUE(m.mlocked());
151 EXPECT_EQ(0, m.data().size());
152}
153
154TEST(MemoryMapping, Advise) {
155 File f = File::temporary();
156 size_t kPageSize = 4096;
157 size_t size = kPageSize + 10; // unaligned file size
158 PCHECK(ftruncateNoInt(f.fd(), size) == 0) << size;
159
160 MemoryMapping m(File(f.fd()));
161
162 // NOTE: advise crashes on bad input.
163
164 m.advise(MADV_NORMAL, 0, kPageSize);
165 m.advise(MADV_NORMAL, 1, kPageSize);
166 m.advise(MADV_NORMAL, 0, 2);
167 m.advise(MADV_NORMAL, 1, 2);
168
169 m.advise(MADV_NORMAL, kPageSize, 0);
170 m.advise(MADV_NORMAL, kPageSize, 1);
171 m.advise(MADV_NORMAL, kPageSize, size - kPageSize);
172
173 auto off = kPageSize + 1;
174 m.advise(MADV_NORMAL, off, size - off);
175
176 EXPECT_DEATH(m.advise(MADV_NORMAL, off, size - off + 1), "");
177}
178
179} // namespace folly
180