#ifndef LIBPMEMOBJ_TRANSACTION_HPP
#define LIBPMEMOBJ_TRANSACTION_HPP
#include <functional>
#include <string>
#include "libpmemobj++/detail/pexceptions.hpp"
#include "libpmemobj++/pool.hpp"
#include "libpmemobj/tx_base.h"
namespace pmem
{
namespace obj
{
class transaction {
public:
class manual {
public:
template <typename... L>
manual(obj::pool_base &pop, L &... locks)
{
if (pmemobj_tx_begin(pop.get_handle(), NULL,
TX_PARAM_NONE) != 0)
throw transaction_error(
"failed to start transaction");
auto err = add_lock(locks...);
if (err) {
pmemobj_tx_abort(EINVAL);
throw transaction_error("failed to"
" add lock");
}
}
~manual() noexcept
{
if (pmemobj_tx_stage() == TX_STAGE_WORK)
pmemobj_tx_abort(ECANCELED);
(void)pmemobj_tx_end();
}
manual(const manual &p) = delete;
manual(const manual &&p) = delete;
manual &operator=(const manual &p) = delete;
manual &operator=(manual &&p) = delete;
};
#if __cpp_lib_uncaught_exceptions || _MSC_VER >= 1900
class automatic {
public:
template <typename... L>
automatic(obj::pool_base &pop, L &... locks)
: tx_worker(pop, locks...)
{
}
~automatic() noexcept(false)
{
if (exceptions.new_uncaught_exception())
return;
if (pmemobj_tx_stage() == TX_STAGE_WORK)
pmemobj_tx_commit();
else if (pmemobj_tx_stage() == TX_STAGE_ONABORT ||
(pmemobj_tx_stage() == TX_STAGE_FINALLY &&
pmemobj_tx_errno() != 0))
throw transaction_error("Transaction aborted");
}
automatic(const automatic &p) = delete;
automatic(const automatic &&p) = delete;
automatic &operator=(const automatic &p) = delete;
automatic &operator=(automatic &&p) = delete;
private:
class uncaught_exception_counter {
public:
uncaught_exception_counter()
: count(std::uncaught_exceptions())
{
}
bool
new_uncaught_exception()
{
return std::uncaught_exceptions() > this->count;
}
private:
int count;
} exceptions;
transaction::manual tx_worker;
};
#endif
transaction() = delete;
~transaction() noexcept = delete;
static void
abort(int err)
{
if (pmemobj_tx_stage() != TX_STAGE_WORK)
throw transaction_error("wrong stage for"
" abort");
pmemobj_tx_abort(err);
throw manual_tx_abort("explicit abort " + std::to_string(err));
}
static void
commit()
{
if (pmemobj_tx_stage() != TX_STAGE_WORK)
throw transaction_error("wrong stage for"
" commit");
pmemobj_tx_commit();
}
static int
get_last_tx_error() noexcept
{
return pmemobj_tx_errno();
}
template <typename... Locks>
static void
exec_tx(pool_base &pool, std::function<void()> tx, Locks &... locks)
{
if (pmemobj_tx_begin(pool.get_handle(), NULL, TX_PARAM_NONE) !=
0)
throw transaction_error("failed to start transaction");
auto err = add_lock(locks...);
if (err) {
pmemobj_tx_abort(err);
(void)pmemobj_tx_end();
throw transaction_error("failed to add a lock to the"
" transaction");
}
try {
tx();
} catch (manual_tx_abort &) {
(void)pmemobj_tx_end();
throw;
} catch (...) {
if (pmemobj_tx_stage() == TX_STAGE_WORK)
pmemobj_tx_abort(ECANCELED);
(void)pmemobj_tx_end();
throw;
}
auto stage = pmemobj_tx_stage();
if (stage == TX_STAGE_WORK) {
pmemobj_tx_commit();
} else if (stage == TX_STAGE_ONABORT) {
(void)pmemobj_tx_end();
throw transaction_error("transaction aborted");
} else if (stage == TX_STAGE_NONE) {
throw transaction_error("transaction ended"
"prematurely");
}
(void)pmemobj_tx_end();
}
private:
template <typename L, typename... Locks>
static int
add_lock(L &lock, Locks &... locks) noexcept
{
auto err =
pmemobj_tx_lock(lock.lock_type(), lock.native_handle());
if (err)
return err;
return add_lock(locks...);
}
static inline int
add_lock() noexcept
{
return 0;
}
};
}
}
#endif