PMDK C++ bindings  1.2.0
This is the C++ bindings documentation for PMDK's libpmemobj.
transaction.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016-2019, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
38 #ifndef LIBPMEMOBJ_CPP_TRANSACTION_HPP
39 #define LIBPMEMOBJ_CPP_TRANSACTION_HPP
40 
41 #include <functional>
42 #include <string>
43 
46 #include <libpmemobj++/pool.hpp>
47 #include <libpmemobj/tx_base.h>
48 
49 namespace pmem
50 {
51 
52 namespace obj
53 {
54 
72 class transaction {
73 public:
92  class manual {
93  public:
107  template <typename... L>
108  manual(obj::pool_base &pop, L &... locks)
109  {
110  if (pmemobj_tx_begin(pop.handle(), nullptr,
111  TX_PARAM_NONE) != 0)
112  throw transaction_error(
113  "failed to start transaction");
114 
115  auto err = add_lock(locks...);
116 
117  if (err) {
118  pmemobj_tx_abort(EINVAL);
119  throw transaction_error("failed to add lock");
120  }
121  }
122 
130  ~manual() noexcept
131  {
132  /* normal exit or with an active exception */
133  if (pmemobj_tx_stage() == TX_STAGE_WORK)
134  pmemobj_tx_abort(ECANCELED);
135 
136  (void)pmemobj_tx_end();
137  }
138 
142  manual(const manual &p) = delete;
143 
147  manual(const manual &&p) = delete;
148 
152  manual &operator=(const manual &p) = delete;
153 
157  manual &operator=(manual &&p) = delete;
158  };
159 
160 /*
161  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
162  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
163  */
164 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
165 
184  class automatic {
185  public:
203  template <typename... L>
204  automatic(obj::pool_base &pop, L &... locks)
205  : tx_worker(pop, locks...)
206  {
207  }
208 
219  ~automatic() noexcept(false)
220  {
221  /* active exception, abort handled by tx_worker */
222  if (exceptions.new_uncaught_exception())
223  return;
224 
225  /* transaction ended normally */
226  if (pmemobj_tx_stage() == TX_STAGE_WORK)
227  pmemobj_tx_commit();
228  /* transaction aborted, throw an exception */
229  else if (pmemobj_tx_stage() == TX_STAGE_ONABORT ||
230  (pmemobj_tx_stage() == TX_STAGE_FINALLY &&
231  pmemobj_tx_errno() != 0))
232  throw transaction_error("Transaction aborted");
233  }
234 
238  automatic(const automatic &p) = delete;
239 
243  automatic(const automatic &&p) = delete;
244 
248  automatic &operator=(const automatic &p) = delete;
249 
253  automatic &operator=(automatic &&p) = delete;
254 
255  private:
260  public:
268  : count(std::uncaught_exceptions())
269  {
270  }
271 
279  bool
281  {
282  return std::uncaught_exceptions() > this->count;
283  }
284 
285  private:
289  int count;
290  } exceptions;
291 
292  transaction::manual tx_worker;
293  };
294 #endif /* __cpp_lib_uncaught_exceptions */
295 
296  /*
297  * Deleted default constructor.
298  */
299  transaction() = delete;
300 
307  ~transaction() noexcept = delete;
308 
323  static void
324  abort(int err)
325  {
326  if (pmemobj_tx_stage() != TX_STAGE_WORK)
327  throw transaction_error("wrong stage for abort");
328 
329  pmemobj_tx_abort(err);
330  throw manual_tx_abort("explicit abort " + std::to_string(err));
331  }
332 
343  static void
345  {
346  if (pmemobj_tx_stage() != TX_STAGE_WORK)
347  throw transaction_error("wrong stage for commit");
348 
349  pmemobj_tx_commit();
350  }
351 
352  static int
353  error() noexcept
354  {
355  return pmemobj_tx_errno();
356  }
357 
358  POBJ_CPP_DEPRECATED static int
359  get_last_tx_error() noexcept
360  {
361  return transaction::error();
362  }
363 
395  template <typename... Locks>
396  static void
397  run(pool_base &pool, std::function<void()> tx, Locks &... locks)
398  {
399  if (pmemobj_tx_begin(pool.handle(), nullptr, TX_PARAM_NONE) !=
400  0)
401  throw transaction_error("failed to start transaction");
402 
403  auto err = add_lock(locks...);
404 
405  if (err) {
406  pmemobj_tx_abort(err);
407  (void)pmemobj_tx_end();
408  throw transaction_error(
409  "failed to add a lock to the transaction");
410  }
411 
412  try {
413  tx();
414  } catch (manual_tx_abort &) {
415  (void)pmemobj_tx_end();
416  throw;
417  } catch (...) {
418  /* first exception caught */
419  if (pmemobj_tx_stage() == TX_STAGE_WORK)
420  pmemobj_tx_abort(ECANCELED);
421 
422  /* waterfall tx_end for outer tx */
423  (void)pmemobj_tx_end();
424  throw;
425  }
426 
427  auto stage = pmemobj_tx_stage();
428 
429  if (stage == TX_STAGE_WORK) {
430  pmemobj_tx_commit();
431  } else if (stage == TX_STAGE_ONABORT) {
432  (void)pmemobj_tx_end();
433  throw transaction_error("transaction aborted");
434  } else if (stage == TX_STAGE_NONE) {
435  throw transaction_error(
436  "transaction ended prematurely");
437  }
438 
439  (void)pmemobj_tx_end();
440  }
441 
442  template <typename... Locks>
443  POBJ_CPP_DEPRECATED static void
444  exec_tx(pool_base &pool, std::function<void()> tx, Locks &... locks)
445  {
446  transaction::run(pool, tx, locks...);
447  }
448 
468  template <typename T,
469  typename std::enable_if<IS_TRIVIALLY_COPYABLE(T), T>::type * =
470  nullptr>
471  static void
472  snapshot(const T *addr, size_t num = 1)
473  {
474  if (TX_STAGE_WORK != pmemobj_tx_stage())
475  throw transaction_error(
476  "wrong stage for taking a snapshot.");
477 
478  if (pmemobj_tx_add_range_direct(addr, sizeof(*addr) * num))
479  throw transaction_error(
480  "Could not take a snapshot of given memory range.");
481  }
482 
483 private:
496  template <typename L, typename... Locks>
497  static int
498  add_lock(L &lock, Locks &... locks) noexcept
499  {
500  auto err =
501  pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
502 
503  if (err)
504  return err;
505 
506  return add_lock(locks...);
507  }
508 
512  static inline int
513  add_lock() noexcept
514  {
515  return 0;
516  }
517 };
518 
519 } /* namespace obj */
520 
521 } /* namespace pmem */
522 
523 #endif /* LIBPMEMOBJ_CPP_TRANSACTION_HPP */
int count
The number of active exceptions.
Definition: transaction.hpp:289
static int add_lock() noexcept
Method ending the recursive algorithm.
Definition: transaction.hpp:513
The non-template pool base class.
Definition: pool.hpp:67
Internal class for counting active exceptions.
Definition: transaction.hpp:259
~manual() noexcept
Destructor.
Definition: transaction.hpp:130
~automatic() noexcept(false)
Destructor.
Definition: transaction.hpp:219
C++ transaction handler class.
Definition: transaction.hpp:72
Custom transaction error class.
Definition: pexceptions.hpp:114
Definition: pext.hpp:342
static void snapshot(const T *addr, size_t num=1)
Takes a “snapshot” of given elements of type T number (1 by default), located at the given address ...
Definition: transaction.hpp:472
C++ manual scope transaction class.
Definition: transaction.hpp:92
PMEMobj pool class.
Definition: persistent_ptr.hpp:59
manual & operator=(const manual &p)=delete
Deleted assignment operator.
PMEMobjpool * handle() noexcept
Gets the C style handle to the pool.
Definition: pool.hpp:398
manual(obj::pool_base &pop, L &... locks)
RAII constructor with pmem resident locks.
Definition: transaction.hpp:108
Commonly used functionality.
Custom exceptions.
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:344
static void abort(int err)
Manually abort the current transaction.
Definition: transaction.hpp:324
~transaction() noexcept=delete
Default destructor.
C++ automatic scope transaction class.
Definition: transaction.hpp:184
Custom transaction error class.
Definition: pexceptions.hpp:63
static int add_lock(L &lock, Locks &... locks) noexcept
Recursively add locks to the active transaction.
Definition: transaction.hpp:498
bool new_uncaught_exception()
Notifies is a new exception is being handled.
Definition: transaction.hpp:280
automatic(obj::pool_base &pop, L &... locks)
RAII constructor with pmem resident locks.
Definition: transaction.hpp:204
uncaught_exception_counter()
Default constructor.
Definition: transaction.hpp:267
Resides on pmem class.
Definition: p.hpp:64
Definition: allocator.hpp:48
C++ pmemobj pool.
static void run(pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:397
automatic & operator=(const automatic &p)=delete
Deleted assignment operator.