Libosmium  2.15.5
Fast and flexible C++ library for working with OpenStreetMap data
memory_mapping.hpp
Go to the documentation of this file.
1 #ifndef OSMIUM_UTIL_MEMORY_MAPPING_HPP
2 #define OSMIUM_UTIL_MEMORY_MAPPING_HPP
3 
4 /*
5 
6 This file is part of Osmium (https://osmcode.org/libosmium).
7 
8 Copyright 2013-2020 Jochen Topf <jochen@topf.org> and others (see README).
9 
10 Boost Software License - Version 1.0 - August 17th, 2003
11 
12 Permission is hereby granted, free of charge, to any person or organization
13 obtaining a copy of the software and accompanying documentation covered by
14 this license (the "Software") to use, reproduce, display, distribute,
15 execute, and transmit the Software, and to prepare derivative works of the
16 Software, and to permit third-parties to whom the Software is furnished to
17 do so, all subject to the following:
18 
19 The copyright notices in the Software and this entire statement, including
20 the above license grant, this restriction and the following disclaimer,
21 must be included in all copies of the Software, in whole or in part, and
22 all derivative works of the Software, unless such copies or derivative
23 works are solely in the form of machine-executable object code generated by
24 a source language processor.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 DEALINGS IN THE SOFTWARE.
33 
34 */
35 
37 #include <osmium/util/file.hpp>
38 
39 #include <cassert>
40 #include <cerrno>
41 #include <cstddef>
42 #include <stdexcept>
43 #include <system_error>
44 
45 #ifndef _WIN32
46 # include <sys/mman.h>
47 # include <sys/statvfs.h>
48 #else
49 # include <fcntl.h>
50 # include <io.h>
51 # include <windows.h>
52 # include <sys/types.h>
53 #endif
54 
55 namespace osmium {
56 
57  inline namespace util {
58 
96  class MemoryMapping {
97 
98  public:
99 
100  enum class mapping_mode {
101  readonly = 0,
102  write_private = 1,
103  write_shared = 2
104  };
105 
106  private:
107 
109  std::size_t m_size;
110 
112  off_t m_offset;
113 
115  int m_fd;
116 
119 
120 #ifdef _WIN32
121  HANDLE m_handle;
122 #endif
123 
125  void* m_addr;
126 
127  bool is_valid() const noexcept;
128 
129  void make_invalid() noexcept;
130 
131 #ifdef _WIN32
132  using flag_type = DWORD;
133 #else
134  using flag_type = int;
135 #endif
136 
137  flag_type get_protection() const noexcept;
138 
139  flag_type get_flags() const noexcept;
140 
141  static std::size_t check_size(std::size_t size) {
142  if (size == 0) {
143  return osmium::get_pagesize();
144  }
145  return size;
146  }
147 
148 #ifdef _WIN32
149  HANDLE get_handle() const noexcept;
150  HANDLE create_file_mapping() const noexcept;
151  void* map_view_of_file() const noexcept;
152 #endif
153 
154  // Get the available space on the file system where the file
155  // behind fd is on. Return 0 if it can't be determined.
156  static std::size_t available_space(int fd) {
157 #ifdef _WIN32
158  return 0;
159 #else
160  struct statvfs stat;
161  const int result = ::fstatvfs(fd, &stat);
162  if (result != 0) {
163  return 0;
164  }
165  return stat.f_bsize * stat.f_bavail;
166 #endif
167  }
168 
169  int resize_fd(int fd) {
170  // Anonymous mapping doesn't need resizing.
171  if (fd == -1) {
172  return -1;
173  }
174 
175  // Make sure the file backing this mapping is large enough.
176  auto const current_file_size = osmium::file_size(fd);
177  if (current_file_size < m_size + m_offset) {
178  const auto available = available_space(fd);
179  if (available > 0 && current_file_size + available <= m_size) {
180  throw std::system_error{ENOSPC, std::system_category(), "Could not resize file: Not enough space on filesystem"};
181  }
182 
183  osmium::resize_file(fd, m_size + m_offset);
184  }
185  return fd;
186  }
187 
188  public:
189 
206  MemoryMapping(std::size_t size, mapping_mode mode, int fd = -1, off_t offset = 0);
207 
213  OSMIUM_DEPRECATED MemoryMapping(std::size_t size, bool writable = true, int fd = -1, off_t offset = 0) : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
214  MemoryMapping(size, writable ? mapping_mode::write_shared : mapping_mode::readonly, fd, offset) {
215  }
216 
218  MemoryMapping(const MemoryMapping&) = delete;
219 
221  MemoryMapping& operator=(const MemoryMapping&) = delete;
222 
227  MemoryMapping(MemoryMapping&& other) noexcept;
228 
232  MemoryMapping& operator=(MemoryMapping&& other) noexcept;
233 
238  ~MemoryMapping() noexcept {
239  try {
240  unmap();
241  } catch (const std::system_error&) {
242  // Ignore any exceptions because destructor must not throw.
243  }
244  }
245 
252  void unmap();
253 
265  void resize(std::size_t new_size);
266 
271  explicit operator bool() const noexcept {
272  return is_valid();
273  }
274 
280  std::size_t size() const noexcept {
281  return m_size;
282  }
283 
289  int fd() const noexcept {
290  return m_fd;
291  }
292 
296  bool writable() const noexcept {
297  return m_mapping_mode != mapping_mode::readonly;
298  }
299 
305  template <typename T = void>
306  T* get_addr() const noexcept {
307  return reinterpret_cast<T*>(m_addr);
308  }
309 
310  }; // class MemoryMapping
311 
323 
324  public:
325 
326  explicit AnonymousMemoryMapping(std::size_t size) :
328  }
329 
330 #ifndef __linux__
331 
335  void resize(std::size_t) = delete;
336 #endif
337 
338  }; // class AnonymousMemoryMapping
339 
349  template <typename T>
351 
353 
354  public:
355 
362  explicit TypedMemoryMapping(std::size_t size) :
363  m_mapping(sizeof(T) * size, MemoryMapping::mapping_mode::write_private) {
364  }
365 
376  TypedMemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset = 0) :
377  m_mapping(sizeof(T) * size, mode, fd, sizeof(T) * offset) {
378  }
379 
385  OSMIUM_DEPRECATED TypedMemoryMapping(std::size_t size, bool writable, int fd, off_t offset = 0) :
386  m_mapping(sizeof(T) * size,
388  fd,
389  sizeof(T) * offset) {
390  }
391 
393  TypedMemoryMapping(const TypedMemoryMapping&) = delete;
394 
397 
402  TypedMemoryMapping(TypedMemoryMapping&& other) noexcept = default;
403 
407  TypedMemoryMapping& operator=(TypedMemoryMapping&& other) noexcept = default;
408 
413  ~TypedMemoryMapping() noexcept = default;
414 
421  void unmap() {
422  m_mapping.unmap();
423  }
424 
435  void resize(std::size_t new_size) {
436  m_mapping.resize(sizeof(T) * new_size);
437  }
438 
443  explicit operator bool() const noexcept {
444  return !!m_mapping;
445  }
446 
452  std::size_t size() const noexcept {
453  assert(m_mapping.size() % sizeof(T) == 0);
454  return m_mapping.size() / sizeof(T);
455  }
456 
462  int fd() const noexcept {
463  return m_mapping.fd();
464  }
465 
469  bool writable() const noexcept {
470  return m_mapping.writable();
471  }
472 
478  T* begin() noexcept {
479  return m_mapping.get_addr<T>();
480  }
481 
487  T* end() noexcept {
488  return m_mapping.get_addr<T>() + size();
489  }
490 
496  const T* cbegin() const noexcept {
497  return m_mapping.get_addr<T>();
498  }
499 
505  const T* cend() const noexcept {
506  return m_mapping.get_addr<T>() + size();
507  }
508 
514  const T* begin() const noexcept {
515  return m_mapping.get_addr<T>();
516  }
517 
523  const T* end() const noexcept {
524  return m_mapping.get_addr<T>() + size();
525  }
526 
527  }; // class TypedMemoryMapping
528 
529  template <typename T>
531 
532  public:
533 
534  explicit AnonymousTypedMemoryMapping(std::size_t size) :
535  TypedMemoryMapping<T>(size) {
536  }
537 
538 #ifndef __linux__
539 
543  void resize(std::size_t) = delete;
544 #endif
545 
546  }; // class AnonymousTypedMemoryMapping
547 
548  } // namespace util
549 
550 } // namespace osmium
551 
552 #ifndef _WIN32
553 
554 // =========== Unix implementation =============
555 
556 // MAP_FAILED is often a macro containing an old style cast
557 #pragma GCC diagnostic push
558 #pragma GCC diagnostic ignored "-Wold-style-cast"
559 
560 inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
561  return m_addr != MAP_FAILED; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
562 }
563 
565  m_addr = MAP_FAILED; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
566 }
567 
568 #pragma GCC diagnostic pop
569 
570 // for BSD systems
571 #ifndef MAP_ANONYMOUS
572 # define MAP_ANONYMOUS MAP_ANON
573 #endif
574 
575 inline int osmium::util::MemoryMapping::get_protection() const noexcept {
577  return PROT_READ;
578  }
579  return PROT_READ | PROT_WRITE; // NOLINT(hicpp-signed-bitwise)
580 }
581 
582 inline int osmium::util::MemoryMapping::get_flags() const noexcept {
583  if (m_fd == -1) {
584  return MAP_PRIVATE | MAP_ANONYMOUS; // NOLINT(hicpp-signed-bitwise)
585  }
587  return MAP_SHARED;
588  }
589  return MAP_PRIVATE;
590 }
591 
592 inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, mapping_mode mode, int fd, off_t offset) :
593  m_size(check_size(size)),
594  m_offset(offset),
595  m_fd(resize_fd(fd)),
596  m_mapping_mode(mode),
597  m_addr(::mmap(nullptr, m_size, get_protection(), get_flags(), m_fd, m_offset)) {
598  assert(!(fd == -1 && mode == mapping_mode::readonly));
599  if (!is_valid()) {
600  throw std::system_error{errno, std::system_category(), "mmap failed"};
601  }
602 }
603 
605  m_size(other.m_size),
606  m_offset(other.m_offset),
607  m_fd(other.m_fd),
608  m_mapping_mode(other.m_mapping_mode),
609  m_addr(other.m_addr) {
610  other.make_invalid();
611 }
612 
614  try {
615  unmap();
616  } catch (const std::system_error&) {
617  // Ignore unmap error. It should never happen anyway and we can't do
618  // anything about it here.
619  }
620  m_size = other.m_size;
621  m_offset = other.m_offset;
622  m_fd = other.m_fd;
623  m_mapping_mode = other.m_mapping_mode;
624  m_addr = other.m_addr;
625  other.make_invalid();
626  return *this;
627 }
628 
630  if (is_valid()) {
631  if (::munmap(m_addr, m_size) != 0) {
632  throw std::system_error{errno, std::system_category(), "munmap failed"};
633  }
634  make_invalid();
635  }
636 }
637 
638 inline void osmium::util::MemoryMapping::resize(std::size_t new_size) {
639  assert(new_size > 0 && "can not resize to zero size");
640  if (m_fd == -1) { // anonymous mapping
641 #ifdef __linux__
642  m_addr = ::mremap(m_addr, m_size, new_size, MREMAP_MAYMOVE);
643  if (!is_valid()) {
644  throw std::system_error{errno, std::system_category(), "mremap failed"};
645  }
646  m_size = new_size;
647 #else
648  assert(false && "can't resize anonymous mappings on non-linux systems");
649 #endif
650  } else { // file-based mapping
651  unmap();
652  m_size = new_size;
653  resize_fd(m_fd);
654  m_addr = ::mmap(nullptr, new_size, get_protection(), get_flags(), m_fd, m_offset);
655  if (!is_valid()) {
656  throw std::system_error{errno, std::system_category(), "mmap (remap) failed"};
657  }
658  }
659 }
660 
661 #else
662 
663 // =========== Windows implementation =============
664 
665 /* References:
666  * CreateFileMapping: https://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
667  * CloseHandle: https://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
668  * MapViewOfFile: https://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
669  * UnmapViewOfFile: https://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
670  */
671 
672 namespace osmium {
673 
674  inline namespace util {
675 
676  inline DWORD dword_hi(uint64_t x) {
677  return static_cast<DWORD>(x >> 32);
678  }
679 
680  inline DWORD dword_lo(uint64_t x) {
681  return static_cast<DWORD>(x & 0xffffffff);
682  }
683 
684  } // namespace util
685 
686 } // namespace osmium
687 
688 inline DWORD osmium::util::MemoryMapping::get_protection() const noexcept {
689  switch (m_mapping_mode) {
691  return PAGE_READONLY;
693  return PAGE_WRITECOPY;
694  default: // mapping_mode::write_shared
695  break;
696  }
697  return PAGE_READWRITE;
698 }
699 
700 inline DWORD osmium::util::MemoryMapping::get_flags() const noexcept {
701  switch (m_mapping_mode) {
703  return FILE_MAP_READ;
705  return FILE_MAP_COPY;
706  default: // mapping_mode::write_shared
707  break;
708  }
709  return FILE_MAP_WRITE;
710 }
711 
712 inline HANDLE osmium::util::MemoryMapping::get_handle() const noexcept {
713  if (m_fd == -1) {
714  return INVALID_HANDLE_VALUE;
715  }
716  return reinterpret_cast<HANDLE>(_get_osfhandle(m_fd));
717 }
718 
719 inline HANDLE osmium::util::MemoryMapping::create_file_mapping() const noexcept {
720  if (m_fd != -1) {
721  _setmode(m_fd, _O_BINARY);
722  }
723  return CreateFileMapping(get_handle(),
724  nullptr,
725  get_protection(),
726  osmium::dword_hi(static_cast<uint64_t>(m_size) + m_offset),
727  osmium::dword_lo(static_cast<uint64_t>(m_size) + m_offset),
728  nullptr);
729 }
730 
731 inline void* osmium::util::MemoryMapping::map_view_of_file() const noexcept {
732  return MapViewOfFile(m_handle,
733  get_flags(),
734  osmium::dword_hi(m_offset),
735  osmium::dword_lo(m_offset),
736  m_size);
737 }
738 
739 inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
740  return m_addr != nullptr;
741 }
742 
743 inline void osmium::util::MemoryMapping::make_invalid() noexcept {
744  m_addr = nullptr;
745 }
746 
747 // GetLastError() returns a DWORD (A 32-bit unsigned integer), but the error
748 // code for std::system_error is an int. So we convert this here and hope
749 // it all works.
750 inline int last_error() noexcept {
751  return static_cast<int>(GetLastError());
752 }
753 
754 inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) :
755  m_size(check_size(size)),
756  m_offset(offset),
757  m_fd(resize_fd(fd)),
758  m_mapping_mode(mode),
759  m_handle(create_file_mapping()),
760  m_addr(nullptr) {
761 
762  if (!m_handle) {
763  throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"};
764  }
765 
766  m_addr = map_view_of_file();
767  if (!is_valid()) {
768  throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"};
769  }
770 }
771 
773  m_size(other.m_size),
774  m_offset(other.m_offset),
775  m_fd(other.m_fd),
776  m_mapping_mode(other.m_mapping_mode),
777  m_handle(std::move(other.m_handle)),
778  m_addr(other.m_addr) {
779  other.make_invalid();
780  other.m_handle = nullptr;
781 }
782 
784  unmap();
785  m_size = other.m_size;
786  m_offset = other.m_offset;
787  m_fd = other.m_fd;
788  m_mapping_mode = other.m_mapping_mode;
789  m_handle = std::move(other.m_handle);
790  m_addr = other.m_addr;
791  other.make_invalid();
792  other.m_handle = nullptr;
793  return *this;
794 }
795 
797  if (is_valid()) {
798  if (!UnmapViewOfFile(m_addr)) {
799  throw std::system_error{last_error(), std::system_category(), "UnmapViewOfFile failed"};
800  }
801  make_invalid();
802  }
803 
804  if (m_handle) {
805  if (!CloseHandle(m_handle)) {
806  throw std::system_error{last_error(), std::system_category(), "CloseHandle failed"};
807  }
808  m_handle = nullptr;
809  }
810 }
811 
812 inline void osmium::util::MemoryMapping::resize(std::size_t new_size) {
813  unmap();
814 
815  m_size = new_size;
816  resize_fd(m_fd);
817 
818  m_handle = create_file_mapping();
819  if (!m_handle) {
820  throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"};
821  }
822 
823  m_addr = map_view_of_file();
824  if (!is_valid()) {
825  throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"};
826  }
827 }
828 
829 #endif
830 
831 #endif // OSMIUM_UTIL_MEMORY_MAPPING_HPP
const T * begin() const noexcept
Definition: memory_mapping.hpp:514
~MemoryMapping() noexcept
Definition: memory_mapping.hpp:238
bool is_valid() const noexcept
Definition: memory_mapping.hpp:560
#define OSMIUM_DEPRECATED
Definition: compatibility.hpp:51
OSMIUM_DEPRECATED TypedMemoryMapping(std::size_t size, bool writable, int fd, off_t offset=0)
Definition: memory_mapping.hpp:385
flag_type get_protection() const noexcept
Definition: memory_mapping.hpp:575
MemoryMapping m_mapping
Definition: memory_mapping.hpp:352
int flag_type
Definition: memory_mapping.hpp:134
OSMIUM_DEPRECATED MemoryMapping(std::size_t size, bool writable=true, int fd=-1, off_t offset=0)
Definition: memory_mapping.hpp:213
std::size_t file_size(int fd)
Definition: file.hpp:109
const T * cend() const noexcept
Definition: memory_mapping.hpp:505
flag_type get_flags() const noexcept
Definition: memory_mapping.hpp:582
T * get_addr() const noexcept
Definition: memory_mapping.hpp:306
int fd() const noexcept
Definition: memory_mapping.hpp:289
static std::size_t available_space(int fd)
Definition: memory_mapping.hpp:156
void unmap()
Definition: memory_mapping.hpp:629
Definition: memory_mapping.hpp:96
Definition: location.hpp:551
mapping_mode
Definition: memory_mapping.hpp:100
int resize_fd(int fd)
Definition: memory_mapping.hpp:169
std::size_t size() const noexcept
Definition: memory_mapping.hpp:452
void * m_addr
The address where the memory is mapped.
Definition: memory_mapping.hpp:125
off_t m_offset
Offset into the file.
Definition: memory_mapping.hpp:112
#define MAP_ANONYMOUS
Definition: memory_mapping.hpp:572
int m_fd
File handle we got the mapping from.
Definition: memory_mapping.hpp:115
mapping_mode m_mapping_mode
Mapping mode.
Definition: memory_mapping.hpp:118
Namespace for everything in the Osmium library.
Definition: assembler.hpp:53
const T * cbegin() const noexcept
Definition: memory_mapping.hpp:496
Definition: memory_mapping.hpp:530
std::size_t get_pagesize()
Definition: file.hpp:193
T * begin() noexcept
Definition: memory_mapping.hpp:478
Definition: memory_mapping.hpp:322
TypedMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:362
void make_invalid() noexcept
Definition: memory_mapping.hpp:564
std::size_t m_size
The size of the mapping.
Definition: memory_mapping.hpp:109
int fd() const noexcept
Definition: memory_mapping.hpp:462
MemoryMapping & operator=(const MemoryMapping &)=delete
You can not copy a MemoryMapping.
Definition: memory_mapping.hpp:350
void unmap()
Definition: memory_mapping.hpp:421
MemoryMapping(std::size_t size, mapping_mode mode, int fd=-1, off_t offset=0)
Definition: memory_mapping.hpp:592
bool writable() const noexcept
Definition: memory_mapping.hpp:296
AnonymousTypedMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:534
AnonymousMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:326
void resize_file(int fd, std::size_t new_size)
Definition: file.hpp:177
T * end() noexcept
Definition: memory_mapping.hpp:487
std::size_t size() const noexcept
Definition: memory_mapping.hpp:280
const T * end() const noexcept
Definition: memory_mapping.hpp:523
void resize(std::size_t new_size)
Definition: memory_mapping.hpp:638
static std::size_t check_size(std::size_t size)
Definition: memory_mapping.hpp:141
TypedMemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset=0)
Definition: memory_mapping.hpp:376
void resize(std::size_t new_size)
Definition: memory_mapping.hpp:435
bool writable() const noexcept
Definition: memory_mapping.hpp:469