PMDK C++ bindings 1.13.0
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
26namespace pmem
27{
28
29namespace detail
30{
35template <typename T>
37 static constexpr bool value = LIBPMEMOBJ_CPP_IS_TRIVIALLY_COPYABLE(T);
38};
39
43template <bool is_flat>
45public:
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
488private:
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
586namespace obj
587{
588
614public:
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
741class flat_transaction : public detail::transaction_base<true> {
742public:
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
~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 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
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