Electroneum
database.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2018, The Monero Project
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without modification, are
5 // permitted provided that the following conditions are met:
6 //
7 // 1. Redistributions of source code must retain the above copyright notice, this list of
8 // conditions and the following disclaimer.
9 //
10 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 // of conditions and the following disclaimer in the documentation and/or other
12 // materials provided with the distribution.
13 //
14 // 3. Neither the name of the copyright holder nor the names of its contributors may be
15 // used to endorse or promote products derived from this software without specific
16 // prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
19 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "database.h"
28 #include "lmdb/error.h"
29 #include "lmdb/util.h"
30 
31 #ifdef _WIN32
32 namespace
33 {
34  constexpr const mdb_mode_t open_flags = 0;
35 }
36 #else
37 #include <sys/stat.h>
38 
39 namespace
40 {
41  constexpr const mdb_mode_t open_flags = (S_IRUSR | S_IWUSR);
42 }
43 #endif
44 
45 namespace lmdb
46 {
47  namespace
48  {
49  constexpr const mdb_size_t max_resize = 1 * 1024 * 1024 * 1024; // 1 GB
50  void acquire_context(context& ctx) noexcept
51  {
52  while (ctx.lock.test_and_set());
53  ++(ctx.active);
54  ctx.lock.clear();
55  }
56 
57  void release_context(context& ctx) noexcept
58  {
59  --(ctx.active);
60  }
61  }
62 
63  void release_read_txn::operator()(MDB_txn* ptr) const noexcept
64  {
65  if (ptr)
66  {
67  MDB_env* const env = mdb_txn_env(ptr);
68  abort_txn{}(ptr);
69  if (env)
70  {
71  context* ctx = reinterpret_cast<context*>(mdb_env_get_userctx(env));
72  if (ctx)
73  release_context(*ctx);
74  }
75  }
76  }
77 
78  expect<environment> open_environment(const char* path, MDB_dbi max_dbs) noexcept
79  {
80  ELECTRONEUM_PRECOND(path != nullptr);
81 
82  MDB_env* obj = nullptr;
83  ELECTRONEUM_LMDB_CHECK(mdb_env_create(std::addressof(obj)));
84  environment out{obj};
85 
86  ELECTRONEUM_LMDB_CHECK(mdb_env_set_maxdbs(out.get(), max_dbs));
87  ELECTRONEUM_LMDB_CHECK(mdb_env_open(out.get(), path, 0, open_flags));
88  return {std::move(out)};
89  }
90 
91  expect<write_txn> database::do_create_txn(unsigned int flags) noexcept
92  {
93  ELECTRONEUM_PRECOND(handle() != nullptr);
94 
95  for (unsigned attempts = 0; attempts < 3; ++attempts)
96  {
97  acquire_context(ctx);
98 
99  MDB_txn* txn = nullptr;
100  const int err =
101  mdb_txn_begin(handle(), nullptr, flags, &txn);
102  if (!err && txn != nullptr)
103  return write_txn{txn};
104 
105  release_context(ctx);
106  if (err != MDB_MAP_RESIZED)
107  return {lmdb::error(err)};
108  ELECTRONEUM_CHECK(this->resize());
109  }
110  return {lmdb::error(MDB_MAP_RESIZED)};
111  }
112 
114  : env(std::move(env)), ctx{{}, ATOMIC_FLAG_INIT}
115  {
116  if (handle())
117  {
118  const int err = mdb_env_set_userctx(handle(), std::addressof(ctx));
119  if (err)
120  ELECTRONEUM_THROW(lmdb::error(err), "Failed to set user context");
121  }
122  }
123 
125  {
126  while (ctx.active);
127  }
128 
130  {
131  ELECTRONEUM_PRECOND(handle() != nullptr);
132 
133  while (ctx.lock.test_and_set());
134  while (ctx.active);
135 
136  MDB_envinfo info{};
138 
139  const mdb_size_t resize = std::min(info.me_mapsize, max_resize);
140  const int err = mdb_env_set_mapsize(handle(), info.me_mapsize + resize);
141  ctx.lock.clear();
142  if (err)
143  return {lmdb::error(err)};
144  return success();
145  }
146 
148  {
149  if (txn)
150  {
151  acquire_context(ctx);
152  const int err = mdb_txn_renew(txn.get());
153  if (err)
154  {
155  release_context(ctx);
156  return {lmdb::error(err)};
157  }
158  return read_txn{txn.release()};
159  }
160  auto new_txn = do_create_txn(MDB_RDONLY);
161  if (new_txn)
162  return read_txn{new_txn->release()};
163  return new_txn.error();
164  }
165 
167  {
168  ELECTRONEUM_PRECOND(txn != nullptr);
169  mdb_txn_reset(txn.get());
170  release_context(ctx);
171  return suspended_txn{txn.release()};
172  }
173 
175  {
176  return do_create_txn(0);
177  }
178 
180  {
181  ELECTRONEUM_PRECOND(txn != nullptr);
183  txn.release();
184  release_context(ctx);
185  return success();
186  }
187 } // lmdb
Context given to LMDB.
Definition: database.h:57
virtual ~database() noexcept
Definition: database.cpp:124
int mdb_env_set_mapsize(MDB_env *env, mdb_size_t size)
Set the size of the memory map to use for this environment.
#define MDB_RDONLY
Definition: lmdb.h:320
expect< read_txn > create_read_txn(suspended_txn txn=nullptr) noexcept
Definition: database.cpp:147
#define ELECTRONEUM_LMDB_CHECK(...)
Executes a LMDB command, and returns errors via lmdb::error enum.
Definition: error.h:33
int mdb_txn_commit(MDB_txn *txn)
Commit all the operations of a transaction into the database.
void * mdb_env_get_userctx(MDB_env *env)
Get the application information associated with the MDB_env.
std::unique_ptr< MDB_txn, abort_txn > suspended_txn
Definition: transaction.h:92
int mdb_env_info(MDB_env *env, MDB_envinfo *stat)
Return information about the LMDB environment.
STL namespace.
std::unique_ptr< MDB_txn, abort_write_txn > write_txn
Definition: transaction.h:94
int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
Set the maximum number of named databases for the environment.
struct MDB_env MDB_env
Opaque structure for a database environment.
Definition: lmdb.h:260
MDB_env * mdb_txn_env(MDB_txn *txn)
Returns the transaction&#39;s MDB_env.
expect< environment > open_environment(const char *path, MDB_dbi max_dbs) noexcept
Definition: database.cpp:78
std::unique_ptr< MDB_env, close_env > environment
Definition: database.h:51
database(environment env)
Definition: database.cpp:113
std::unique_ptr< MDB_txn, release_read_txn > read_txn
Definition: transaction.h:93
struct MDB_txn MDB_txn
Opaque structure for a transaction handle.
Definition: lmdb.h:267
std::atomic_flag lock
Definition: database.h:60
#define MDB_MAP_RESIZED
Definition: lmdb.h:465
int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
Open an environment handle.
Information about the environment.
Definition: lmdb.h:501
expect< void > resize() noexcept
Definition: database.cpp:129
void mdb_txn_reset(MDB_txn *txn)
Reset a read-only transaction.
std::atomic< std::size_t > active
Definition: database.h:59
Definition: expect.h:70
expect< write_txn > create_write_txn() noexcept
Definition: database.cpp:174
int mdb_txn_renew(MDB_txn *txn)
Renew a read-only transaction.
#define ELECTRONEUM_CHECK(...)
Check expect<void> and return errors in current scope.
Definition: expect.h:47
expect< void > commit(write_txn txn) noexcept
Commit the read-write transaction.
Definition: database.cpp:179
unsigned int MDB_dbi
A handle for an individual database in the DB environment.
Definition: lmdb.h:270
int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
Create a transaction for use with the environment.
int mdb_env_create(MDB_env **env)
Create an LMDB environment handle.
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
expect< void > success() noexcept
Definition: expect.h:397
mode_t mdb_mode_t
Definition: lmdb.h:181
const T & move(const T &t)
Definition: gtest-port.h:1317
void operator()(MDB_txn *ptr) const noexcept
Definition: database.cpp:63
#define ELECTRONEUM_PRECOND(...)
If precondition fails, return ::error::kInvalidArgument in current scope.
Definition: expect.h:39
int mdb_env_set_userctx(MDB_env *env, void *ctx)
Set application information associated with the MDB_env.
expect< suspended_txn > reset_txn(read_txn txn) noexcept
Definition: database.cpp:166
#define ELECTRONEUM_THROW(code, msg)
Definition: expect.h:66
size_t mdb_size_t
Definition: lmdb.h:196
error
Tracks LMDB error codes.
Definition: error.h:44