PMDK C++ bindings  1.11
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-2020, 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 detail
26 {
31 template <typename T>
33  static constexpr bool value = LIBPMEMOBJ_CPP_IS_TRIVIALLY_COPYABLE(T);
34 };
35 
36 } /* namespace detail */
37 
38 namespace obj
39 {
40 
64 class transaction {
65 public:
84  class manual {
85  public:
99  template <typename... L>
100  manual(obj::pool_base &pop, L &... locks)
101  {
102  int ret = 0;
103 
104  if (pmemobj_tx_stage() == TX_STAGE_NONE) {
105  ret = pmemobj_tx_begin(pop.handle(), nullptr,
106  TX_PARAM_CB,
108  nullptr, TX_PARAM_NONE);
109  } else {
110  ret = pmemobj_tx_begin(pop.handle(), nullptr,
111  TX_PARAM_NONE);
112  }
113 
114  if (ret != 0)
116  "failed to start transaction")
117  .with_pmemobj_errormsg();
118 
119  auto err = add_lock(locks...);
120 
121  if (err) {
122  pmemobj_tx_abort(EINVAL);
123  (void)pmemobj_tx_end();
125  "failed to add lock")
126  .with_pmemobj_errormsg();
127  }
128  }
129 
137  ~manual() noexcept
138  {
139  /* normal exit or with an active exception */
140  if (pmemobj_tx_stage() == TX_STAGE_WORK)
141  pmemobj_tx_abort(ECANCELED);
142 
143  (void)pmemobj_tx_end();
144  }
145 
149  manual(const manual &p) = delete;
150 
154  manual(const manual &&p) = delete;
155 
159  manual &operator=(const manual &p) = delete;
160 
164  manual &operator=(manual &&p) = delete;
165  };
166 
167 /*
168  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
169  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
170  */
171 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
172 
191  class automatic {
192  public:
210  template <typename... L>
211  automatic(obj::pool_base &pop, L &... locks)
212  : tx_worker(pop, locks...)
213  {
214  }
215 
226  ~automatic() noexcept(false)
227  {
228  /* active exception, abort handled by tx_worker */
229  if (exceptions.new_uncaught_exception())
230  return;
231 
232  /* transaction ended normally */
233  if (pmemobj_tx_stage() == TX_STAGE_WORK)
234  pmemobj_tx_commit();
235  /* transaction aborted, throw an exception */
236  else if (pmemobj_tx_stage() == TX_STAGE_ONABORT ||
237  (pmemobj_tx_stage() == TX_STAGE_FINALLY &&
238  pmemobj_tx_errno() != 0))
240  "Transaction aborted");
241  }
242 
246  automatic(const automatic &p) = delete;
247 
251  automatic(const automatic &&p) = delete;
252 
256  automatic &operator=(const automatic &p) = delete;
257 
262 
263  private:
268  public:
276  : count(std::uncaught_exceptions())
277  {
278  }
279 
287  bool
289  {
290  return std::uncaught_exceptions() > this->count;
291  }
292 
293  private:
297  int count;
298  } exceptions;
299 
300  transaction::manual tx_worker;
301  };
302 #endif /* __cpp_lib_uncaught_exceptions */
303 
304  /*
305  * Deleted default constructor.
306  */
307  transaction() = delete;
308 
315  ~transaction() noexcept = delete;
316 
331  static void
332  abort(int err)
333  {
334  if (pmemobj_tx_stage() != TX_STAGE_WORK)
335  throw pmem::transaction_error("wrong stage for abort");
336 
337  pmemobj_tx_abort(err);
338  throw pmem::manual_tx_abort("explicit abort " +
339  std::to_string(err));
340  }
341 
352  static void
354  {
355  if (pmemobj_tx_stage() != TX_STAGE_WORK)
356  throw pmem::transaction_error("wrong stage for commit");
357 
358  pmemobj_tx_commit();
359  }
360 
361  static int
362  error() noexcept
363  {
364  return pmemobj_tx_errno();
365  }
366 
367  POBJ_CPP_DEPRECATED static int
368  get_last_tx_error() noexcept
369  {
370  return transaction::error();
371  }
372 
404  template <typename... Locks>
405  static void
406  run(pool_base &pool, std::function<void()> tx, Locks &... locks)
407  {
408  int ret = 0;
409 
410  if (pmemobj_tx_stage() == TX_STAGE_NONE) {
411  ret = pmemobj_tx_begin(pool.handle(), nullptr,
412  TX_PARAM_CB,
413  transaction::c_callback, nullptr,
414  TX_PARAM_NONE);
415  } else {
416  ret = pmemobj_tx_begin(pool.handle(), nullptr,
417  TX_PARAM_NONE);
418  }
419 
420  if (ret != 0)
422  "failed to start transaction")
423  .with_pmemobj_errormsg();
424 
425  auto err = add_lock(locks...);
426 
427  if (err) {
428  pmemobj_tx_abort(err);
429  (void)pmemobj_tx_end();
431  "failed to add a lock to the transaction")
432  .with_pmemobj_errormsg();
433  }
434 
435  try {
436  tx();
437  } catch (manual_tx_abort &) {
438  (void)pmemobj_tx_end();
439  throw;
440  } catch (...) {
441  /* first exception caught */
442  if (pmemobj_tx_stage() == TX_STAGE_WORK)
443  pmemobj_tx_abort(ECANCELED);
444 
445  /* waterfall tx_end for outer tx */
446  (void)pmemobj_tx_end();
447  throw;
448  }
449 
450  auto stage = pmemobj_tx_stage();
451 
452  if (stage == TX_STAGE_WORK) {
453  pmemobj_tx_commit();
454  } else if (stage == TX_STAGE_ONABORT) {
455  (void)pmemobj_tx_end();
456  throw pmem::transaction_error("transaction aborted");
457  } else if (stage == TX_STAGE_NONE) {
459  "transaction ended prematurely");
460  }
461 
462  (void)pmemobj_tx_end();
463  }
464 
465  template <typename... Locks>
466  POBJ_CPP_DEPRECATED static void
467  exec_tx(pool_base &pool, std::function<void()> tx, Locks &... locks)
468  {
469  transaction::run(pool, tx, locks...);
470  }
471 
492  template <typename T,
493  typename std::enable_if<detail::can_do_snapshot<T>::value,
494  T>::type * = nullptr>
495  static void
496  snapshot(const T *addr, size_t num = 1)
497  {
498  if (TX_STAGE_WORK != pmemobj_tx_stage())
500  "wrong stage for taking a snapshot.");
501 
502  if (pmemobj_tx_add_range_direct(addr, sizeof(*addr) * num)) {
503  if (errno == ENOMEM)
505  "Could not take a snapshot of given memory range.")
506  .with_pmemobj_errormsg();
507  else
509  "Could not take a snapshot of given memory range.")
510  .with_pmemobj_errormsg();
511  }
512  }
513 
518  enum class stage {
519  work = TX_STAGE_WORK, /* transaction in progress */
520  oncommit = TX_STAGE_ONCOMMIT, /* successfully committed */
521  onabort = TX_STAGE_ONABORT, /* tx_begin failed or transaction
522  aborted */
523  finally = TX_STAGE_FINALLY, /* ready for cleanup */
524  };
525 
539  static void
540  register_callback(stage stg, std::function<void()> cb)
541  {
542  if (pmemobj_tx_stage() != TX_STAGE_WORK)
544  "register_callback must be called during a transaction");
545 
546  get_tx_data()->callbacks[static_cast<size_t>(stg)].push_back(
547  cb);
548  }
549 
550 private:
563  template <typename L, typename... Locks>
564  static int
565  add_lock(L &lock, Locks &... locks) noexcept
566  {
567  auto err =
568  pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
569 
570  if (err)
571  return err;
572 
573  return add_lock(locks...);
574  }
575 
579  static inline int
580  add_lock() noexcept
581  {
582  return 0;
583  }
584 
585  using callbacks_list_type = std::vector<std::function<void()>>;
586  using callbacks_map_type =
587  std::array<callbacks_list_type, MAX_TX_STAGE>;
588 
593  static void
594  c_callback(PMEMobjpool *pop, enum pobj_tx_stage obj_stage, void *arg)
595  {
596  /*
597  * We cannot do anything when in TX_STAGE_NONE because
598  * pmemobj_tx_get_user_data() can only be called when there is
599  * an active transaction.
600  */
601  if (obj_stage == TX_STAGE_NONE)
602  return;
603 
604  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
605  if (data == nullptr)
606  return;
607 
608  for (auto &cb : data->callbacks[obj_stage])
609  cb();
610 
611  /*
612  * Callback for TX_STAGE_FINALLY is called as the last one so we
613  * can free tx_data here
614  */
615  if (obj_stage == TX_STAGE_FINALLY) {
616  delete data;
617  pmemobj_tx_set_user_data(NULL);
618  }
619  }
620 
625  struct tx_data {
626  callbacks_map_type callbacks;
627  };
628 
633  static tx_data *
635  {
636  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
637  if (data == nullptr) {
638  data = new tx_data;
639  pmemobj_tx_set_user_data(data);
640  }
641 
642  return data;
643  }
644 };
645 
646 } /* namespace obj */
647 
648 } /* namespace pmem */
649 
650 #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:211
pmem::obj::transaction::automatic::uncaught_exception_counter::count
int count
The number of active exceptions.
Definition: transaction.hpp:297
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:540
pmem::obj::transaction::commit
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:353
pmem::obj::transaction::manual::operator=
manual & operator=(manual &&p)=delete
Deleted move assignment operator.
pmem::transaction_error
Custom transaction error class.
Definition: pexceptions.hpp:63
pmem
Persistent memory namespace.
Definition: allocation_flag.hpp:15
pmem::transaction_out_of_memory
Custom out of memory error class.
Definition: pexceptions.hpp:120
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:565
pmem::obj::transaction::manual::~manual
~manual() noexcept
Destructor.
Definition: transaction.hpp:137
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:496
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:100
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:518
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:406
pmem::detail::can_do_snapshot
A structure that checks if it is possible to snapshot the specified memory.
Definition: transaction.hpp:32
pmem::obj::transaction::add_lock
static int add_lock() noexcept
Method ending the recursive algorithm.
Definition: transaction.hpp:580
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:625
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:594
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:191
pmem::obj::transaction::automatic::uncaught_exception_counter
Internal class for counting active exceptions.
Definition: transaction.hpp:267
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:288
pmem::obj::transaction::automatic::uncaught_exception_counter::uncaught_exception_counter
uncaught_exception_counter()
Default constructor.
Definition: transaction.hpp:275
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:226
pmem::obj::transaction::manual::manual
manual(const manual &p)=delete
Deleted copy constructor.
pmem::obj::transaction
C++ transaction handler class.
Definition: transaction.hpp:64
pmem::transaction_scope_error
Custom transaction error class.
Definition: pexceptions.hpp:158
pmem::manual_tx_abort
Custom transaction error class.
Definition: pexceptions.hpp:168
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:332
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:634
pmem::obj::transaction::manual
C++ manual scope transaction class.
Definition: transaction.hpp:84
pmem::obj::transaction::manual::manual
manual(const manual &&p)=delete
Deleted move constructor.