PMDK C++ bindings  1.13.0-git23.gf49772ac
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 
456  enum class stage {
457  work = TX_STAGE_WORK,
458  oncommit = TX_STAGE_ONCOMMIT,
459  onabort = TX_STAGE_ONABORT,
461  finally = TX_STAGE_FINALLY,
462  };
463 
477  static void
478  register_callback(stage stg, std::function<void()> cb)
479  {
480  if (pmemobj_tx_stage() != TX_STAGE_WORK)
482  "register_callback must be called during a transaction");
483 
484  get_tx_data()->callbacks[static_cast<size_t>(stg)].push_back(
485  cb);
486  }
487 
488 private:
501  template <typename L, typename... Locks>
502  static int
503  add_lock(L &lock, Locks &... locks) noexcept
504  {
505  auto err =
506  pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
507 
508  if (err)
509  return err;
510 
511  return add_lock(locks...);
512  }
513 
517  static inline int
518  add_lock() noexcept
519  {
520  return 0;
521  }
522 
523  using callbacks_list_type = std::vector<std::function<void()>>;
524  using callbacks_map_type =
525  std::array<callbacks_list_type, MAX_TX_STAGE>;
526 
531  static void
532  c_callback(PMEMobjpool *pop, enum pobj_tx_stage obj_stage, void *arg)
533  {
534  /*
535  * We cannot do anything when in TX_STAGE_NONE because
536  * pmemobj_tx_get_user_data() can only be called when there is
537  * an active transaction.
538  */
539  if (obj_stage == TX_STAGE_NONE)
540  return;
541 
542  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
543  if (data == nullptr)
544  return;
545 
546  for (auto &cb : data->callbacks[obj_stage])
547  cb();
548 
549  /*
550  * Callback for TX_STAGE_FINALLY is called as the last one so we
551  * can free tx_data here
552  */
553  if (obj_stage == TX_STAGE_FINALLY) {
554  delete data;
555  pmemobj_tx_set_user_data(NULL);
556  }
557  }
558 
563  struct tx_data {
564  callbacks_map_type callbacks;
565  };
566 
571  static tx_data *
573  {
574  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
575  if (data == nullptr) {
576  data = new tx_data;
577  pmemobj_tx_set_user_data(data);
578  }
579 
580  return data;
581  }
582 };
583 
584 } /* namespace detail */
585 
586 namespace obj
587 {
588 
614 public:
634 
635 /*
636  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
637  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
638  */
639 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
660 #endif /* __cpp_lib_uncaught_exceptions */
692  template <typename... Locks>
693  static void
694  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
695  {
697  }
698 
699  /*
700  * Deleted default constructor.
701  */
702  basic_transaction() = delete;
703 
707  ~basic_transaction() noexcept = delete;
708 };
709 
741 class flat_transaction : public detail::transaction_base<true> {
742 public:
763 
764 /*
765  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
766  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
767  */
768 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
789 #endif /* __cpp_lib_uncaught_exceptions */
821  template <typename... Locks>
822  static void
823  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
824  {
826  }
827 
828  /*
829  * Deleted default constructor.
830  */
831  flat_transaction() = delete;
832 
836  ~flat_transaction() noexcept = delete;
837 };
838 
839 #ifdef LIBPMEMOBJ_CPP_USE_FLAT_TRANSACTION
854 #else
869 #endif
870 
871 } /* namespace obj */
872 
873 } /* namespace pmem */
874 
875 #endif /* LIBPMEMOBJ_CPP_TRANSACTION_HPP */
Internal class for counting active exceptions.
Definition: transaction.hpp:239
bool new_uncaught_exception()
Notifies is a new exception is being handled.
Definition: transaction.hpp:260
uncaught_exception_counter()
Default constructor.
Definition: transaction.hpp:247
int count
The number of active exceptions.
Definition: transaction.hpp:269
Common functionality for basic_transaction and flat_transaction.
Definition: transaction.hpp:44
static int add_lock(L &lock, Locks &... locks) noexcept
Recursively add locks to the active transaction.
Definition: transaction.hpp:503
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:325
stage
Possible stages of a transaction.
Definition: transaction.hpp:456
@ oncommit
successfully committed
@ onabort
tx_begin failed or transaction aborted
@ work
transaction in progress
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:572
~transaction_base() noexcept=delete
Default destructor.
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
static int add_lock() noexcept
Method ending the recursive algorithm.
Definition: transaction.hpp:518
static void register_callback(stage stg, std::function< void()> cb)
Registers callback to be called on specified stage for the transaction.
Definition: transaction.hpp:478
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
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:532
static void abort(int err)
Manually abort the current transaction.
Definition: transaction.hpp:304
Custom transaction error class.
Definition: pexceptions.hpp:186
C++ transaction handler class.
Definition: transaction.hpp:613
~basic_transaction() noexcept=delete
Default destructor.
typename detail::transaction_base< false >::automatic automatic
C++ automatic scope transaction class.
Definition: transaction.hpp:659
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:694
typename detail::transaction_base< false >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:633
C++ flat transaction handler class.
Definition: transaction.hpp:741
~flat_transaction() noexcept=delete
Default destructor.
typename detail::transaction_base< true >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:762
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:823
typename detail::transaction_base< true >::automatic automatic
C++ automatic scope transaction class.
Definition: transaction.hpp:788
The non-template pool base class.
Definition: pool.hpp:50
PMEMobjpool * handle() noexcept
Gets the C style handle to the pool.
Definition: pool.hpp:394
PMEMobj pool class.
Definition: pool.hpp:482
Custom transaction error class.
Definition: pexceptions.hpp:81
Custom out of memory error class.
Definition: pexceptions.hpp:138
Custom transaction error class.
Definition: pexceptions.hpp:176
Commonly used functionality.
Persistent memory namespace.
Definition: allocation_flag.hpp:15
Custom exceptions.
C++ pmemobj pool.
A structure that checks if it is possible to snapshot the specified memory.
Definition: transaction.hpp:36
This data is stored along with the pmemobj transaction data using pmemobj_tx_set_data().
Definition: transaction.hpp:563