pstore2
database.hpp
Go to the documentation of this file.
1 //===- include/pstore/core/database.hpp -------------------*- mode: C++ -*-===//
2 //* _ _ _ *
3 //* __| | __ _| |_ __ _| |__ __ _ ___ ___ *
4 //* / _` |/ _` | __/ _` | '_ \ / _` / __|/ _ \ *
5 //* | (_| | (_| | || (_| | |_) | (_| \__ \ __/ *
6 //* \__,_|\__,_|\__\__,_|_.__/ \__,_|___/\___| *
7 //* *
8 //===----------------------------------------------------------------------===//
9 //
10 // Part of the pstore project, under the Apache License v2.0 with LLVM Exceptions.
11 // See https://github.com/SNSystems/pstore/blob/master/LICENSE.txt for license
12 // information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //===----------------------------------------------------------------------===//
17 
18 #ifndef PSTORE_CORE_DATABASE_HPP
19 #define PSTORE_CORE_DATABASE_HPP
20 
24 #include "pstore/core/storage.hpp"
27 #include "pstore/support/head_revision.hpp"
28 
29 namespace pstore {
30 
31  //* _ _ _ *
32  //* __| | __ _| |_ __ _| |__ __ _ ___ ___ *
33  //* / _` |/ _` | __/ _` | '_ \ / _` / __|/ _ \ *
34  //* | (_| | (_| | || (_| | |_) | (_| \__ \ __/ *
35  //* \__,_|\__,_|\__\__,_|_.__/ \__,_|___/\___| *
36  //* *
37 
38  class heartbeat;
39 
40  class database {
41  public:
42  enum class access_mode { read_only, writable, writeable_no_create };
43 
50  explicit database (std::string const & path, access_mode am,
51  bool access_tick_enabled = true);
52 
55  template <typename File>
56  explicit database (std::shared_ptr<File> file,
57  std::unique_ptr<system_page_size_interface> && page_size,
58  std::unique_ptr<region::factory> && region_factory,
59  bool access_tick_enabled = true);
60 
61  template <typename File>
62  explicit database (std::shared_ptr<File> file, bool access_tick_enabled = true)
63  : database (file, std::make_unique<system_page_size> (),
64  region::get_factory (file, storage::full_region_size,
65  storage::min_region_size),
66  access_tick_enabled) {}
67 
68  database (database &&) = delete;
69  database (database const &) = delete;
70 
71  virtual ~database () noexcept;
72 
73  database & operator= (database &&) = delete;
74  database & operator= (database const &) = delete;
75 
76 
80  std::uint64_t size () const noexcept { return size_.logical_size (); }
81 
83  std::string path () const { return storage_.file ()->path (); }
84 
87  file::file_base const * file () const { return storage_.file (); }
88  file::file_base * file () { return storage_.file (); }
90 
93  static void build_new_store (file::file_base & file);
94 
96  void sync (unsigned revision = head_revision);
97 
106  typed_address<trailer> older_revision_footer_pos (unsigned revision) const;
107 
108  static constexpr bool small_files_enabled () noexcept {
109  return region::small_files_enabled ();
110  }
111 
112  std::unique_lock<file::range_lock> * upgrade_to_write_lock ();
113  std::time_t latest_time () const {
114  auto lt = this->file ()->latest_time ();
115 #ifdef _WIN32
116  lt = std::max (lt, this->get_shared ()->time.load ());
117 #endif
118  return lt;
119  }
120  bool is_writable () const noexcept { return storage_.file ()->is_writable (); }
121 
128  std::shared_ptr<void const> getro (address const addr, std::size_t const size) const {
129  return this->get (addr, size, true /*initialized*/, false /*writable*/);
130  }
131 
136  template <typename T,
137  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
138  std::shared_ptr<T const> getro (extent<T> const & ex) const {
139  if (ex.addr.to_address ().absolute () % alignof (T) != 0) {
140  raise (error_code::bad_alignment);
141  }
142  // Note that ex.size specifies the size in bytes of the data to be loaded, not the
143  // number of elements of type T. For this reason we call the plain address version of
144  // getro().
145  return std::static_pointer_cast<T const> (this->getro (ex.addr.to_address (), ex.size));
146  }
147 
153  template <typename T,
154  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
155  std::shared_ptr<T const> getro (typed_address<T> const addr) const {
156  return this->getro (addr, std::size_t{1});
157  }
158 
165  template <typename T,
166  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
167  std::shared_ptr<T const> getro (typed_address<T> const addr,
168  std::size_t const elements) const {
169  if (addr.to_address ().absolute () % alignof (T) != 0) {
170  raise (error_code::bad_alignment);
171  }
172  return std::static_pointer_cast<T const> (
173  this->getro (addr.to_address (), sizeof (T) * elements));
174  }
176 
183 
189  std::shared_ptr<void> getrw (address const addr, std::size_t const size) {
190  return std::const_pointer_cast<void> (
191  this->get (addr, size, true /*initialized*/, true /*writable*/));
192  }
193 
198  template <typename T,
199  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
200  std::shared_ptr<T> getrw (extent<T> const & ex) {
201  if (ex.addr.to_address ().absolute () % alignof (T) != 0) {
202  raise (error_code::bad_alignment);
203  }
204  // Note that ex.size specifies the size in bytes of the data to be loaded, not the
205  // number of elements of type T. For this reason we call the plain address version of
206  // getro().
207  return std::static_pointer_cast<T> (this->getrw (ex.addr.to_address (), ex.size));
208  }
209 
214  template <typename T,
215  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
216  std::shared_ptr<T> getrw (typed_address<T> addr) {
217  return this->getrw<T> (addr, std::size_t{1});
218  }
219 
225  template <typename T,
226  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
227  std::shared_ptr<T> getrw (typed_address<T> const addr, std::size_t const elements) {
228  if (addr.to_address ().absolute () % alignof (T) != 0) {
229  raise (error_code::bad_alignment);
230  }
231  return std::static_pointer_cast<T> (
232  this->getrw (addr.to_address (), sizeof (T) * elements));
233  }
235 
236  // (Virtual for mocking.)
237  virtual auto get (address addr, std::size_t size, bool initialized, bool writable) const
238  -> std::shared_ptr<void const>;
239 
241  enum class vacuum_mode {
242  disabled,
243  immediate,
244  background,
245  };
246  void set_vacuum_mode (vacuum_mode const mode) noexcept { vacuum_mode_ = mode; }
247  vacuum_mode get_vacuum_mode () const noexcept { return vacuum_mode_; }
249 
251  class storage const & storage () const noexcept {
252  return storage_;
253  }
254 
255  void close ();
256 
257  header const & get_header () const noexcept { return *header_; }
258  typed_address<trailer> footer_pos () const noexcept { return size_.footer_pos (); }
259 
262  unsigned get_current_revision () const { return get_footer ()->a.generation.load (); }
263 
270  std::string get_sync_name () const {
271  PSTORE_ASSERT (sync_name_.length () > 0);
272  return sync_name_;
273  }
274 
275  std::string shared_memory_name () const { return this->get_sync_name () + ".pst"; }
276 
290  virtual address allocate (std::uint64_t bytes, unsigned align);
291 
292  virtual void truncate (std::uint64_t size);
293 
296  void set_new_footer (typed_address<trailer> new_footer_pos);
297 
298  void protect (address const first, address const last) { storage_.protect (first, last); }
299 
305  static bool crc_checks_enabled ();
306 
307  void set_id (uuid const & id) noexcept { header_->set_id (id); }
308 
309  shared const * get_shared () const;
310  shared * get_shared ();
311 
315  // \warning This function is dangerous. It returns a non-const index from a const
316  // database.
317  std::shared_ptr<index::index_base> &
318  get_mutable_index (enum pstore::trailer::indices const which) const {
319  return indices_[static_cast<std::underlying_type<decltype (which)>::type> (which)];
320  }
321  std::shared_ptr<trailer const> get_footer () const {
322  return this->getro (this->footer_pos ());
323  }
324 
325  private:
326  class storage storage_;
327  std::shared_ptr<header> header_;
328  file::range_lock range_lock_;
329  std::unique_lock<file::range_lock> lock_;
330 
331  vacuum_mode vacuum_mode_ = vacuum_mode::disabled;
332  bool modified_ = false;
333  bool closed_ = false;
334 
343  class sizes {
344  public:
345  sizes () noexcept = default;
346  constexpr explicit sizes (typed_address<trailer> const footer_pos) noexcept
347  : footer_pos_{footer_pos}
348  , logical_{footer_pos_.absolute () + sizeof (trailer)} {}
349 
350  typed_address<trailer> footer_pos () const noexcept { return footer_pos_; }
351  std::uint64_t logical_size () const noexcept { return logical_; }
352 
353  void update_footer_pos (typed_address<trailer> const new_footer_pos) noexcept {
354  PSTORE_ASSERT (new_footer_pos.absolute () >= leader_size);
355  footer_pos_ = new_footer_pos;
356  logical_ = std::max (logical_, footer_pos_.absolute () + sizeof (trailer));
357  }
358 
359  void update_logical_size (std::uint64_t const new_logical_size) noexcept {
360  PSTORE_ASSERT (new_logical_size >= footer_pos_.absolute () + sizeof (trailer));
361  logical_ = std::max (logical_, new_logical_size);
362  }
363 
364  void truncate_logical_size (std::uint64_t const new_logical_size) noexcept {
365  PSTORE_ASSERT (new_logical_size >= footer_pos_.absolute () + sizeof (trailer));
366  logical_ = new_logical_size;
367  }
368 
369  private:
371 
373  std::uint64_t logical_ = 0;
374  };
375  sizes size_;
376 
377  mutable std::array<std::shared_ptr<index::index_base>,
378  static_cast<unsigned> (trailer::indices::last)>
379  indices_;
380  std::string sync_name_;
381  static constexpr auto const sync_name_length = std::size_t{20};
382 
383 
384  shared_memory<shared> shared_;
385  std::shared_ptr<heartbeat> heartbeat_;
386 
389  void clear_index_cache ();
390 
392  address first_writable_address () const;
393 
398  auto get_spanning (address addr, std::size_t size, bool initialized, bool writable) const
399  -> std::shared_ptr<void const>;
400 
401 
402 
403  template <typename File>
404  static typed_address<trailer> get_footer_pos (File & file);
405 
406  static std::string build_sync_name (header const & header);
407 
408 
411  void map_bytes (std::uint64_t new_size);
412 
422  static auto open (std::string const & path, access_mode am)
423  -> std::shared_ptr<file::file_handle>;
424 
427  void finish_init (bool access_tick_enabled);
428  };
429 
430 
431  // (ctor)
432  // ~~~~~~
433  template <typename File>
434  database::database (std::shared_ptr<File> file,
435  std::unique_ptr<system_page_size_interface> && page_size,
436  std::unique_ptr<region::factory> && region_factory,
437  bool const access_tick_enabled)
438  : storage_{std::move (file), std::move (page_size), std::move (region_factory)}
439  , size_{database::get_footer_pos (*file)} {
440 
441  this->finish_init (access_tick_enabled);
442  }
443 
444 
445  // get_footer_pos [static]
446  // ~~~~~~~~~~~~~~
447  template <typename File>
448  auto database::get_footer_pos (File & file) -> typed_address<trailer> {
449  static_assert (std::is_base_of<file::file_base, File>::value,
450  "File type must be derived from file::file_base");
451  PSTORE_ASSERT (file.is_open ());
452 
453  std::aligned_storage<sizeof (header), alignof (header)>::type header_storage{};
454  auto h = reinterpret_cast<header *> (&header_storage);
455  file.seek (0);
456  file.read (h);
457 
458  auto const dtor = [] (header * const p) { p->~header (); };
459  std::unique_ptr<header, decltype (dtor)> deleter (h, dtor);
460 
461 #if PSTORE_SIGNATURE_CHECKS_ENABLED
462  if (h->a.signature1 != header::file_signature1 ||
463  h->a.signature2 != header::file_signature2) {
464  raise (error_code::header_corrupt, file.path ());
465  }
466 #endif
467  if (h->a.header_size != sizeof (class header) || h->a.version[0] != header::major_version ||
468  h->a.version[1] != header::minor_version) {
469  raise (error_code::header_version_mismatch, file.path ());
470  }
471  if (!h->is_valid ()) {
472  raise (pstore::error_code::header_corrupt, file.path ());
473  }
474 
475  typed_address<trailer> const result = h->footer_pos.load ();
476  std::uint64_t const footer_offset = result.absolute ();
477  std::uint64_t const file_size = file.size ();
478  if (footer_offset < leader_size || file_size < leader_size + sizeof (trailer) ||
479  footer_offset > file_size - sizeof (trailer)) {
480  raise (error_code::header_corrupt, file.path ());
481  }
482  return result;
483  }
484 
485 } // namespace pstore
486 
487 #endif // PSTORE_CORE_DATABASE_HPP
Implements sstring_view, a class which is based on std::string_view but holds a pointer which may be ...
std::shared_ptr< index::index_base > & get_mutable_index(enum pstore::trailer::indices const which) const
Returns a pointer to an index base.
Definition: database.hpp:318
std::shared_ptr< T > getrw(typed_address< T > const addr, std::size_t const elements)
Returns a pointer to a mutable array of instances of type T.
Definition: database.hpp:227
typed_address< trailer > older_revision_footer_pos(unsigned revision) const
Returns the address of the footer of a specified revision.
Definition: database.cpp:147
std::shared_ptr< void const > getro(address const addr, std::size_t const size) const
Definition: database.hpp:128
typed_address< T > addr
The address of the data associated with this extent.
Definition: address.hpp:456
An extent is a contiguous area of storage reserved for a data BLOB, represented as a range...
Definition: address.hpp:441
Opens a shared memory object containing type Ty with the given name.
Definition: shared_memory.hpp:92
The transaction footer structure.
Definition: file_header.hpp:206
virtual void truncate(std::uint64_t size)
Definition: database.cpp:435
Definition: address.hpp:81
class storage const & storage() const noexcept
For unit testing.
Definition: database.hpp:251
std::string path() const
Returns the path of the file in which this database is contained.
Definition: database.hpp:83
posix::deleter deleter
The cross-platform name for the deleter class.
Definition: file_posix.hpp:54
file::file_base const * file() const
Returns the file in which this database is contained.
Definition: database.hpp:87
void sync(unsigned revision=head_revision)
Update to a specified revision of the data.
Definition: database.cpp:176
Definition: address.hpp:231
std::shared_ptr< T > getrw(extent< T > const &ex)
Loads a block of storage at the address and size given by ex.
Definition: database.hpp:200
std::shared_ptr< void > getrw(address const addr, std::size_t const size)
Load a block of data starting at address addr and of size bytes.
Definition: database.hpp:189
void set_new_footer(typed_address< trailer > new_footer_pos)
Call as part of completing a transaction.
Definition: database.cpp:452
A synchronization object that can be used to protect data in a file from being simultaneously accesse...
Definition: file.hpp:382
static bool crc_checks_enabled()
Returns true if CRC checks are enabled.
Definition: database.cpp:40
unsigned get_current_revision() const
Returns the generation number to which the database is synced.
Definition: database.hpp:262
std::uint64_t size
The size of the data associated with this extent.
Definition: address.hpp:461
Definition: storage.hpp:58
std::shared_ptr< T const > getro(typed_address< T > const addr, std::size_t const elements) const
Returns a pointer to a read-only array of instances of type T.
Definition: database.hpp:167
The file header and footer member functions.
std::string get_sync_name() const
Returns the name of the store&#39;s synchronisation object.
Definition: database.hpp:270
Definition: nonpod2.cpp:40
std::uint64_t size() const noexcept
Returns the logical size of the data store.
Definition: database.hpp:80
std::shared_ptr< T const > getro(extent< T > const &ex) const
Load a block of data starting at the address and size specified by ex.
Definition: database.hpp:138
Definition: database.hpp:40
static void build_new_store(file::file_base &file)
Constructs the basic data store structures in an empty file.
Definition: database.cpp:248
std::shared_ptr< T > getrw(typed_address< T > addr)
Returns a pointer to a mutable instance of type T.
Definition: database.hpp:216
std::shared_ptr< T const > getro(typed_address< T > const addr) const
Returns a pointer to a immutable instance of type T.
Definition: database.hpp:155
virtual address allocate(std::uint64_t bytes, unsigned align)
Appends an amount of storage to the database.
Definition: database.cpp:403
Definition: vacuum_intf.hpp:43
The uuid class is used to represent Universally Unique Identifiers (UUID) as defined by RFC 4122...
Definition: uuid.hpp:40
An abstract file class. Provides the interface for file access.
Definition: file.hpp:176
The data store file header.
Definition: file_header.hpp:78
database(std::string const &path, access_mode am, bool access_tick_enabled=true)
Creates a database instance give the path of the file to use.
Definition: database.cpp:61