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 | |
25 | static constexpr double kSomeDouble = 3.14; |
26 | |
27 | namespace folly { |
28 | |
29 | TEST(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 | |
47 | TEST(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 | |
68 | TEST(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 | |
85 | namespace { |
86 | |
87 | void 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 | |
109 | TEST(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 | |
123 | TEST(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 | |
146 | TEST(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 | |
154 | TEST(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 | |