PMDK C++ bindings  1.12.1-rc1
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-2021, 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 #ifndef LIBPMEMOBJ_CPP_FLAT_TX_USE_FAILURE_RETURN
23 #define LIBPMEMOBJ_CPP_FLAT_TX_USE_FAILURE_RETURN true
24 #endif
25 
26 namespace pmem
27 {
28 
29 namespace detail
30 {
35 template <typename T>
37  static constexpr bool value = LIBPMEMOBJ_CPP_IS_TRIVIALLY_COPYABLE(T);
38 };
39 
43 template <bool is_flat>
45 public:
46  class manual {
47  public:
61  template <typename... L>
62  manual(obj::pool_base &pop, L &... locks)
63  {
64  int ret = 0;
65 
66  nested = pmemobj_tx_stage() == TX_STAGE_WORK;
67 
68  if (nested) {
69  ret = pmemobj_tx_begin(pop.handle(), nullptr,
70  TX_PARAM_NONE);
71  } else if (pmemobj_tx_stage() == TX_STAGE_NONE) {
72  ret = pmemobj_tx_begin(
73  pop.handle(), nullptr, TX_PARAM_CB,
75  TX_PARAM_NONE);
76  } else {
78  "Cannot start transaction in stage different than WORK or NONE");
79  }
80 
81  if (ret != 0)
83  "failed to start transaction")
84  .with_pmemobj_errormsg();
85 
86  auto err = add_lock(locks...);
87 
88  if (err) {
89  pmemobj_tx_abort(EINVAL);
90  (void)pmemobj_tx_end();
92  "failed to add lock")
93  .with_pmemobj_errormsg();
94  }
95 
96  set_failure_behavior();
97  }
98 
106  ~manual() noexcept
107  {
108  /* normal exit or with an active exception */
109  if (pmemobj_tx_stage() == TX_STAGE_WORK) {
110  if (is_flat && nested)
111  pmemobj_tx_commit();
112  else
113  pmemobj_tx_abort(ECANCELED);
114  }
115 
116  (void)pmemobj_tx_end();
117  }
118 
122  manual(const manual &p) = delete;
123 
127  manual(const manual &&p) = delete;
128 
132  manual &operator=(const manual &p) = delete;
133 
137  manual &operator=(manual &&p) = delete;
138 
139  private:
140  template <bool cnd = is_flat
141  &&LIBPMEMOBJ_CPP_FLAT_TX_USE_FAILURE_RETURN>
142  typename std::enable_if<cnd>::type
143  set_failure_behavior()
144  {
145  pmemobj_tx_set_failure_behavior(POBJ_TX_FAILURE_RETURN);
146  }
147 
148  template <bool cnd = is_flat
149  &&LIBPMEMOBJ_CPP_FLAT_TX_USE_FAILURE_RETURN>
150  typename std::enable_if<!cnd>::type
151  set_failure_behavior()
152  {
153  }
154 
155  bool nested = false;
156  };
157 
158 /*
159  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
160  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
161  */
162 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
163  class automatic {
164  public:
182  template <typename... L>
183  automatic(obj::pool_base &pop, L &... locks)
184  : tx_worker(pop, locks...)
185  {
186  }
187 
198  ~automatic() noexcept(false)
199  {
200  /* active exception, abort handled by tx_worker */
201  if (exceptions.new_uncaught_exception())
202  return;
203 
204  /* transaction ended normally */
205  if (pmemobj_tx_stage() == TX_STAGE_WORK)
206  pmemobj_tx_commit();
207  /* transaction aborted, throw an exception */
208  else if (pmemobj_tx_stage() == TX_STAGE_ONABORT ||
209  (pmemobj_tx_stage() == TX_STAGE_FINALLY &&
210  pmemobj_tx_errno() != 0))
212  "Transaction aborted");
213  }
214 
218  automatic(const automatic &p) = delete;
219 
223  automatic(const automatic &&p) = delete;
224 
228  automatic &operator=(const automatic &p) = delete;
229 
233  automatic &operator=(automatic &&p) = delete;
234 
235  private:
240  public:
248  : count(std::uncaught_exceptions())
249  {
250  }
251 
259  bool
261  {
262  return std::uncaught_exceptions() > this->count;
263  }
264 
265  private:
269  int count;
270  } exceptions;
271 
272  manual tx_worker;
273  };
274 #endif /* __cpp_lib_uncaught_exceptions */
275 
276  /*
277  * Deleted default constructor.
278  */
279  transaction_base() = delete;
280 
287  ~transaction_base() noexcept = delete;
288 
303  static void
304  abort(int err)
305  {
306  if (pmemobj_tx_stage() != TX_STAGE_WORK)
307  throw pmem::transaction_error("wrong stage for abort");
308 
309  pmemobj_tx_abort(err);
310  throw pmem::manual_tx_abort("explicit abort " +
311  std::to_string(err));
312  }
313 
324  static void
326  {
327  if (pmemobj_tx_stage() != TX_STAGE_WORK)
328  throw pmem::transaction_error("wrong stage for commit");
329 
330  pmemobj_tx_commit();
331  }
332 
333  static int
334  error() noexcept
335  {
336  return pmemobj_tx_errno();
337  }
338 
339  POBJ_CPP_DEPRECATED static int
340  get_last_tx_error() noexcept
341  {
342  return error();
343  }
344 
376  template <typename... Locks>
377  static void
378  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
379  {
380  manual worker(pool, locks...);
381 
382  tx();
383 
384  auto stage = pmemobj_tx_stage();
385 
386  if (stage == TX_STAGE_WORK) {
387  pmemobj_tx_commit();
388  } else if (stage == TX_STAGE_ONABORT) {
389  throw pmem::transaction_error("transaction aborted");
390  } else if (stage == TX_STAGE_NONE) {
392  "transaction ended prematurely");
393  }
394  }
395 
396  template <typename... Locks>
397  POBJ_CPP_DEPRECATED static void
398  exec_tx(obj::pool_base &pool, std::function<void()> tx,
399  Locks &... locks)
400  {
401  run(pool, tx, locks...);
402  }
403 
424  template <typename T,
425  typename std::enable_if<detail::can_do_snapshot<T>::value,
426  T>::type * = nullptr>
427  static void
428  snapshot(const T *addr, size_t num = 1)
429  {
430  if (TX_STAGE_WORK != pmemobj_tx_stage())
432  "wrong stage for taking a snapshot.");
433 
434  if (pmemobj_tx_add_range_direct(addr, sizeof(*addr) * num)) {
435  if (errno == ENOMEM)
437  "Could not take a snapshot of given memory range.")
438  .with_pmemobj_errormsg();
439  else
441  "Could not take a snapshot of given memory range.")
442  .with_pmemobj_errormsg();
443  }
444  }
445 
450  enum class stage {
451  work = TX_STAGE_WORK, /* transaction in progress */
452  oncommit = TX_STAGE_ONCOMMIT, /* successfully committed */
453  onabort = TX_STAGE_ONABORT, /* tx_begin failed or transaction
454  aborted */
455  finally = TX_STAGE_FINALLY, /* ready for cleanup */
456  };
457 
471  static void
472  register_callback(stage stg, std::function<void()> cb)
473  {
474  if (pmemobj_tx_stage() != TX_STAGE_WORK)
476  "register_callback must be called during a transaction");
477 
478  get_tx_data()->callbacks[static_cast<size_t>(stg)].push_back(
479  cb);
480  }
481 
482 private:
495  template <typename L, typename... Locks>
496  static int
497  add_lock(L &lock, Locks &... locks) noexcept
498  {
499  auto err =
500  pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
501 
502  if (err)
503  return err;
504 
505  return add_lock(locks...);
506  }
507 
511  static inline int
512  add_lock() noexcept
513  {
514  return 0;
515  }
516 
517  using callbacks_list_type = std::vector<std::function<void()>>;
518  using callbacks_map_type =
519  std::array<callbacks_list_type, MAX_TX_STAGE>;
520 
525  static void
526  c_callback(PMEMobjpool *pop, enum pobj_tx_stage obj_stage, void *arg)
527  {
528  /*
529  * We cannot do anything when in TX_STAGE_NONE because
530  * pmemobj_tx_get_user_data() can only be called when there is
531  * an active transaction.
532  */
533  if (obj_stage == TX_STAGE_NONE)
534  return;
535 
536  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
537  if (data == nullptr)
538  return;
539 
540  for (auto &cb : data->callbacks[obj_stage])
541  cb();
542 
543  /*
544  * Callback for TX_STAGE_FINALLY is called as the last one so we
545  * can free tx_data here
546  */
547  if (obj_stage == TX_STAGE_FINALLY) {
548  delete data;
549  pmemobj_tx_set_user_data(NULL);
550  }
551  }
552 
557  struct tx_data {
558  callbacks_map_type callbacks;
559  };
560 
565  static tx_data *
567  {
568  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
569  if (data == nullptr) {
570  data = new tx_data;
571  pmemobj_tx_set_user_data(data);
572  }
573 
574  return data;
575  }
576 };
577 
578 } /* namespace detail */
579 
580 namespace obj
581 {
582 
608 public:
628 
629 /*
630  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
631  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
632  */
633 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
634 
654 #endif /* __cpp_lib_uncaught_exceptions */
655 
686  template <typename... Locks>
687  static void
688  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
689  {
691  }
692 
693  /*
694  * Deleted default constructor.
695  */
696  basic_transaction() = delete;
697 
701  ~basic_transaction() noexcept = delete;
702 };
703 
735 class flat_transaction : public detail::transaction_base<true> {
736 public:
757 
758 /*
759  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
760  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
761  */
762 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
763 
783 #endif /* __cpp_lib_uncaught_exceptions */
784 
815  template <typename... Locks>
816  static void
817  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
818  {
820  }
821 
822  /*
823  * Deleted default constructor.
824  */
825  flat_transaction() = delete;
826 
830  ~flat_transaction() noexcept = delete;
831 };
832 
833 #ifdef LIBPMEMOBJ_CPP_USE_FLAT_TRANSACTION
834 
848 #else
849 
863 #endif
864 
865 } /* namespace obj */
866 
867 } /* namespace pmem */
868 
869 #endif /* LIBPMEMOBJ_CPP_TRANSACTION_HPP */
pmem::transaction_error
Custom transaction error class.
Definition: pexceptions.hpp:65
pmem
Persistent memory namespace.
Definition: allocation_flag.hpp:15
pmem::detail::transaction_base::automatic::uncaught_exception_counter::count
int count
The number of active exceptions.
Definition: transaction.hpp:269
pmem::transaction_out_of_memory
Custom out of memory error class.
Definition: pexceptions.hpp:125
pmem::detail::transaction_base::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:472
pmem::detail::transaction_base::run
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:378
common.hpp
Commonly used functionality.
pmem::detail::transaction_base::add_lock
static int add_lock() noexcept
Method ending the recursive algorithm.
Definition: transaction.hpp:512
pmem::detail::transaction_base::abort
static void abort(int err)
Manually abort the current transaction.
Definition: transaction.hpp:304
pexceptions.hpp
Custom exceptions.
pmem::obj::flat_transaction::automatic
typename detail::transaction_base< true >::automatic automatic
C++ automatic scope transaction class.
Definition: transaction.hpp:782
pmem::obj::basic_transaction::run
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:688
pmem::detail::transaction_base::tx_data
This data is stored along with the pmemobj transaction data using pmemobj_tx_set_data().
Definition: transaction.hpp:557
pmem::detail::transaction_base
Common functionality for basic_transaction and flat_transaction.
Definition: transaction.hpp:44
pmem::detail::transaction_base::automatic::uncaught_exception_counter::new_uncaught_exception
bool new_uncaught_exception()
Notifies is a new exception is being handled.
Definition: transaction.hpp:260
pmem::detail::transaction_base::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:526
pool.hpp
C++ pmemobj pool.
pmem::detail::transaction_base::~transaction_base
~transaction_base() noexcept=delete
Default destructor.
pmem::detail::transaction_base< true >::stage
stage
Possible stages of a transaction, for every stage one or more callbacks can be registered.
Definition: transaction.hpp:450
pmem::detail::can_do_snapshot
A structure that checks if it is possible to snapshot the specified memory.
Definition: transaction.hpp:36
pmem::obj::basic_transaction
C++ transaction handler class.
Definition: transaction.hpp:607
pmem::detail::transaction_base::automatic::uncaught_exception_counter
Internal class for counting active exceptions.
Definition: transaction.hpp:239
pmem::detail::transaction_base::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:566
pmem::obj::pool_base::handle
PMEMobjpool * handle() noexcept
Gets the C style handle to the pool.
Definition: pool.hpp:398
pmem::detail::transaction_base::automatic::uncaught_exception_counter::uncaught_exception_counter
uncaught_exception_counter()
Default constructor.
Definition: transaction.hpp:247
pmem::detail::transaction_base::commit
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:325
pmem::obj::pool
PMEMobj pool class.
Definition: pool.hpp:465
pmem::obj::basic_transaction::~basic_transaction
~basic_transaction() noexcept=delete
Default destructor.
pmem::obj::basic_transaction::manual
typename detail::transaction_base< false >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:627
pmem::detail::transaction_base::add_lock
static int add_lock(L &lock, Locks &... locks) noexcept
Recursively add locks to the active transaction.
Definition: transaction.hpp:497
pmem::obj::flat_transaction
C++ flat transaction handler class.
Definition: transaction.hpp:735
pmem::obj::basic_transaction::automatic
typename detail::transaction_base< false >::automatic automatic
C++ automatic scope transaction class.
Definition: transaction.hpp:653
pmem::transaction_scope_error
Custom transaction error class.
Definition: pexceptions.hpp:163
pmem::obj::flat_transaction::~flat_transaction
~flat_transaction() noexcept=delete
Default destructor.
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::flat_transaction::manual
typename detail::transaction_base< true >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:756
pmem::obj::flat_transaction::run
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:817
pmem::detail::transaction_base::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:428