tudocomp
– The TU Dortmund Compression Framework
MMapHandle.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <sys/mman.h>
4 #include <fcntl.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <cstring>
9 
10 #include <tudocomp_stat/malloc.hpp>
11 #include <tudocomp/def.hpp>
12 #include <tudocomp/util/View.hpp>
13 #include <tudocomp/io/IOUtil.hpp>
14 
16 namespace tdc {namespace io {
17  inline size_t pagesize() {
18  return sysconf(_SC_PAGESIZE);
19  }
20 
26  class MMap {
27  static constexpr const void* EMPTY = "";
28 
29  enum class State {
30  Unmapped,
31  Shared,
32  Private
33  };
34 
35  inline static size_t adj_size(size_t v) {
36  return std::max(size_t(1), v);
37  }
38 
39  inline static void check_mmap_error(void* ptr, string_ref descr) {
40  if (ptr == MAP_FAILED) {
41  perror("MMap error");
42  }
43  CHECK(ptr != MAP_FAILED) << "Error at " << descr;
44  }
45  public:
46  enum class Mode {
47  Read,
48  ReadWrite
49  };
50  private:
51  uint8_t* m_ptr = (uint8_t*) EMPTY;
52  size_t m_size = 0;
53 
54  State m_state = State::Unmapped;
55  Mode m_mode = Mode::Read;
56 
57  public:
58  inline static bool is_offset_valid(size_t offset) {
59  return (offset % pagesize()) == 0;
60  }
61 
62  inline static size_t next_valid_offset(size_t offset) {
63  auto ps = pagesize();
64  auto diff = offset % ps;
65  auto ok = offset - diff;
66  DCHECK(is_offset_valid(ok));
67  DCHECK(ok <= offset);
68  return ok;
69  }
70 
72  inline MMap() { /* field default values are fine already */ }
73 
79  inline MMap(const std::string& path,
80  Mode mode,
81  size_t size,
82  size_t offset = 0)
83  {
84  m_mode = mode;
85  m_size = size;
86 
87  DCHECK(is_offset_valid(offset))
88  << "Offset must be page aligned, use MMap::next_valid_offset() to ensure this.";
89 
90  size_t file_size = read_file_size(path);
91  bool needs_to_overallocate =
92  (offset + m_size) > file_size;
93 
94  // Open file for memory map
95  auto fd = open(path.c_str(), O_RDONLY);
96  CHECK(fd != -1) << "Error at opening file";
97 
98  int mmap_prot;
99  int mmap_flags;
100 
101  bool try_next = false;
102 
103  if (!needs_to_overallocate) {
104  // Map file directly into memory
105 
106  State state;
107 
108  if (m_mode == Mode::ReadWrite) {
109  mmap_prot = PROT_READ | PROT_WRITE;
110  mmap_flags = MAP_PRIVATE;
111  state = State::Private;
112  } else {
113  mmap_prot = PROT_READ;
114  mmap_flags = MAP_SHARED;
115  state = State::Shared;
116  }
117 
118  void* ptr = mmap(NULL,
119  adj_size(m_size),
120  mmap_prot,
121  mmap_flags,
122  fd,
123  offset);
124  //check_mmap_error(ptr, "mapping file into memory");
125  if (false && ptr != MAP_FAILED) {
126  m_ptr = (uint8_t*) ptr;
127  m_state = state;
128  IF_STATS(if (m_state == State::Private) {
129  malloc_callback::on_alloc(adj_size(m_size));
130  })
131  } else {
132  //LOG(INFO) << "Mapping file into memory failed, falling"
133  // << " back to copying into a anonymous map";
134  try_next = true;
135  }
136  }
137 
138  if (try_next || needs_to_overallocate) {
139  if (try_next) {
140  //std::cout << "I get used\n";
141  }
142 
143  // Allocate memory and copy file into it
144 
145  *this = MMap(m_size);
146 
147  // seek to offset
148  {
149  auto ret = lseek(fd, offset, SEEK_SET);
150  if (ret == -1) {
151  perror("Seeking fd");
152  }
153  CHECK(ret != -1);
154  }
155 
156  // copy data
157  {
158  auto ptr = m_ptr;
159  auto size = file_size - offset;
160 
161  while (size > 0) {
162  auto ret = read(fd, ptr, size);
163  if (ret == -1) {
164  perror("Reading fd into mapped memory");
165  }
166  CHECK(ret >= 0);
167  size -= ret;
168  ptr += ret;
169  }
170 
171  }
172  }
173 
174  close(fd);
175  }
176 
178  inline MMap(size_t size)
179  {
180  m_mode = Mode::ReadWrite;
181  m_size = size;
182 
183  int mmap_prot = PROT_READ | PROT_WRITE;
184  int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
185 
186  void* ptr = mmap(NULL,
187  adj_size(m_size),
188  mmap_prot,
189  mmap_flags,
190  -1,
191  0);
192  check_mmap_error(ptr, "creating anon. memory map");
193 
194  m_ptr = (uint8_t*) ptr;
195 
196  m_state = State::Private;
197  IF_STATS(if (m_state == State::Private) {
198  malloc_callback::on_alloc(adj_size(m_size));
199  })
200  }
201 
205  inline void remap(size_t new_size) {
206  DCHECK(m_mode == Mode::ReadWrite);
207  DCHECK(m_state == State::Private);
208 
209  // On Linux, use mremap to expand memory in place
210  #ifndef __MACH__
211 
212  auto p = mremap(m_ptr, adj_size(m_size), adj_size(new_size), MREMAP_MAYMOVE);
213  check_mmap_error(p, "remapping memory");
214  IF_STATS(if (m_state == State::Private) {
215  malloc_callback::on_free(adj_size(m_size));
216  malloc_callback::on_alloc(adj_size(new_size));
217  // TODO ^ handle lazy initialization better by not seemingly
218  // allocating everything
219  })
220 
221  m_ptr = (uint8_t*) p;
222  m_size = new_size;
223 
224  // On Mac there is no mremap, so we just copy
225  #else
226 
227  auto new_map = MMap(new_size);
228  size_t common_size = std::min(new_size, m_size);
229  std::memcpy(new_map.view().data(), view().data(), common_size);
230  *this = std::move(new_map);
231 
232  #endif
233  }
234 
235  View view() const {
236  return View(m_ptr, m_size);
237  }
238 
239  GenericView<uint8_t> view() {
240  const auto err = "Attempting to get a mutable view into a read-only mapping. Call the const overload of view() instead"_v;
241 
242  DCHECK(m_state == State::Private) << err;
243  DCHECK(m_mode == Mode::ReadWrite) << err;
244  return GenericView<uint8_t>(m_ptr, m_size);
245  }
246 
247  inline MMap(const MMap& other) = delete;
248  inline MMap& operator=(const MMap& other) = delete;
249  private:
250  inline void move_from(MMap&& other) {
251  m_ptr = other.m_ptr;
252  m_size = other.m_size;
253 
254  m_state = other.m_state;
255  m_mode = other.m_mode;
256 
257  other.m_state = State::Unmapped;
258  other.m_ptr = (uint8_t*) EMPTY;
259  other.m_size = 0;
260  }
261  public:
262  inline MMap(MMap&& other) {
263  move_from(std::move(other));
264  }
265 
266  inline MMap& operator=(MMap&& other) {
267  move_from(std::move(other));
268  return *this;
269  }
270 
271  ~MMap() {
272  if (m_state != State::Unmapped) {
273  DCHECK(m_ptr != EMPTY);
274 
275  int rc = munmap(m_ptr, adj_size(m_size));
276  CHECK(rc == 0) << "Error at unmapping";
277  IF_STATS(if (m_state == State::Private) {
278  malloc_callback::on_free(adj_size(m_size));
279  })
280  }
281  }
282  };
283 }}
285 
Contains the text compression and encoding framework.
Definition: namespaces.hpp:11
uint_impl_t & operator=(const uint_impl_t &b)
Definition: uint_t.hpp:43
View string_ref
Definition: View.hpp:26
#define IF_STATS(x)
x is compiled only when the STATS_DISABLED macro is undefined.
Definition: def.hpp:59
ByteView View
Definition: View.hpp:25