PMDK C++ bindings  1.10.1
This is the C++ bindings documentation for PMDK's libpmemobj.
transaction.hpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: BSD-3-Clause
2 /* Copyright 2016-2019, Intel Corporation */
3 
9 #ifndef LIBPMEMOBJ_CPP_TRANSACTION_HPP
10 #define LIBPMEMOBJ_CPP_TRANSACTION_HPP
11 
12 #include <array>
13 #include <functional>
14 #include <string>
15 #include <vector>
16 
19 #include <libpmemobj++/pool.hpp>
20 #include <libpmemobj/tx_base.h>
21 
22 namespace pmem
23 {
24 
25 namespace obj
26 {
27 
51 class transaction {
52 public:
71  class manual {
72  public:
86  template <typename... L>
87  manual(obj::pool_base &pop, L &... locks)
88  {
89  int ret = 0;
90 
91  if (pmemobj_tx_stage() == TX_STAGE_NONE) {
92  ret = pmemobj_tx_begin(pop.handle(), nullptr,
93  TX_PARAM_CB,
95  nullptr, TX_PARAM_NONE);
96  } else {
97  ret = pmemobj_tx_begin(pop.handle(), nullptr,
98  TX_PARAM_NONE);
99  }
100 
101  if (ret != 0)
103  "failed to start transaction")
104  .with_pmemobj_errormsg();
105 
106  auto err = add_lock(locks...);
107 
108  if (err) {
109  pmemobj_tx_abort(EINVAL);
110  (void)pmemobj_tx_end();
112  "failed to add lock")
113  .with_pmemobj_errormsg();
114  }
115  }
116 
124  ~manual() noexcept
125  {
126  /* normal exit or with an active exception */
127  if (pmemobj_tx_stage() == TX_STAGE_WORK)
128  pmemobj_tx_abort(ECANCELED);
129 
130  (void)pmemobj_tx_end();
131  }
132 
136  manual(const manual &p) = delete;
137 
141  manual(const manual &&p) = delete;
142 
146  manual &operator=(const manual &p) = delete;
147 
151  manual &operator=(manual &&p) = delete;
152  };
153 
154 /*
155  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
156  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
157  */
158 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
159 
178  class automatic {
179  public:
197  template <typename... L>
198  automatic(obj::pool_base &pop, L &... locks)
199  : tx_worker(pop, locks...)
200  {
201  }
202 
213  ~automatic() noexcept(false)
214  {
215  /* active exception, abort handled by tx_worker */
216  if (exceptions.new_uncaught_exception())
217  return;
218 
219  /* transaction ended normally */
220  if (pmemobj_tx_stage() == TX_STAGE_WORK)
221  pmemobj_tx_commit();
222  /* transaction aborted, throw an exception */
223  else if (pmemobj_tx_stage() == TX_STAGE_ONABORT ||
224  (pmemobj_tx_stage() == TX_STAGE_FINALLY &&
225  pmemobj_tx_errno() != 0))
227  "Transaction aborted");
228  }
229 
233  automatic(const automatic &p) = delete;
234 
238  automatic(const automatic &&p) = delete;
239 
243  automatic &operator=(const automatic &p) = delete;
244 
249 
250  private:
255  public:
263  : count(std::uncaught_exceptions())
264  {
265  }
266 
274  bool
276  {
277  return std::uncaught_exceptions() > this->count;
278  }
279 
280  private:
284  int count;
285  } exceptions;
286 
287  transaction::manual tx_worker;
288  };
289 #endif /* __cpp_lib_uncaught_exceptions */
290 
291  /*
292  * Deleted default constructor.
293  */
294  transaction() = delete;
295 
302  ~transaction() noexcept = delete;
303 
318  static void
319  abort(int err)
320  {
321  if (pmemobj_tx_stage() != TX_STAGE_WORK)
322  throw pmem::transaction_error("wrong stage for abort");
323 
324  pmemobj_tx_abort(err);
325  throw pmem::manual_tx_abort("explicit abort " +
326  std::to_string(err));
327  }
328 
339  static void
341  {
342  if (pmemobj_tx_stage() != TX_STAGE_WORK)
343  throw pmem::transaction_error("wrong stage for commit");
344 
345  pmemobj_tx_commit();
346  }
347 
348  static int
349  error() noexcept
350  {
351  return pmemobj_tx_errno();
352  }
353 
354  POBJ_CPP_DEPRECATED static int
355  get_last_tx_error() noexcept
356  {
357  return transaction::error();
358  }
359 
391  template <typename... Locks>
392  static void
393  run(pool_base &pool, std::function<void()> tx, Locks &... locks)
394  {
395  int ret = 0;
396 
397  if (pmemobj_tx_stage() == TX_STAGE_NONE) {
398  ret = pmemobj_tx_begin(pool.handle(), nullptr,
399  TX_PARAM_CB,
400  transaction::c_callback, nullptr,
401  TX_PARAM_NONE);
402  } else {
403  ret = pmemobj_tx_begin(pool.handle(), nullptr,
404  TX_PARAM_NONE);
405  }
406 
407  if (ret != 0)
409  "failed to start transaction")
410  .with_pmemobj_errormsg();
411 
412  auto err = add_lock(locks...);
413 
414  if (err) {
415  pmemobj_tx_abort(err);
416  (void)pmemobj_tx_end();
418  "failed to add a lock to the transaction")
419  .with_pmemobj_errormsg();
420  }
421 
422  try {
423  tx();
424  } catch (manual_tx_abort &) {
425  (void)pmemobj_tx_end();
426  throw;
427  } catch (...) {
428  /* first exception caught */
429  if (pmemobj_tx_stage() == TX_STAGE_WORK)
430  pmemobj_tx_abort(ECANCELED);
431 
432  /* waterfall tx_end for outer tx */
433  (void)pmemobj_tx_end();
434  throw;
435  }
436 
437  auto stage = pmemobj_tx_stage();
438 
439  if (stage == TX_STAGE_WORK) {
440  pmemobj_tx_commit();
441  } else if (stage == TX_STAGE_ONABORT) {
442  (void)pmemobj_tx_end();
443  throw pmem::transaction_error("transaction aborted");
444  } else if (stage == TX_STAGE_NONE) {
446  "transaction ended prematurely");
447  }
448 
449  (void)pmemobj_tx_end();
450  }
451 
452  template <typename... Locks>
453  POBJ_CPP_DEPRECATED static void
454  exec_tx(pool_base &pool, std::function<void()> tx, Locks &... locks)
455  {
456  transaction::run(pool, tx, locks...);
457  }
458 
479  template <
480  typename T,
481  typename std::enable_if<LIBPMEMOBJ_CPP_IS_TRIVIALLY_COPYABLE(T),
482  T>::type * = nullptr>
483  static void
484  snapshot(const T *addr, size_t num = 1)
485  {
486  if (TX_STAGE_WORK != pmemobj_tx_stage())
488  "wrong stage for taking a snapshot.");
489 
490  if (pmemobj_tx_add_range_direct(addr, sizeof(*addr) * num)) {
491  if (errno == ENOMEM)
493  "Could not take a snapshot of given memory range.")
494  .with_pmemobj_errormsg();
495  else
497  "Could not take a snapshot of given memory range.")
498  .with_pmemobj_errormsg();
499  }
500  }
501 
506  enum class stage {
507  work = TX_STAGE_WORK, /* transaction in progress */
508  oncommit = TX_STAGE_ONCOMMIT, /* successfully committed */
509  onabort = TX_STAGE_ONABORT, /* tx_begin failed or transaction
510  aborted */
511  finally = TX_STAGE_FINALLY, /* ready for cleanup */
512  };
513 
527  static void
528  register_callback(stage stg, std::function<void()> cb)
529  {
530  if (pmemobj_tx_stage() != TX_STAGE_WORK)
532  "register_callback must be called during a transaction");
533 
534  get_tx_data()->callbacks[static_cast<size_t>(stg)].push_back(
535  cb);
536  }
537 
538 private:
551  template <typename L, typename... Locks>
552  static int
553  add_lock(L &lock, Locks &... locks) noexcept
554  {
555  auto err =
556  pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
557 
558  if (err)
559  return err;
560 
561  return add_lock(locks...);
562  }
563 
567  static inline int
568  add_lock() noexcept
569  {
570  return 0;
571  }
572 
573  using callbacks_list_type = std::vector<std::function<void()>>;
574  using callbacks_map_type =
575  std::array<callbacks_list_type, MAX_TX_STAGE>;
576 
581  static void
582  c_callback(PMEMobjpool *pop, enum pobj_tx_stage obj_stage, void *arg)
583  {
584  /*
585  * We cannot do anything when in TX_STAGE_NONE because
586  * pmemobj_tx_get_user_data() can only be called when there is
587  * an active transaction.
588  */
589  if (obj_stage == TX_STAGE_NONE)
590  return;
591 
592  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
593  if (data == nullptr)
594  return;
595 
596  for (auto &cb : data->callbacks[obj_stage])
597  cb();
598 
599  /*
600  * Callback for TX_STAGE_FINALLY is called as the last one so we
601  * can free tx_data here
602  */
603  if (obj_stage == TX_STAGE_FINALLY) {
604  delete data;
605  pmemobj_tx_set_user_data(NULL);
606  }
607  }
608 
613  struct tx_data {
614  callbacks_map_type callbacks;
615  };
616 
621  static tx_data *
623  {
624  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
625  if (data == nullptr) {
626  data = new tx_data;
627  pmemobj_tx_set_user_data(data);
628  }
629 
630  return data;
631  }
632 };
633 
634 } /* namespace obj */
635 
636 } /* namespace pmem */
637 
638 #endif /* LIBPMEMOBJ_CPP_TRANSACTION_HPP */
pmem::obj::transaction::automatic::automatic
automatic(obj::pool_base &pop, L &... locks)
RAII constructor with pmem resident locks.
Definition: transaction.hpp:198
pmem::obj::transaction::automatic::uncaught_exception_counter::count
int count
The number of active exceptions.
Definition: transaction.hpp:284
pmem::obj::transaction::register_callback
static void register_callback(stage stg, std::function< void()> cb)
Registers callback to be called on specified stage for the transaction.
Definition: transaction.hpp:528
pmem::obj::transaction::commit
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:340
pmem::obj::transaction::manual::operator=
manual & operator=(manual &&p)=delete
Deleted move assignment operator.
pmem::transaction_error
Custom transaction error class.
Definition: pexceptions.hpp:65
pmem
Persistent memory namespace.
Definition: allocation_flag.hpp:15
pmem::transaction_out_of_memory
Custom out of memory error class.
Definition: pexceptions.hpp:125
pmem::obj::transaction::automatic::operator=
automatic & operator=(const automatic &p)=delete
Deleted assignment operator.
common.hpp
Commonly used functionality.
pmem::obj::transaction::add_lock
static int add_lock(L &lock, Locks &... locks) noexcept
Recursively add locks to the active transaction.
Definition: transaction.hpp:553
pmem::obj::transaction::manual::~manual
~manual() noexcept
Destructor.
Definition: transaction.hpp:124
pmem::obj::transaction::snapshot
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 pt...
Definition: transaction.hpp:484
pexceptions.hpp
Custom exceptions.
pmem::obj::p
Resides on pmem class.
Definition: p.hpp:35
pmem::obj::transaction::manual::manual
manual(obj::pool_base &pop, L &... locks)
RAII constructor with pmem resident locks.
Definition: transaction.hpp:87
pool.hpp
C++ pmemobj pool.
pmem::obj::transaction::stage
stage
Possible stages of a transaction, for every stage one or more callbacks can be registered.
Definition: transaction.hpp:506
pmem::obj::transaction::run
static void run(pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:393
pmem::obj::transaction::add_lock
static int add_lock() noexcept
Method ending the recursive algorithm.
Definition: transaction.hpp:568
pmem::obj::transaction::automatic::operator=
automatic & operator=(automatic &&p)=delete
Deleted move assignment operator.
pmem::obj::transaction::tx_data
This data is stored along with the pmemobj transaction data using pmemobj_tx_set_data().
Definition: transaction.hpp:613
pmem::obj::transaction::c_callback
static void c_callback(PMEMobjpool *pop, enum pobj_tx_stage obj_stage, void *arg)
C-style function which is passed as callback to pmemobj_begin.
Definition: transaction.hpp:582
pmem::obj::pool_base::handle
PMEMobjpool * handle() noexcept
Gets the C style handle to the pool.
Definition: pool.hpp:398
pmem::obj::transaction::automatic::automatic
automatic(const automatic &p)=delete
Deleted copy constructor.
pmem::obj::transaction::automatic
C++ automatic scope transaction class.
Definition: transaction.hpp:178
pmem::obj::transaction::automatic::uncaught_exception_counter
Internal class for counting active exceptions.
Definition: transaction.hpp:254
pmem::obj::transaction::automatic::uncaught_exception_counter::new_uncaught_exception
bool new_uncaught_exception()
Notifies is a new exception is being handled.
Definition: transaction.hpp:275
pmem::obj::transaction::automatic::uncaught_exception_counter::uncaught_exception_counter
uncaught_exception_counter()
Default constructor.
Definition: transaction.hpp:262
pmem::obj::pool
PMEMobj pool class.
Definition: pool.hpp:462
pmem::obj::transaction::manual::operator=
manual & operator=(const manual &p)=delete
Deleted assignment operator.
pmem::obj::transaction::~transaction
~transaction() noexcept=delete
Default destructor.
pmem::obj::transaction::automatic::~automatic
~automatic() noexcept(false)
Destructor.
Definition: transaction.hpp:213
pmem::obj::transaction::manual::manual
manual(const manual &p)=delete
Deleted copy constructor.
pmem::obj::transaction
C++ transaction handler class.
Definition: transaction.hpp:51
pmem::transaction_scope_error
Custom transaction error class.
Definition: pexceptions.hpp:163
pmem::manual_tx_abort
Custom transaction error class.
Definition: pexceptions.hpp:173
pmem::obj::pool_base
The non-template pool base class.
Definition: pool.hpp:46
pmem::obj::transaction::automatic::automatic
automatic(const automatic &&p)=delete
Deleted move constructor.
pmem::obj::transaction::abort
static void abort(int err)
Manually abort the current transaction.
Definition: transaction.hpp:319
pmem::obj::transaction::get_tx_data
static tx_data * get_tx_data()
Gets tx user data from pmemobj or creates it if this is a first call to this function inside a transa...
Definition: transaction.hpp:622
pmem::obj::transaction::manual
C++ manual scope transaction class.
Definition: transaction.hpp:71
pmem::obj::transaction::manual::manual
manual(const manual &&p)=delete
Deleted move constructor.