Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
pinnable_mapped_file.cpp
Go to the documentation of this file.
3#include <boost/interprocess/managed_external_buffer.hpp>
4#include <boost/interprocess/anonymous_shared_memory.hpp>
5#include <boost/asio/signal_set.hpp>
6#include <iostream>
7#include <fstream>
8
9#ifdef __linux__
10#include <linux/mman.h>
11#endif
12
13namespace chainbase {
14
15const char* chainbase_error_category::name() const noexcept {
16 return "chainbase";
17}
18
19std::string chainbase_error_category::message(int ev) const {
20 switch(ev) {
22 return "Ok";
24 return "Database dirty flag set";
26 return "Database incompatible; All environment parameters must match";
28 return "Database format not compatible with this version of chainbase";
30 return "Database file not found";
32 return "Bad size";
34 return "Heap and locked mode are not supported on win32";
36 return "Failed to read DB header";
38 return "Could not gain write access to the shared memory file";
40 return "Database load aborted";
42 return "Failed to mlock database";
43 default:
44 return "Unrecognized error code";
45 }
46}
47
48const std::error_category& chainbase_error_category() {
49 static class chainbase_error_category the_category;
50 return the_category;
51}
52
53pinnable_mapped_file::pinnable_mapped_file(const bfs::path& dir, bool writable, uint64_t shared_file_size, bool allow_dirty, map_mode mode) :
54 _data_file_path(bfs::absolute(dir/"shared_memory.bin")),
55 _database_name(dir.filename().string()),
56 _writable(writable)
57{
58 if(shared_file_size % _db_size_multiple_requirement) {
59 std::string what_str("Database must be mulitple of " + std::to_string(_db_size_multiple_requirement) + " bytes");
60 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::bad_size), what_str));
61 }
62#ifdef _WIN32
63 if(mode != mapped)
64 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::unsupported_win32_mode)));
65#endif
66 if(!_writable && !bfs::exists(_data_file_path)){
67 std::string what_str("database file not found at " + _data_file_path.string());
68 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::not_found), what_str));
69 }
70
71 bfs::create_directories(dir);
72
73 if(bfs::exists(_data_file_path)) {
74 char header[header_size];
75 std::ifstream hs(_data_file_path.generic_string(), std::ifstream::binary);
76 hs.read(header, header_size);
77 if(hs.fail())
78 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::bad_header)));
79
80 db_header* dbheader = reinterpret_cast<db_header*>(header);
81 if(dbheader->id != header_id) {
82 std::string what_str("\"" + _database_name + "\" database format not compatible with this version of chainbase.");
83 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::incorrect_db_version), what_str));
84 }
85 if(!allow_dirty && dbheader->dirty) {
86 std::string what_str("\"" + _database_name + "\" database dirty flag set");
87 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::dirty)));
88 }
89 if(dbheader->dbenviron != environment()) {
90 std::cerr << "CHAINBASE: \"" << _database_name << "\" database was created with a chainbase from a different environment" << std::endl;
91 std::cerr << "Current compiler environment:" << std::endl;
92 std::cerr << environment();
93 std::cerr << "DB created with compiler environment:" << std::endl;
94 std::cerr << dbheader->dbenviron;
95 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::incompatible)));
96 }
97 }
98
99 segment_manager* file_mapped_segment_manager = nullptr;
100 if(!bfs::exists(_data_file_path)) {
101 std::ofstream ofs(_data_file_path.generic_string(), std::ofstream::trunc);
102 //win32 impl of bfs::resize_file() doesn't like the file being open
103 ofs.close();
104 bfs::resize_file(_data_file_path, shared_file_size);
105 _file_mapping = bip::file_mapping(_data_file_path.generic_string().c_str(), bip::read_write);
106 _file_mapped_region = bip::mapped_region(_file_mapping, bip::read_write);
107 file_mapped_segment_manager = new ((char*)_file_mapped_region.get_address()+header_size) segment_manager(shared_file_size-header_size);
108 new (_file_mapped_region.get_address()) db_header;
109 }
110 else if(_writable) {
111 auto existing_file_size = bfs::file_size(_data_file_path);
112 size_t grow = 0;
113 if(shared_file_size > existing_file_size) {
114 grow = shared_file_size - existing_file_size;
115 bfs::resize_file(_data_file_path, shared_file_size);
116 }
117 else if(shared_file_size < existing_file_size) {
118 std::cerr << "CHAINBASE: \"" << _database_name << "\" requested size of " << shared_file_size << " is less than "
119 "existing size of " << existing_file_size << ". This database will not be shrunk and will "
120 "remain at " << existing_file_size << std::endl;
121 }
122 _file_mapping = bip::file_mapping(_data_file_path.generic_string().c_str(), bip::read_write);
123 _file_mapped_region = bip::mapped_region(_file_mapping, bip::read_write);
124 file_mapped_segment_manager = reinterpret_cast<segment_manager*>((char*)_file_mapped_region.get_address()+header_size);
125 if(grow)
126 file_mapped_segment_manager->grow(grow);
127 }
128 else {
129 _file_mapping = bip::file_mapping(_data_file_path.generic_string().c_str(), bip::read_only);
130 _file_mapped_region = bip::mapped_region(_file_mapping, bip::read_only);
131 file_mapped_segment_manager = reinterpret_cast<segment_manager*>((char*)_file_mapped_region.get_address()+header_size);
132 }
133
134 if(_writable) {
135 //remove meta file created in earlier versions
136 boost::system::error_code ec;
137 bfs::remove(bfs::absolute(dir/"shared_memory.meta"), ec);
138
139 _mapped_file_lock = bip::file_lock(_data_file_path.generic_string().c_str());
140 if(!_mapped_file_lock.try_lock())
141 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::no_access)));
142
143 set_mapped_file_db_dirty(true);
144 }
145
146 if(mode == mapped) {
147 _segment_manager = file_mapped_segment_manager;
148 }
149 else {
150 boost::asio::io_service sig_ios;
151 boost::asio::signal_set sig_set(sig_ios, SIGINT, SIGTERM);
152#ifdef SIGPIPE
153 sig_set.add(SIGPIPE);
154#endif
155 sig_set.async_wait([](const boost::system::error_code&, int) {
156 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::aborted)));
157 });
158
159 try {
160 setup_non_file_mapping();
161 load_database_file(sig_ios);
162
163#ifndef _WIN32
164 if(mode == locked) {
165 if(mlock(_non_file_mapped_mapping, _non_file_mapped_mapping_size)) {
166 std::string what_str("Failed to mlock database \"" + _database_name + "\"");
167 BOOST_THROW_EXCEPTION(std::system_error(make_error_code(db_error_code::no_mlock), what_str));
168 }
169 std::cerr << "CHAINBASE: Database \"" << _database_name << "\" has been successfully locked in memory" << std::endl;
170 }
171#endif
172
173 _file_mapped_region = bip::mapped_region();
174 }
175 catch(...) {
176 if(_writable)
177 set_mapped_file_db_dirty(false);
178 throw;
179 }
180
181 _segment_manager = reinterpret_cast<segment_manager*>((char*)_non_file_mapped_mapping+header_size);
182 }
183}
184
185void pinnable_mapped_file::setup_non_file_mapping() {
186 int common_map_opts = MAP_PRIVATE|MAP_ANONYMOUS;
187
188 _non_file_mapped_mapping_size = _file_mapped_region.get_size();
189 auto round_up_mmaped_size = [this](unsigned r) {
190 _non_file_mapped_mapping_size = (_non_file_mapped_mapping_size + (r-1u))/r*r;
191 };
192
193 const unsigned _1gb = 1u<<30u;
194 const unsigned _2mb = 1u<<21u;
195
196#if defined(MAP_HUGETLB) && defined(MAP_HUGE_1GB)
197 _non_file_mapped_mapping = mmap(NULL, _non_file_mapped_mapping_size, PROT_READ|PROT_WRITE, common_map_opts|MAP_HUGETLB|MAP_HUGE_1GB, -1, 0);
198 if(_non_file_mapped_mapping != MAP_FAILED) {
199 round_up_mmaped_size(_1gb);
200 std::cerr << "CHAINBASE: Database \"" << _database_name << "\" using 1GB pages" << std::endl;
201 return;
202 }
203#endif
204
205#if defined(MAP_HUGETLB) && defined(MAP_HUGE_2MB)
206 //in the future as we expand to support other platforms, consider not specifying any size here so we get the default size. However
207 // when mapping the default hugepage size, we'll need to go figure out that size so that the munmap() can be specified correctly
208 _non_file_mapped_mapping = mmap(NULL, _non_file_mapped_mapping_size, PROT_READ|PROT_WRITE, common_map_opts|MAP_HUGETLB|MAP_HUGE_2MB, -1, 0);
209 if(_non_file_mapped_mapping != MAP_FAILED) {
210 round_up_mmaped_size(_2mb);
211 std::cerr << "CHAINBASE: Database \"" << _database_name << "\" using 2MB pages" << std::endl;
212 return;
213 }
214#endif
215
216#if defined(VM_FLAGS_SUPERPAGE_SIZE_2MB)
217 round_up_mmaped_size(_2mb);
218 _non_file_mapped_mapping = mmap(NULL, _non_file_mapped_mapping_size, PROT_READ|PROT_WRITE, common_map_opts, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
219 if(_non_file_mapped_mapping != MAP_FAILED) {
220 std::cerr << "CHAINBASE: Database \"" << _database_name << "\" using 2MB pages" << std::endl;
221 return;
222 }
223 _non_file_mapped_mapping_size = _file_mapped_region.get_size(); //restore to non 2MB rounded size
224#endif
225
226#ifndef _WIN32
227 _non_file_mapped_mapping = mmap(NULL, _non_file_mapped_mapping_size, PROT_READ|PROT_WRITE, common_map_opts, -1, 0);
228 if(_non_file_mapped_mapping == MAP_FAILED)
229 BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to map database ") + _database_name + ": " + strerror(errno)));
230#endif
231}
232
233void pinnable_mapped_file::load_database_file(boost::asio::io_service& sig_ios) {
234 std::cerr << "CHAINBASE: Preloading \"" << _database_name << "\" database file, this could take a moment..." << std::endl;
235 char* const src = (char*)_file_mapped_region.get_address();
236 char* const dst = (char*)_non_file_mapped_mapping;
237 size_t offset = 0;
238 time_t t = time(nullptr);
239 while(offset != _file_mapped_region.get_size()) {
240 memcpy(dst+offset, src+offset, _db_size_multiple_requirement);
241 offset += _db_size_multiple_requirement;
242
243 if(time(nullptr) != t) {
244 t = time(nullptr);
245 std::cerr << " " << offset/(_file_mapped_region.get_size()/100) << "% complete..." << std::endl;
246 }
247 sig_ios.poll();
248 }
249 std::cerr << " Complete" << std::endl;
250}
251
252bool pinnable_mapped_file::all_zeros(char* data, size_t sz) {
253 uint64_t* p = (uint64_t*)data;
254 uint64_t* end = p+sz/sizeof(uint64_t);
255 while(p != end) {
256 if(*p++ != 0)
257 return false;
258 }
259 return true;
260}
261
262void pinnable_mapped_file::save_database_file() {
263 std::cerr << "CHAINBASE: Writing \"" << _database_name << "\" database file, this could take a moment..." << std::endl;
264 char* src = (char*)_non_file_mapped_mapping;
265 char* dst = (char*)_file_mapped_region.get_address();
266 size_t offset = 0;
267 time_t t = time(nullptr);
268 while(offset != _file_mapped_region.get_size()) {
269 if(!all_zeros(src+offset, _db_size_multiple_requirement))
270 memcpy(dst+offset, src+offset, _db_size_multiple_requirement);
271 offset += _db_size_multiple_requirement;
272
273 if(time(nullptr) != t) {
274 t = time(nullptr);
275 std::cerr << " " << offset/(_file_mapped_region.get_size()/100) << "% complete..." << std::endl;
276 }
277 }
278 std::cerr << " Syncing buffers..." << std::endl;
279 if(_file_mapped_region.flush(0, 0, false) == false)
280 std::cerr << "CHAINBASE: ERROR: syncing buffers failed" << std::endl;
281 std::cerr << " Complete" << std::endl;
282}
283
285 _mapped_file_lock(std::move(o._mapped_file_lock)),
286 _data_file_path(std::move(o._data_file_path)),
287 _database_name(std::move(o._database_name)),
288 _file_mapped_region(std::move(o._file_mapped_region))
289{
290 _segment_manager = o._segment_manager;
291 _writable = o._writable;
292 _non_file_mapped_mapping = o._non_file_mapped_mapping;
293 o._non_file_mapped_mapping = nullptr;
294 o._writable = false; //prevent dtor from doing anything interesting
295}
296
298 _mapped_file_lock = std::move(o._mapped_file_lock);
299 _data_file_path = std::move(o._data_file_path);
300 _database_name = std::move(o._database_name);
301 _file_mapped_region = std::move(o._file_mapped_region);
302 _non_file_mapped_mapping = o._non_file_mapped_mapping;
303 o._non_file_mapped_mapping = nullptr;
304 _segment_manager = o._segment_manager;
305 _writable = o._writable;
306 o._writable = false; //prevent dtor from doing anything interesting
307 return *this;
308}
309
311 if(_writable) {
312 if(_non_file_mapped_mapping) { //in heap or locked mode
313 _file_mapped_region = bip::mapped_region(_file_mapping, bip::read_write);
314 save_database_file();
315#ifndef _WIN32
316 if(munmap(_non_file_mapped_mapping, _non_file_mapped_mapping_size))
317 std::cerr << "CHAINBASE: ERROR: unmapping failed: " << strerror(errno) << std::endl;
318#endif
319 }
320 else
321 if(_file_mapped_region.flush(0, 0, false) == false)
322 std::cerr << "CHAINBASE: ERROR: syncing buffers failed" << std::endl;
323 set_mapped_file_db_dirty(false);
324 }
325}
326
327void pinnable_mapped_file::set_mapped_file_db_dirty(bool dirty) {
328 *((char*)_file_mapped_region.get_address()+header_dirty_bit_offset) = dirty;
329 if(_file_mapped_region.flush(0, 0, false) == false)
330 std::cerr << "CHAINBASE: ERROR: syncing buffers failed" << std::endl;
331}
332
333std::istream& operator>>(std::istream& in, pinnable_mapped_file::map_mode& runtime) {
334 std::string s;
335 in >> s;
336 if (s == "mapped")
338 else if (s == "heap")
340 else if (s == "locked")
342 else
343 in.setstate(std::ios_base::failbit);
344 return in;
345}
346
347std::ostream& operator<<(std::ostream& osm, pinnable_mapped_file::map_mode m) {
349 osm << "mapped";
351 osm << "heap";
353 osm << "locked";
354
355 return osm;
356}
357
358static std::string print_os(environment::os_t os) {
359 switch(os) {
360 case environment::OS_LINUX: return "Linux";
361 case environment::OS_MACOS: return "macOS";
362 case environment::OS_WINDOWS: return "Windows";
363 case environment::OS_OTHER: return "Unknown";
364 }
365 return "error";
366}
367static std::string print_arch(environment::arch_t arch) {
368 switch(arch) {
369 case environment::ARCH_X86_64: return "x86_64";
370 case environment::ARCH_ARM: return "ARM";
371 case environment::ARCH_RISCV: return "RISC-v";
372 case environment::ARCH_OTHER: return "Unknown";
373 }
374 return "error";
375}
376
377std::ostream& operator<<(std::ostream& os, const chainbase::environment& dt) {
378 os << std::right << std::setw(17) << "Compiler: " << dt.compiler << std::endl;
379 os << std::right << std::setw(17) << "Debug: " << (dt.debug ? "Yes" : "No") << std::endl;
380 os << std::right << std::setw(17) << "OS: " << print_os(dt.os) << std::endl;
381 os << std::right << std::setw(17) << "Arch: " << print_arch(dt.arch) << std::endl;
382 os << std::right << std::setw(17) << "Boost: " << dt.boost_version/100000 << "."
383 << dt.boost_version/100%1000 << "."
384 << dt.boost_version%100 << std::endl;
385 return os;
386}
387
388}
const mie::Vuint & p
Definition bn.cpp:27
const mie::Vuint & r
Definition bn.cpp:28
std::string message(int ev) const override
const char * name() const noexcept override
bip::managed_mapped_file::segment_manager segment_manager
pinnable_mapped_file & operator=(pinnable_mapped_file &&)
pinnable_mapped_file(const bfs::path &dir, bool writable, uint64_t shared_file_size, bool allow_dirty, map_mode mode)
os_t os
environment()
arch_t arch
std::error_code make_error_code(db_error_code e) noexcept
constexpr size_t header_dirty_bit_offset
DataStream & operator<<(DataStream &ds, const oid< OidType > &oid)
constexpr size_t header_size
constexpr uint64_t header_id
const std::error_category & chainbase_error_category()
DataStream & operator>>(DataStream &ds, oid< OidType > &oid)
Definition name.hpp:106
unsigned __int64 uint64_t
Definition stdint.h:136
char * s
memcpy((char *) pInfo->slotDescription, s, l)