PMDK C++ bindings  1.9.1
This is the C++ bindings documentation for PMDK's libpmemobj.
transaction.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016-2019, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
38 #ifndef LIBPMEMOBJ_CPP_TRANSACTION_HPP
39 #define LIBPMEMOBJ_CPP_TRANSACTION_HPP
40 
41 #include <array>
42 #include <functional>
43 #include <string>
44 #include <vector>
45 
48 #include <libpmemobj++/pool.hpp>
49 #include <libpmemobj/tx_base.h>
50 
51 namespace pmem
52 {
53 
54 namespace obj
55 {
56 
80 class transaction {
81 public:
100  class manual {
101  public:
115  template <typename... L>
116  manual(obj::pool_base &pop, L &... locks)
117  {
118  int ret = 0;
119 
120  if (pmemobj_tx_stage() == TX_STAGE_NONE) {
121  ret = pmemobj_tx_begin(pop.handle(), nullptr,
122  TX_PARAM_CB,
124  nullptr, TX_PARAM_NONE);
125  } else {
126  ret = pmemobj_tx_begin(pop.handle(), nullptr,
127  TX_PARAM_NONE);
128  }
129 
130  if (ret != 0)
132  "failed to start transaction")
133  .with_pmemobj_errormsg();
134 
135  auto err = add_lock(locks...);
136 
137  if (err) {
138  pmemobj_tx_abort(EINVAL);
139  (void)pmemobj_tx_end();
141  "failed to add lock")
142  .with_pmemobj_errormsg();
143  }
144  }
145 
153  ~manual() noexcept
154  {
155  /* normal exit or with an active exception */
156  if (pmemobj_tx_stage() == TX_STAGE_WORK)
157  pmemobj_tx_abort(ECANCELED);
158 
159  (void)pmemobj_tx_end();
160  }
161 
165  manual(const manual &p) = delete;
166 
170  manual(const manual &&p) = delete;
171 
175  manual &operator=(const manual &p) = delete;
176 
180  manual &operator=(manual &&p) = delete;
181  };
182 
183 /*
184  * XXX The Microsoft compiler does not follow the ISO SD-6: SG10 Feature
185  * Test Recommendations. "|| _MSC_VER >= 1900" is a workaround.
186  */
187 #if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
188 
207  class automatic {
208  public:
226  template <typename... L>
227  automatic(obj::pool_base &pop, L &... locks)
228  : tx_worker(pop, locks...)
229  {
230  }
231 
242  ~automatic() noexcept(false)
243  {
244  /* active exception, abort handled by tx_worker */
245  if (exceptions.new_uncaught_exception())
246  return;
247 
248  /* transaction ended normally */
249  if (pmemobj_tx_stage() == TX_STAGE_WORK)
250  pmemobj_tx_commit();
251  /* transaction aborted, throw an exception */
252  else if (pmemobj_tx_stage() == TX_STAGE_ONABORT ||
253  (pmemobj_tx_stage() == TX_STAGE_FINALLY &&
254  pmemobj_tx_errno() != 0))
256  "Transaction aborted");
257  }
258 
262  automatic(const automatic &p) = delete;
263 
267  automatic(const automatic &&p) = delete;
268 
272  automatic &operator=(const automatic &p) = delete;
273 
278 
279  private:
284  public:
292  : count(std::uncaught_exceptions())
293  {
294  }
295 
303  bool
305  {
306  return std::uncaught_exceptions() > this->count;
307  }
308 
309  private:
313  int count;
314  } exceptions;
315 
316  transaction::manual tx_worker;
317  };
318 #endif /* __cpp_lib_uncaught_exceptions */
319 
320  /*
321  * Deleted default constructor.
322  */
323  transaction() = delete;
324 
331  ~transaction() noexcept = delete;
332 
347  static void
348  abort(int err)
349  {
350  if (pmemobj_tx_stage() != TX_STAGE_WORK)
351  throw pmem::transaction_error("wrong stage for abort");
352 
353  pmemobj_tx_abort(err);
354  throw pmem::manual_tx_abort("explicit abort " +
355  std::to_string(err));
356  }
357 
368  static void
370  {
371  if (pmemobj_tx_stage() != TX_STAGE_WORK)
372  throw pmem::transaction_error("wrong stage for commit");
373 
374  pmemobj_tx_commit();
375  }
376 
377  static int
378  error() noexcept
379  {
380  return pmemobj_tx_errno();
381  }
382 
383  POBJ_CPP_DEPRECATED static int
384  get_last_tx_error() noexcept
385  {
386  return transaction::error();
387  }
388 
420  template <typename... Locks>
421  static void
422  run(pool_base &pool, std::function<void()> tx, Locks &... locks)
423  {
424  int ret = 0;
425 
426  if (pmemobj_tx_stage() == TX_STAGE_NONE) {
427  ret = pmemobj_tx_begin(pool.handle(), nullptr,
428  TX_PARAM_CB,
429  transaction::c_callback, nullptr,
430  TX_PARAM_NONE);
431  } else {
432  ret = pmemobj_tx_begin(pool.handle(), nullptr,
433  TX_PARAM_NONE);
434  }
435 
436  if (ret != 0)
438  "failed to start transaction")
439  .with_pmemobj_errormsg();
440 
441  auto err = add_lock(locks...);
442 
443  if (err) {
444  pmemobj_tx_abort(err);
445  (void)pmemobj_tx_end();
447  "failed to add a lock to the transaction")
448  .with_pmemobj_errormsg();
449  }
450 
451  try {
452  tx();
453  } catch (manual_tx_abort &) {
454  (void)pmemobj_tx_end();
455  throw;
456  } catch (...) {
457  /* first exception caught */
458  if (pmemobj_tx_stage() == TX_STAGE_WORK)
459  pmemobj_tx_abort(ECANCELED);
460 
461  /* waterfall tx_end for outer tx */
462  (void)pmemobj_tx_end();
463  throw;
464  }
465 
466  auto stage = pmemobj_tx_stage();
467 
468  if (stage == TX_STAGE_WORK) {
469  pmemobj_tx_commit();
470  } else if (stage == TX_STAGE_ONABORT) {
471  (void)pmemobj_tx_end();
472  throw pmem::transaction_error("transaction aborted");
473  } else if (stage == TX_STAGE_NONE) {
475  "transaction ended prematurely");
476  }
477 
478  (void)pmemobj_tx_end();
479  }
480 
481  template <typename... Locks>
482  POBJ_CPP_DEPRECATED static void
483  exec_tx(pool_base &pool, std::function<void()> tx, Locks &... locks)
484  {
485  transaction::run(pool, tx, locks...);
486  }
487 
508  template <
509  typename T,
510  typename std::enable_if<LIBPMEMOBJ_CPP_IS_TRIVIALLY_COPYABLE(T),
511  T>::type * = nullptr>
512  static void
513  snapshot(const T *addr, size_t num = 1)
514  {
515  if (TX_STAGE_WORK != pmemobj_tx_stage())
517  "wrong stage for taking a snapshot.");
518 
519  if (pmemobj_tx_add_range_direct(addr, sizeof(*addr) * num)) {
520  if (errno == ENOMEM)
522  "Could not take a snapshot of given memory range.")
523  .with_pmemobj_errormsg();
524  else
526  "Could not take a snapshot of given memory range.")
527  .with_pmemobj_errormsg();
528  }
529  }
530 
535  enum class stage {
536  work = TX_STAGE_WORK, /* transaction in progress */
537  oncommit = TX_STAGE_ONCOMMIT, /* successfully committed */
538  onabort = TX_STAGE_ONABORT, /* tx_begin failed or transaction
539  aborted */
540  finally = TX_STAGE_FINALLY, /* ready for cleanup */
541  };
542 
556  static void
557  register_callback(stage stg, std::function<void()> cb)
558  {
559  if (pmemobj_tx_stage() != TX_STAGE_WORK)
561  "register_callback must be called during a transaction");
562 
563  get_tx_data()->callbacks[static_cast<size_t>(stg)].push_back(
564  cb);
565  }
566 
567 private:
580  template <typename L, typename... Locks>
581  static int
582  add_lock(L &lock, Locks &... locks) noexcept
583  {
584  auto err =
585  pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
586 
587  if (err)
588  return err;
589 
590  return add_lock(locks...);
591  }
592 
596  static inline int
597  add_lock() noexcept
598  {
599  return 0;
600  }
601 
602  using callbacks_list_type = std::vector<std::function<void()>>;
603  using callbacks_map_type =
604  std::array<callbacks_list_type, MAX_TX_STAGE>;
605 
610  static void
611  c_callback(PMEMobjpool *pop, enum pobj_tx_stage obj_stage, void *arg)
612  {
613  /*
614  * We cannot do anything when in TX_STAGE_NONE because
615  * pmemobj_tx_get_user_data() can only be called when there is
616  * an active transaction.
617  */
618  if (obj_stage == TX_STAGE_NONE)
619  return;
620 
621  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
622  if (data == nullptr)
623  return;
624 
625  for (auto &cb : data->callbacks[obj_stage])
626  cb();
627 
628  /*
629  * Callback for TX_STAGE_FINALLY is called as the last one so we
630  * can free tx_data here
631  */
632  if (obj_stage == TX_STAGE_FINALLY) {
633  delete data;
634  pmemobj_tx_set_user_data(NULL);
635  }
636  }
637 
642  struct tx_data {
643  callbacks_map_type callbacks;
644  };
645 
650  static tx_data *
652  {
653  auto *data = static_cast<tx_data *>(pmemobj_tx_get_user_data());
654  if (data == nullptr) {
655  data = new tx_data;
656  pmemobj_tx_set_user_data(data);
657  }
658 
659  return data;
660  }
661 };
662 
663 } /* namespace obj */
664 
665 } /* namespace pmem */
666 
667 #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:227
pmem::obj::transaction::automatic::uncaught_exception_counter::count
int count
The number of active exceptions.
Definition: transaction.hpp:313
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:557
pmem::obj::transaction::commit
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:369
pmem::obj::transaction::manual::operator=
manual & operator=(manual &&p)=delete
Deleted move assignment operator.
pmem::transaction_error
Custom transaction error class.
Definition: pexceptions.hpp:94
pmem
Persistent memory namespace.
Definition: allocation_flag.hpp:44
pmem::transaction_out_of_memory
Custom out of memory error class.
Definition: pexceptions.hpp:154
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:582
pmem::obj::transaction::manual::~manual
~manual() noexcept
Destructor.
Definition: transaction.hpp:153
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:513
pexceptions.hpp
Custom exceptions.
pmem::obj::p
Resides on pmem class.
Definition: p.hpp:64
pmem::obj::transaction::manual::manual
manual(obj::pool_base &pop, L &... locks)
RAII constructor with pmem resident locks.
Definition: transaction.hpp:116
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:535
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:422
pmem::obj::transaction::add_lock
static int add_lock() noexcept
Method ending the recursive algorithm.
Definition: transaction.hpp:597
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:642
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:611
pmem::obj::pool_base::handle
PMEMobjpool * handle() noexcept
Gets the C style handle to the pool.
Definition: pool.hpp:427
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:207
pmem::obj::transaction::automatic::uncaught_exception_counter
Internal class for counting active exceptions.
Definition: transaction.hpp:283
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:304
pmem::obj::transaction::automatic::uncaught_exception_counter::uncaught_exception_counter
uncaught_exception_counter()
Default constructor.
Definition: transaction.hpp:291
pmem::obj::pool
PMEMobj pool class.
Definition: pool.hpp:491
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:242
pmem::obj::transaction::manual::manual
manual(const manual &p)=delete
Deleted copy constructor.
pmem::obj::transaction
C++ transaction handler class.
Definition: transaction.hpp:80
pmem::transaction_scope_error
Custom transaction error class.
Definition: pexceptions.hpp:192
pmem::manual_tx_abort
Custom transaction error class.
Definition: pexceptions.hpp:202
pmem::obj::pool_base
The non-template pool base class.
Definition: pool.hpp:75
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:348
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:651
pmem::obj::transaction::manual
C++ manual scope transaction class.
Definition: transaction.hpp:100
pmem::obj::transaction::manual::manual
manual(const manual &&p)=delete
Deleted move constructor.