.\" Automatically generated by Pandoc 2.9.2.1
.\"
.TH "" "" "2023-05-31" "PMDK - " "PMDK Programmer's Manual"
.hy
.\" SPDX-License-Identifier: BSD-3-Clause
.\" Copyright 2017-2020, Intel Corporation
.SH NAME
.PP
\f[B]pmemobj_tx_stage\f[R](),
.PP
\f[B]pmemobj_tx_begin\f[R](), \f[B]pmemobj_tx_lock\f[R](),
\f[B]pmemobj_tx_xlock\f[R](), \f[B]pmemobj_tx_abort\f[R](),
\f[B]pmemobj_tx_commit\f[R](), \f[B]pmemobj_tx_end\f[R](),
\f[B]pmemobj_tx_errno\f[R](), \f[B]pmemobj_tx_process\f[R](),
.PP
\f[B]TX_BEGIN_PARAM\f[R](), \f[B]TX_BEGIN_CB\f[R](),
\f[B]TX_BEGIN\f[R](), \f[B]TX_ONABORT\f[R], \f[B]TX_ONCOMMIT\f[R],
\f[B]TX_FINALLY\f[R], \f[B]TX_END\f[R],
.PP
\f[B]pmemobj_tx_log_append_buffer\f[R](),
\f[B]pmemobj_tx_xlog_append_buffer\f[R](),
\f[B]pmemobj_tx_log_auto_alloc\f[R](),
\f[B]pmemobj_tx_log_snapshots_max_size\f[R](),
\f[B]pmemobj_tx_log_intents_max_size\f[R](),
.PP
\f[B]pmemobj_tx_set_user_data\f[R](),
\f[B]pmemobj_tx_get_user_data\f[R](),
.PP
\f[B]pmemobj_tx_set_failure_behavior\f[R](),
\f[B]pmemobj_tx_get_failure_behavior\f[R]() - transactional object
manipulation
.SH SYNOPSIS
.IP
.nf
\f[C]
#include <libpmemobj.h>

enum pobj_tx_stage pmemobj_tx_stage(void);

int pmemobj_tx_begin(PMEMobjpool *pop, jmp_buf *env, enum pobj_tx_param, ...);
int pmemobj_tx_lock(enum tx_lock lock_type, void *lockp);
int pmemobj_tx_xlock(enum tx_lock lock_type, void *lockp, uint64_t flags);
void pmemobj_tx_abort(int errnum);
void pmemobj_tx_commit(void);
int pmemobj_tx_end(void);
int pmemobj_tx_errno(void);
void pmemobj_tx_process(void);

TX_BEGIN_PARAM(PMEMobjpool *pop, ...)
TX_BEGIN_CB(PMEMobjpool *pop, cb, arg, ...)
TX_BEGIN(PMEMobjpool *pop)
TX_ONABORT
TX_ONCOMMIT
TX_FINALLY
TX_END

int pmemobj_tx_log_append_buffer(enum pobj_log_type type, void *addr, size_t size);
int pmemobj_tx_xlog_append_buffer(enum pobj_log_type type, void *addr, size_t size, uint64_t flags);
int pmemobj_tx_log_auto_alloc(enum pobj_log_type type, int on_off);
size_t pmemobj_tx_log_snapshots_max_size(size_t *sizes, size_t nsizes);
size_t pmemobj_tx_log_intents_max_size(size_t nintents);

void pmemobj_tx_set_user_data(void *data);
void *pmemobj_tx_get_user_data(void);

void pmemobj_tx_set_failure_behavior(enum pobj_tx_failure_behavior behavior);
enum pobj_tx_failure_behavior pmemobj_tx_get_failure_behavior(void);
\f[R]
.fi
.SH DESCRIPTION
.PP
The non-transactional functions and macros described in
\f[B]pmemobj_alloc\f[R](3), \f[B]pmemobj_list_insert\f[R](3) and
\f[B]POBJ_LIST_HEAD\f[R](3) only guarantee the atomicity of a single
operation on an object.
In case of more complex changes involving multiple operations on an
object, or allocation and modification of multiple objects, data
consistency and fail-safety may be provided only by using \f[I]atomic
transactions\f[R].
.PP
A transaction is defined as series of operations on persistent memory
objects that either all occur, or nothing occurs.
In particular, if the execution of a transaction is interrupted by a
power failure or a system crash, it is guaranteed that after system
restart, all the changes made as a part of the uncompleted transaction
will be rolled back, restoring the consistent state of the memory pool
from the moment when the transaction was started.
.PP
Note that transactions do not provide atomicity with respect to other
threads.
All the modifications performed within the transactions are immediately
visible to other threads.
Therefore it is the responsibility of the application to implement a
proper thread synchronization mechanism.
.PP
Each thread may have only one transaction open at a time, but that
transaction may be nested.
Nested transactions are flattened.
Committing the nested transaction does not commit the outer transaction;
however, errors in the nested transaction are propagated up to the
outermost level, resulting in the interruption of the entire
transaction.
.PP
Each transaction is visible only for the thread that started it.
No other threads can add operations, commit or abort the transaction
initiated by another thread.
Multiple threads may have transactions open on a given memory pool at
the same time.
.PP
Please see the \f[B]CAVEATS\f[R] section below for known limitations of
the transactional API.
.PP
The \f[B]pmemobj_tx_stage\f[R]() function returns the current
\f[I]transaction stage\f[R] for a thread.
Stages are changed only by the \f[B]pmemobj_tx_*\f[R]() functions.
Transaction stages are defined as follows:
.IP \[bu] 2
\f[B]TX_STAGE_NONE\f[R] - no open transaction in this thread
.IP \[bu] 2
\f[B]TX_STAGE_WORK\f[R] - transaction in progress
.IP \[bu] 2
\f[B]TX_STAGE_ONCOMMIT\f[R] - successfully committed
.IP \[bu] 2
\f[B]TX_STAGE_ONABORT\f[R] - starting the transaction failed or
transaction aborted
.IP \[bu] 2
\f[B]TX_STAGE_FINALLY\f[R] - ready for clean up
.PP
The \f[B]pmemobj_tx_begin\f[R]() function starts a new transaction in
the current thread.
If called within an open transaction, it starts a nested transaction.
The caller may use the \f[I]env\f[R] argument to provide a pointer to a
calling environment to be restored in case of transaction abort.
This information must be provided by the caller using the
\f[B]setjmp\f[R](3) macro.
.PP
A new transaction may be started only if the current stage is
\f[B]TX_STAGE_NONE\f[R] or \f[B]TX_STAGE_WORK\f[R].
If successful, the \f[I]transaction stage\f[R] changes to
\f[B]TX_STAGE_WORK\f[R].
Otherwise, the stage is changed to \f[B]TX_STAGE_ONABORT\f[R].
.PP
Optionally, a list of parameters for the transaction may be provided.
Each parameter consists of a type followed by a type-specific number of
values.
Currently there are 4 types:
.IP \[bu] 2
\f[B]TX_PARAM_NONE\f[R], used as a termination marker.
No following value.
.IP \[bu] 2
\f[B]TX_PARAM_MUTEX\f[R], followed by one value, a pmem-resident
PMEMmutex
.IP \[bu] 2
\f[B]TX_PARAM_RWLOCK\f[R], followed by one value, a pmem-resident
PMEMrwlock
.IP \[bu] 2
\f[B]TX_PARAM_CB\f[R], followed by two values: a callback function of
type \f[I]pmemobj_tx_callback\f[R], and a void pointer
.PP
Using \f[B]TX_PARAM_MUTEX\f[R] or \f[B]TX_PARAM_RWLOCK\f[R] causes the
specified lock to be acquired at the beginning of the transaction.
\f[B]TX_PARAM_RWLOCK\f[R] acquires the lock for writing.
It is guaranteed that \f[B]pmemobj_tx_begin\f[R]() will acquire all
locks prior to successful completion, and they will be held by the
current thread until the outermost transaction is finished.
Locks are taken in order from left to right.
To avoid deadlocks, the user is responsible for proper lock ordering.
.PP
\f[B]TX_PARAM_CB\f[R] registers the specified callback function to be
executed at each transaction stage.
For \f[B]TX_STAGE_WORK\f[R], the callback is executed prior to commit.
For all other stages, the callback is executed as the first operation
after a stage change.
It will also be called after each transaction; in this case the
\f[I]stage\f[R] parameter will be set to \f[B]TX_STAGE_NONE\f[R].
\f[I]pmemobj_tx_callback\f[R] must be compatible with:
.IP
.nf
\f[C]
void func(PMEMobjpool *pop, enum pobj_tx_stage stage, void *arg)
\f[R]
.fi
.PP
\f[I]pop\f[R] is a pool identifier used in \f[B]pmemobj_tx_begin\f[R](),
\f[I]stage\f[R] is a current transaction stage and \f[I]arg\f[R] is the
second parameter of \f[B]TX_PARAM_CB\f[R].
Without considering transaction nesting, this mechanism can be
considered an alternative method for executing code between stages
(instead of \f[B]TX_ONCOMMIT\f[R], \f[B]TX_ONABORT\f[R], etc).
However, there are 2 significant differences when nested transactions
are used:
.IP \[bu] 2
The registered function is executed only in the outermost transaction,
even if registered in an inner transaction.
.IP \[bu] 2
There can be only one callback in the entire transaction, that is, the
callback cannot be changed in an inner transaction.
.PP
Note that \f[B]TX_PARAM_CB\f[R] does not replace the
\f[B]TX_ONCOMMIT\f[R], \f[B]TX_ONABORT\f[R], etc.
macros.
They can be used together: the callback will be executed
\f[I]before\f[R] a \f[B]TX_ONCOMMIT\f[R], \f[B]TX_ONABORT\f[R], etc.
section.
.PP
\f[B]TX_PARAM_CB\f[R] can be used when the code dealing with transaction
stage changes is shared between multiple users or when it must be
executed only in the outer transaction.
For example it can be very useful when the application must synchronize
persistent and transient state.
.PP
The \f[B]pmemobj_tx_lock\f[R]() function acquires the lock
\f[I]lockp\f[R] of type \f[I]lock_type\f[R] and adds it to the current
transaction.
\f[I]lock_type\f[R] may be \f[B]TX_LOCK_MUTEX\f[R] or
\f[B]TX_LOCK_RWLOCK\f[R]; \f[I]lockp\f[R] must be of type
\f[I]PMEMmutex\f[R] or \f[I]PMEMrwlock\f[R], respectively.
If \f[I]lock_type\f[R] is \f[B]TX_LOCK_RWLOCK\f[R] the lock is acquired
for writing.
If the lock is not successfully acquired, the function returns an error
number.
This function must be called during \f[B]TX_STAGE_WORK\f[R].
.PP
The \f[B]pmemobj_tx_xlock\f[R]() function behaves exactly the same as
\f[B]pmemobj_tx_lock\f[R]() when \f[I]flags\f[R] equals
\f[B]POBJ_XLOCK_NO_ABORT\f[R].
When \f[I]flags\f[R] equals 0 and if the lock is not successfully
acquired,the transaction is aborted.
\f[I]flags\f[R] is a bitmask of the following values:
.IP \[bu] 2
\f[B]POBJ_XLOCK_NO_ABORT\f[R] - if the function does not end
successfully, do not abort the transaction.
.PP
\f[B]pmemobj_tx_abort\f[R]() aborts the current transaction and causes a
transition to \f[B]TX_STAGE_ONABORT\f[R].
If \f[I]errnum\f[R] is equal to 0, the transaction error code is set to
\f[B]ECANCELED\f[R]; otherwise, it is set to \f[I]errnum\f[R].
This function must be called during \f[B]TX_STAGE_WORK\f[R].
.PP
The \f[B]pmemobj_tx_commit\f[R]() function commits the current open
transaction and causes a transition to \f[B]TX_STAGE_ONCOMMIT\f[R].
If called in the context of the outermost transaction, all the changes
may be considered as durably written upon successful completion.
This function must be called during \f[B]TX_STAGE_WORK\f[R].
.PP
The \f[B]pmemobj_tx_end\f[R]() function performs a cleanup of the
current transaction.
If called in the context of the outermost transaction, it releases all
the locks acquired by \f[B]pmemobj_tx_begin\f[R]() for outer and nested
transactions.
If called in the context of a nested transaction, it returns to the
context of the outer transaction in \f[B]TX_STAGE_WORK\f[R], without
releasing any locks.
The \f[B]pmemobj_tx_end\f[R]() function can be called during
\f[B]TX_STAGE_NONE\f[R] if transitioned to this stage using
\f[B]pmemobj_tx_process\f[R]().
If not already in \f[B]TX_STAGE_NONE\f[R], it causes the transition to
\f[B]TX_STAGE_NONE\f[R].
\f[B]pmemobj_tx_end\f[R] must always be called for each
\f[B]pmemobj_tx_begin\f[R](), even if starting the transaction failed.
This function must \f[I]not\f[R] be called during
\f[B]TX_STAGE_WORK\f[R].
.PP
The \f[B]pmemobj_tx_errno\f[R]() function returns the error code of the
last transaction.
.PP
The \f[B]pmemobj_tx_process\f[R]() function performs the actions
associated with the current stage of the transaction, and makes the
transition to the next stage.
It must be called in a transaction.
The current stage must always be obtained by a call to
\f[B]pmemobj_tx_stage\f[R]().
\f[B]pmemobj_tx_process\f[R]() performs the following transitions in the
transaction stage flow:
.IP \[bu] 2
\f[B]TX_STAGE_WORK\f[R] -> \f[B]TX_STAGE_ONCOMMIT\f[R]
.IP \[bu] 2
\f[B]TX_STAGE_ONABORT\f[R] -> \f[B]TX_STAGE_FINALLY\f[R]
.IP \[bu] 2
\f[B]TX_STAGE_ONCOMMIT\f[R] -> \f[B]TX_STAGE_FINALLY\f[R]
.IP \[bu] 2
\f[B]TX_STAGE_FINALLY\f[R] -> \f[B]TX_STAGE_NONE\f[R]
.IP \[bu] 2
\f[B]TX_STAGE_NONE\f[R] -> \f[B]TX_STAGE_NONE\f[R]
.PP
\f[B]pmemobj_tx_process\f[R]() must not be called after calling
\f[B]pmemobj_tx_end\f[R]() for the outermost transaction.
.PP
In addition to the above API, \f[B]libpmemobj\f[R](7) offers a more
intuitive method of building transactions using the set of macros
described below.
When using these macros, the complete transaction flow looks like this:
.IP
.nf
\f[C]
TX_BEGIN(Pop) {
    /* the actual transaction code goes here... */
} TX_ONCOMMIT {
    /*
     * optional - executed only if the above block
     * successfully completes
     */
} TX_ONABORT {
    /*
     * optional - executed only if starting the transaction fails,
     * or if transaction is aborted by an error or a call to
     * pmemobj_tx_abort()
     */
} TX_FINALLY {
    /*
     * optional - if exists, it is executed after
     * TX_ONCOMMIT or TX_ONABORT block
     */
} TX_END /* mandatory */
\f[R]
.fi
.IP
.nf
\f[C]
TX_BEGIN_PARAM(PMEMobjpool *pop, ...)
TX_BEGIN_CB(PMEMobjpool *pop, cb, arg, ...)
TX_BEGIN(PMEMobjpool *pop)
\f[R]
.fi
.PP
The \f[B]TX_BEGIN_PARAM\f[R](), \f[B]TX_BEGIN_CB\f[R]() and
\f[B]TX_BEGIN\f[R]() macros start a new transaction in the same way as
\f[B]pmemobj_tx_begin\f[R](), except that instead of the environment
buffer provided by a caller, they set up the local \f[I]jmp_buf\f[R]
buffer and use it to catch the transaction abort.
The \f[B]TX_BEGIN\f[R]() macro starts a transaction without any options.
\f[B]TX_BEGIN_PARAM\f[R] may be used when there is a need to acquire
locks prior to starting a transaction (such as for a multi-threaded
program) or set up a transaction stage callback.
\f[B]TX_BEGIN_CB\f[R] is just a wrapper around \f[B]TX_BEGIN_PARAM\f[R]
that validates the callback signature.
(For compatibility there is also a \f[B]TX_BEGIN_LOCK\f[R] macro, which
is an alias for \f[B]TX_BEGIN_PARAM\f[R]).
Each of these macros must be followed by a block of code with all the
operations that are to be performed atomically.
.PP
The \f[B]TX_ONABORT\f[R] macro starts a block of code that will be
executed only if starting the transaction fails due to an error in
\f[B]pmemobj_tx_begin\f[R](), or if the transaction is aborted.
This block is optional, but in practice it should not be omitted.
If it is desirable to crash the application when a transaction aborts
and there is no \f[B]TX_ONABORT\f[R] section, the application can define
the \f[B]POBJ_TX_CRASH_ON_NO_ONABORT\f[R] macro before inclusion of
\f[B]<libpmemobj.h>\f[R].
This provides a default \f[B]TX_ONABORT\f[R] section which just calls
\f[B]abort\f[R](3).
.PP
The \f[B]TX_ONCOMMIT\f[R] macro starts a block of code that will be
executed only if the transaction is successfully committed, which means
that the execution of code in the \f[B]TX_BEGIN\f[R]() block has not
been interrupted by an error or by a call to
\f[B]pmemobj_tx_abort\f[R]().
This block is optional.
.PP
The \f[B]TX_FINALLY\f[R] macro starts a block of code that will be
executed regardless of whether the transaction is committed or aborted.
This block is optional.
.PP
The \f[B]TX_END\f[R] macro cleans up and closes the transaction started
by the \f[B]TX_BEGIN\f[R]() / \f[B]TX_BEGIN_PARAM\f[R]() /
\f[B]TX_BEGIN_CB\f[R]() macros.
It is mandatory to terminate each transaction with this macro.
If the transaction was aborted, \f[I]errno\f[R] is set appropriately.
.SS TRANSACTION LOG TUNING
.PP
From libpmemobj implementation perspective there are two types of
operations in a transaction:
.IP \[bu] 2
\f[B]snapshots\f[R], where action must be persisted immediately,
.IP \[bu] 2
\f[B]intents\f[R], where action can be persisted at the transaction
commit phase
.PP
\f[B]pmemobj_tx_add_range\f[R](3) and all its variants belong to the
\f[B]snapshots\f[R] group.
.PP
\f[B]pmemobj_tx_alloc\f[R](3) (with its variants),
\f[B]pmemobj_tx_free\f[R](3), \f[B]pmemobj_tx_realloc\f[R](3) (with its
variants) and \f[B]pmemobj_tx_publish\f[R](3) belong to the
\f[B]intents\f[R] group.
Even though \f[B]pmemobj_tx_alloc\f[R]() allocates memory immediately,
it modifies only the runtime state and postpones persistent memory
modifications to the commit phase.
\f[B]pmemobj_tx_free\f[R](3) cannot free the object immediately, because
of possible transaction rollback, so it postpones both the action and
persistent memory modifications to the commit phase.
\f[B]pmemobj_tx_realloc\f[R](3) is just a combination of those two.
\f[B]pmemobj_tx_publish\f[R](3) postpones reservations and deferred
frees to the commit phase.
.PP
Those two types of operations (snapshots and intents) require that
libpmemobj builds a persistent log of operations.
Intent log (also known as a \[lq]redo log\[rq]) is applied on commit and
snapshot log (also known as an \[lq]undo log\[rq]) is applied on abort.
.PP
When libpmemobj transaction starts, it\[cq]s not possible to predict how
much persistent memory space will be needed for those logs.
This means that libpmemobj must internally allocate this space whenever
it\[cq]s needed.
This has two downsides:
.IP \[bu] 2
when transaction snapshots a lot of memory or does a lot of allocations,
libpmemobj may need to do many internal allocations, which must be freed
when transaction ends, adding time overhead when big transactions are
frequent,
.IP \[bu] 2
transactions can start to fail due to not enough space for logs - this
can be especially problematic for transactions that want to
\f[B]deallocate\f[R] objects, as those might also fail
.PP
To solve both of these problems libpmemobj exposes the following
functions:
.IP \[bu] 2
\f[B]pmemobj_tx_log_append_buffer\f[R](),
.IP \[bu] 2
\f[B]pmemobj_tx_xlog_append_buffer\f[R](),
.IP \[bu] 2
\f[B]pmemobj_tx_log_auto_alloc\f[R]()
.PP
\f[B]pmemobj_tx_log_append_buffer\f[R]() appends a given range of memory
[\f[I]addr\f[R], \f[I]addr\f[R] + \f[I]size\f[R]) to the log
\f[I]type\f[R] of the current transaction.
\f[I]type\f[R] can be one of the two values (with meanings described
above):
.IP \[bu] 2
\f[B]TX_LOG_TYPE_SNAPSHOT\f[R],
.IP \[bu] 2
\f[B]TX_LOG_TYPE_INTENT\f[R]
.PP
The range of memory \f[B]must\f[R] belong to the same pool the
transaction is on and \f[B]must not\f[R] be used by more than one thread
at the same time.
The latter condition can be verified with tx.debug.verify_user_buffers
ctl (see \f[B]pmemobj_ctl_get\f[R](3)).
.PP
The \f[B]pmemobj_tx_xlog_append_buffer\f[R]() function behaves exactly
the same as \f[B]pmemobj_tx_log_append_buffer\f[R]() when
\f[I]flags\f[R] equals zero.
\f[I]flags\f[R] is a bitmask of the following values:
.IP \[bu] 2
\f[B]POBJ_XLOG_APPEND_BUFFER_NO_ABORT\f[R] - if the function does not
end successfully, do not abort the transaction.
.PP
\f[B]pmemobj_tx_log_snapshots_max_size\f[R] calculates the
\f[B]maximum\f[R] size of a buffer which will be able to hold
\f[I]nsizes\f[R] snapshots, each of size \f[I]sizes[i]\f[R].
Application should not expect this function to return the same value
between restarts.
In future versions of libpmemobj this function can return smaller
(because of better accuracy or space optimizations) or higher (because
of higher alignment required for better performance) value.
This function is independent of transaction stage and can be called both
inside and outside of transaction.
If the returned value S is greater than
\f[B]PMEMOBJ_MAX_ALLOC_SIZE\f[R], the buffer should be split into N
chunks of size \f[B]PMEMOBJ_MAX_ALLOC_SIZE\f[R], where N is equal to (S
/ \f[B]PMEMOBJ_MAX_ALLOC_SIZE\f[R]) (rounded down) and the last chunk of
size (S - (N * \f[B]PMEMOBJ_MAX_ALLOC_SIZE\f[R])).
.PP
\f[B]pmemobj_tx_log_intents_max_size\f[R] calculates the
\f[B]maximum\f[R] size of a buffer which will be able to hold
\f[I]nintents\f[R] intents.
Just like with \f[B]pmemobj_tx_log_snapshots_max_size\f[R], application
should not expect this function to return the same value between
restarts, for the same reasons.
This function is independent of transaction stage and can be called both
inside and outside of transaction.
.PP
\f[B]pmemobj_tx_log_auto_alloc\f[R]() disables (\f[I]on_off\f[R] set to
0) or enables (\f[I]on_off\f[R] set to 1) automatic allocation of
internal logs of given \f[I]type\f[R].
It can be used to verify that the buffer set with
\f[B]pmemobj_tx_log_append_buffer\f[R]() is big enough to hold the log,
without reaching out-of-space scenario.
.PP
The \f[B]pmemobj_tx_set_user_data\f[R]() function associates custom
volatile state, represented by pointer \f[I]data\f[R], with the current
transaction.
This state can later be retrieved using
\f[B]pmemobj_tx_get_user_data\f[R]() function.
If \f[B]pmemobj_tx_set_user_data\f[R]() was not called for a current
transaction, \f[B]pmemobj_tx_get_user_data\f[R]() will return NULL.
These functions must be called during \f[B]TX_STAGE_WORK\f[R] or
\f[B]TX_STAGE_ONABORT\f[R] or \f[B]TX_STAGE_ONCOMMIT\f[R] or
\f[B]TX_STAGE_FINALLY\f[R].
.PP
\f[B]pmemobj_tx_set_failure_behavior\f[R]() specifies what should happen
in case of an error within the transaction.
It only affects functions which take a NO_ABORT flag.
If \f[B]pmemobj_tx_set_failure_behavior\f[R]() is called with
POBJ_TX_FAILURE_RETURN a NO_ABORT flag is implicitly passed to all
functions which accept this flag.
If called with POBJ_TX_FAILURE_ABORT then all functions abort the
transaction (unless NO_ABORT flag is passed explicitly).
This setting is inherited by inner transactions.
It does not affect any of the outer transactions.
Aborting on failure is the default behavior.
\f[B]pmemobj_tx_get_failure_behavior\f[R]() returns failure behavior for
the current transaction.
Both \f[B]pmemobj_tx_set_failure_behavior\f[R]() and
\f[B]pmemobj_tx_get_failure_behavior\f[R]() must be called during
\f[B]TX_STAGE_WORK\f[R].
.SH RETURN VALUE
.PP
The \f[B]pmemobj_tx_stage\f[R]() function returns the stage of the
current transaction stage for a thread.
.PP
On success, \f[B]pmemobj_tx_begin\f[R]() returns 0.
Otherwise, an error number is returned.
.PP
The \f[B]pmemobj_tx_begin\f[R]() and \f[B]pmemobj_tx_lock\f[R]()
functions return zero if \f[I]lockp\f[R] is successfully added to the
transaction.
Otherwise, an error number is returned.
.PP
The \f[B]pmemobj_tx_xlock\f[R]() function return zero if \f[I]lockp\f[R]
is successfully added to the transaction.
Otherwise, the error number is returned, \f[B]errno\f[R] is set and when
flags do not contain \f[B]POBJ_XLOCK_NO_ABORT\f[R], the transaction is
aborted.
.PP
The \f[B]pmemobj_tx_abort\f[R]() and \f[B]pmemobj_tx_commit\f[R]()
functions return no value.
.PP
The \f[B]pmemobj_tx_end\f[R]() function returns 0 if the transaction was
successful.
Otherwise it returns the error code set by \f[B]pmemobj_tx_abort\f[R]().
Note that \f[B]pmemobj_tx_abort\f[R]() can be called internally by the
library.
.PP
The \f[B]pmemobj_tx_errno\f[R]() function returns the error code of the
last transaction.
.PP
The \f[B]pmemobj_tx_process\f[R]() function returns no value.
.PP
On success, \f[B]pmemobj_tx_log_append_buffer\f[R]() returns 0.
Otherwise, the stage is changed to \f[B]TX_STAGE_ONABORT\f[R],
\f[B]errno\f[R] is set appropriately and transaction is aborted.
.PP
On success, \f[B]pmemobj_tx_xlog_append_buffer\f[R]() returns 0.
Otherwise, the error number is returned, \f[B]errno\f[R] is set and when
flags do not contain \f[B]POBJ_XLOG_NO_ABORT\f[R], the transaction is
aborted.
.PP
On success, \f[B]pmemobj_tx_log_auto_alloc\f[R]() returns 0.
Otherwise, the transaction is aborted and an error number is returned.
.PP
On success, \f[B]pmemobj_tx_log_snapshots_max_size\f[R]() returns size
of the buffer.
On failure it returns \f[I]SIZE_MAX\f[R] and sets \f[I]errno\f[R]
appropriately.
.PP
On success, \f[B]pmemobj_tx_log_intents_max_size\f[R]() returns size of
the buffer.
On failure it returns \f[I]SIZE_MAX\f[R] and sets \f[I]errno\f[R]
appropriately.
.SH CAVEATS
.PP
Transaction flow control is governed by the \f[B]setjmp\f[R](3) and
\f[B]longjmp\f[R](3) macros, and they are used in both the macro and
function flavors of the API.
The transaction will longjmp on transaction abort.
This has one major drawback, which is described in the ISO C standard
subsection 7.13.2.1.
It says that \f[B]the values of objects of automatic storage duration
that are local to the function containing the setjmp invocation that do
not have volatile-qualified type and have been changed between the
setjmp invocation and longjmp call are indeterminate.\f[R]
.PP
The following example illustrates the issue described above.
.IP
.nf
\f[C]
int *bad_example_1 = (int *)0xBAADF00D;
int *bad_example_2 = (int *)0xBAADF00D;
int *bad_example_3 = (int *)0xBAADF00D;
int * volatile good_example = (int *)0xBAADF00D;

TX_BEGIN(pop) {
    bad_example_1 = malloc(sizeof(int));
    bad_example_2 = malloc(sizeof(int));
    bad_example_3 = malloc(sizeof(int));
    good_example = malloc(sizeof(int));

    /* manual or library abort called here */
    pmemobj_tx_abort(EINVAL);
} TX_ONCOMMIT {
    /*
     * This section is longjmp-safe
     */
} TX_ONABORT {
    /*
     * This section is not longjmp-safe
     */
    free(good_example); /* OK */
    free(bad_example_1); /* undefined behavior */
} TX_FINALLY {
    /*
     * This section is not longjmp-safe on transaction abort only
     */
    free(bad_example_2); /* undefined behavior */
} TX_END

free(bad_example_3); /* undefined behavior */
\f[R]
.fi
.PP
Objects which are not volatile-qualified, are of automatic storage
duration and have been changed between the invocations of
\f[B]setjmp\f[R](3) and \f[B]longjmp\f[R](3) (that also means within the
work section of the transaction after \f[B]TX_BEGIN\f[R]()) should not
be used after a transaction abort, or should be used with utmost care.
This also includes code after the \f[B]TX_END\f[R] macro.
.PP
\f[B]libpmemobj\f[R](7) is not cancellation-safe.
The pool will never be corrupted because of a canceled thread, but other
threads may stall waiting on locks taken by that thread.
If the application wants to use \f[B]pthread_cancel\f[R](3), it must
disable cancellation before calling any \f[B]libpmemobj\f[R](7) APIs
(see \f[B]pthread_setcancelstate\f[R](3) with
\f[B]PTHREAD_CANCEL_DISABLE\f[R]), and re-enable it afterwards.
Deferring cancellation (\f[B]pthread_setcanceltype\f[R](3) with
\f[B]PTHREAD_CANCEL_DEFERRED\f[R]) is not safe enough, because
\f[B]libpmemobj\f[R](7) internally may call functions that are specified
as cancellation points in POSIX.
.PP
\f[B]libpmemobj\f[R](7) relies on the library destructor being called
from the main thread.
For this reason, all functions that might trigger destruction (e.g.
\f[B]dlclose\f[R](3)) should be called in the main thread.
Otherwise some of the resources associated with that thread might not be
cleaned up properly.
.SH SEE ALSO
.PP
\f[B]dlclose\f[R](3), \f[B]longjmp\f[R](3),
\f[B]pmemobj_tx_add_range\f[R](3), \f[B]pmemobj_tx_alloc\f[R](3),
\f[B]pthread_setcancelstate\f[R](3), \f[B]pthread_setcanceltype\f[R](3),
\f[B]setjmp\f[R](3), \f[B]libpmemobj\f[R](7) and
\f[B]<https://pmem.io>\f[R]