PMDK C++ bindings  1.13.0-git107.g7e59f08f
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 
27 #ifndef LIBPMEMOBJ_CPP_FLAT_TX_USE_FAILURE_RETURN
28 #define LIBPMEMOBJ_CPP_FLAT_TX_USE_FAILURE_RETURN true
29 #endif
30 
31 namespace pmem
32 {
33 
34 namespace detail
35 {
40 template <typename T>
42  static constexpr bool value = LIBPMEMOBJ_CPP_IS_TRIVIALLY_COPYABLE(T);
43 };
44 
48 template <bool is_flat>
50 public:
51  class manual {
52  public:
66  template <typename... L>
67  manual(obj::pool_base &pop, L &... locks)
68  {
69  int ret = 0;
70 
71  nested = pmemobj_tx_stage() == TX_STAGE_WORK;
72 
73  if (nested) {
74  ret = pmemobj_tx_begin(pop.handle(), nullptr,
75  TX_PARAM_NONE);
76  } else if (pmemobj_tx_stage() == TX_STAGE_NONE) {
77  ret = pmemobj_tx_begin(
78  pop.handle(), nullptr, TX_PARAM_CB,
79  transaction_base::c_callback, nullptr,
80  TX_PARAM_NONE);
81  } else {
83  "Cannot start transaction in stage different than WORK or NONE");
84  }
85 
86  if (ret != 0)
89  "failed to start transaction");
90 
91  auto err = add_lock(locks...);
92 
93  if (err) {
94  pmemobj_tx_abort(EINVAL);
95  (void)pmemobj_tx_end();
98  "failed to add lock");
99  }
100 
101  set_failure_behavior();
102  }
103 
111  ~manual() noexcept
112  {
113  /* normal exit or with an active exception */
114  if (pmemobj_tx_stage() == TX_STAGE_WORK) {
115  if (is_flat && nested)
116  pmemobj_tx_commit();
117  else
118  pmemobj_tx_abort(ECANCELED);
119  }
120 
121  (void)pmemobj_tx_end();
122  }
123 
127  manual(const manual &p) = delete;
128 
132  manual(const manual &&p) = delete;
133 
137  manual &operator=(const manual &p) = delete;
138 
142  manual &operator=(manual &&p) = delete;
143 
144  private:
145  template <bool cnd = is_flat
147  typename std::enable_if<cnd>::type
148  set_failure_behavior()
149  {
150  pmemobj_tx_set_failure_behavior(POBJ_TX_FAILURE_RETURN);
151  }
152 
153  template <bool cnd = is_flat
155  typename std::enable_if<!cnd>::type
156  set_failure_behavior()
157  {
158  }
159 
160  bool nested = false;
161  };
162 
163 /*
164  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
165  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
166  */
167 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
168  class automatic {
169  public:
187  template <typename... L>
188  automatic(obj::pool_base &pop, L &... locks)
189  : tx_worker(pop, locks...)
190  {
191  }
192 
203  ~automatic() noexcept(false)
204  {
205  /* active exception, abort handled by tx_worker */
206  if (exceptions.new_uncaught_exception())
207  return;
208 
209  /* transaction ended normally */
210  if (pmemobj_tx_stage() == TX_STAGE_WORK)
211  pmemobj_tx_commit();
212  /* transaction aborted, throw an exception */
213  else if (pmemobj_tx_stage() == TX_STAGE_ONABORT ||
214  (pmemobj_tx_stage() == TX_STAGE_FINALLY &&
215  pmemobj_tx_errno() != 0))
217  "Transaction aborted");
218  }
219 
223  automatic(const automatic &p) = delete;
224 
228  automatic(const automatic &&p) = delete;
229 
233  automatic &operator=(const automatic &p) = delete;
234 
238  automatic &operator=(automatic &&p) = delete;
239 
240  private:
244  class uncaught_exception_counter {
245  public:
252  uncaught_exception_counter()
253  : count(std::uncaught_exceptions())
254  {
255  }
256 
264  bool
265  new_uncaught_exception()
266  {
267  return std::uncaught_exceptions() > this->count;
268  }
269 
270  private:
274  int count;
275  } exceptions;
276 
277  manual tx_worker;
278  };
279 #endif /* __cpp_lib_uncaught_exceptions */
280 
281  /*
282  * Deleted default constructor.
283  */
284  transaction_base() = delete;
285 
292  ~transaction_base() noexcept = delete;
293 
308  static void
309  abort(int err)
310  {
311  if (pmemobj_tx_stage() != TX_STAGE_WORK)
312  throw pmem::transaction_error("wrong stage for abort");
313 
314  pmemobj_tx_abort(err);
315  throw pmem::manual_tx_abort("explicit abort " +
316  std::to_string(err));
317  }
318 
329  static void
331  {
332  if (pmemobj_tx_stage() != TX_STAGE_WORK)
333  throw pmem::transaction_error("wrong stage for commit");
334 
335  pmemobj_tx_commit();
336  }
337 
338  static int
339  error() noexcept
340  {
341  return pmemobj_tx_errno();
342  }
343 
344  POBJ_CPP_DEPRECATED static int
345  get_last_tx_error() noexcept
346  {
347  return error();
348  }
349 
385  template <typename... Locks>
386  static void
387  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
388  {
389  manual worker(pool, locks...);
390 
391  tx();
392 
393  auto stage = pmemobj_tx_stage();
394 
395  if (stage == TX_STAGE_WORK) {
396  pmemobj_tx_commit();
397  } else if (stage == TX_STAGE_ONABORT) {
398  throw pmem::transaction_error("transaction aborted");
399  } else if (stage == TX_STAGE_NONE) {
401  "transaction ended prematurely");
402  }
403  }
404 
405  template <typename... Locks>
406  POBJ_CPP_DEPRECATED static void
407  exec_tx(obj::pool_base &pool, std::function<void()> tx,
408  Locks &... locks)
409  {
410  run(pool, tx, locks...);
411  }
412 
433  template <typename T,
434  typename std::enable_if<detail::can_do_snapshot<T>::value,
435  T>::type * = nullptr>
436  static void
437  snapshot(const T *addr, size_t num = 1)
438  {
439  if (TX_STAGE_WORK != pmemobj_tx_stage())
441  "wrong stage for taking a snapshot.");
442 
443  if (pmemobj_tx_add_range_direct(addr, sizeof(*addr) * num)) {
444  const char *msg =
445  "Could not take a snapshot of given memory range.";
446  if (errno == ENOMEM)
449  else
452  }
453  }
454 
465  enum class stage {
466  work = TX_STAGE_WORK,
467  oncommit = TX_STAGE_ONCOMMIT,
468  onabort = TX_STAGE_ONABORT,
470  finally = TX_STAGE_FINALLY,
471  };
472 
486  static void
487  register_callback(stage stg, std::function<void()> cb)
488  {
489  if (pmemobj_tx_stage() != TX_STAGE_WORK)
491  "register_callback must be called during a transaction");
492 
493  get_tx_data()->callbacks[static_cast<size_t>(stg)].push_back(
494  cb);
495  }
496 
497 private:
510  template <typename L, typename... Locks>
511  static int
512  add_lock(L &lock, Locks &... locks) noexcept
513  {
514  auto err =
515  pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
516 
517  if (err)
518  return err;
519 
520  return add_lock(locks...);
521  }
522 
526  static inline int
527  add_lock() noexcept
528  {
529  return 0;
530  }
531 
532  using callbacks_list_type = std::vector<std::function<void()>>;
533  using callbacks_map_type =
534  std::array<callbacks_list_type, MAX_TX_STAGE>;
535 
540  static void
541  c_callback(PMEMobjpool *pop, enum pobj_tx_stage obj_stage, void *arg)
542  {
543  /*
544  * We cannot do anything when in TX_STAGE_NONE because
545  * pmemobj_tx_get_user_data() can only be called when there is
546  * an active transaction.
547  */
548  if (obj_stage == TX_STAGE_NONE)
549  return;
550 
551  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
552  if (data == nullptr)
553  return;
554 
555  for (auto &cb : data->callbacks[obj_stage])
556  cb();
557 
558  /*
559  * Callback for TX_STAGE_FINALLY is called as the last one so we
560  * can free tx_data here
561  */
562  if (obj_stage == TX_STAGE_FINALLY) {
563  delete data;
564  pmemobj_tx_set_user_data(NULL);
565  }
566  }
567 
572  struct tx_data {
573  callbacks_map_type callbacks;
574  };
575 
580  static tx_data *
581  get_tx_data()
582  {
583  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
584  if (data == nullptr) {
585  data = new tx_data;
586  pmemobj_tx_set_user_data(data);
587  }
588 
589  return data;
590  }
591 };
592 
593 } /* namespace detail */
594 
595 namespace obj
596 {
597 
624 public:
644 
645 /*
646  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
647  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
648  */
649 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
670 #endif /* __cpp_lib_uncaught_exceptions */
674  template <typename... Locks>
675  static void
676  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
677  {
679  }
680 
681  /*
682  * Deleted default constructor.
683  */
684  basic_transaction() = delete;
685 
689  ~basic_transaction() noexcept = delete;
690 };
691 
724 class flat_transaction : public detail::transaction_base<true> {
725 public:
746 
747 /*
748  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
749  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
750  */
751 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
772 #endif /* __cpp_lib_uncaught_exceptions */
808  template <typename... Locks>
809  static void
810  run(obj::pool_base &pool, std::function<void()> tx, Locks &... locks)
811  {
813  }
814 
815  /*
816  * Deleted default constructor.
817  */
818  flat_transaction() = delete;
819 
823  ~flat_transaction() noexcept = delete;
824 };
825 
826 #ifdef LIBPMEMOBJ_CPP_USE_FLAT_TRANSACTION
841 #else
856 #endif
857 
858 } /* namespace obj */
859 
860 } /* namespace pmem */
861 
862 #endif /* LIBPMEMOBJ_CPP_TRANSACTION_HPP */
Common functionality for basic_transaction and flat_transaction.
Definition: transaction.hpp:49
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:330
stage
Possible stages of a transaction.
Definition: transaction.hpp:465
@ oncommit
successfully committed
@ onabort
tx_begin failed or transaction aborted
@ work
transaction in progress
~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:437
static void register_callback(stage stg, std::function< void()> cb)
Registers callback to be called on specified stage for the transaction.
Definition: transaction.hpp:487
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:387
static void abort(int err)
Manually abort the current transaction.
Definition: transaction.hpp:309
Custom transaction error class.
Definition: pexceptions.hpp:178
C++ transaction handler class.
Definition: transaction.hpp:623
~basic_transaction() noexcept=delete
Default destructor.
typename detail::transaction_base< false >::automatic automatic
C++ automatic scope transaction class.
Definition: transaction.hpp:669
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:676
typename detail::transaction_base< false >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:643
C++ flat transaction handler class.
Definition: transaction.hpp:724
~flat_transaction() noexcept=delete
Default destructor.
typename detail::transaction_base< true >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:745
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:810
typename detail::transaction_base< true >::automatic automatic
C++ automatic scope transaction class.
Definition: transaction.hpp:771
The non-template pool base class.
Definition: pool.hpp:51
PMEMobjpool * handle() noexcept
Gets the C style handle to the pool.
Definition: pool.hpp:395
PMEMobj pool class.
Definition: pool.hpp:482
Custom transaction error class.
Definition: pexceptions.hpp:109
Custom out of memory error class.
Definition: pexceptions.hpp:144
Custom transaction error class.
Definition: pexceptions.hpp:167
Commonly used functionality.
ExcT exception_with_errormsg(Args &&... args)
Generic error message decorator for pmemobj-based exceptions.
Definition: pexceptions.hpp:69
Persistent memory namespace.
Definition: allocation_flag.hpp:15
Custom pmem exceptions.
C++ pmemobj pool.
A structure that checks if it is possible to snapshot the specified memory.
Definition: transaction.hpp:41
#define LIBPMEMOBJ_CPP_FLAT_TX_USE_FAILURE_RETURN
Definition to enable back the old transaction behavior.
Definition: transaction.hpp:28