Provided by: libpmemobj-dev_1.4.1-0ubuntu1~18.04.1_amd64 bug

NAME

       pmemobj_tx_stage(),

       pmemobj_tx_begin(),  pmemobj_tx_lock(), pmemobj_tx_abort(), pmemobj_tx_commit(), pmemobj_tx_end(), pmemo‐
       bj_tx_errno(), pmemobj_tx_process(),

       TX_BEGIN_PARAM(), TX_BEGIN_CB(), TX_BEGIN(), TX_ONABORT, TX_ONCOMMIT, TX_FINALLY, TX_END -  transactional
       object manipulation

SYNOPSIS

              #include <libpmemobj.h>

              enum 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);
              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

DESCRIPTION

       The  non-transactional  functions  and  macros  described in pmemobj_alloc(3), pmemobj_list_insert(3) and
       POBJ_LIST_HEAD(3) only guarantee the atomicity of a single operation on an object.  In case of more  com‐
       plex  changes  involving multiple operations on an object, or allocation and modification of multiple ob‐
       jects, data consistency and fail-safety may be provided only by using atomic transactions.

       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  uncom‐
       pleted transaction will be rolled back, restoring the consistent state of the memory pool from the moment
       when the transaction was started.

       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  respon‐
       sibility of the application to implement a proper thread synchronization mechanism.

       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 in‐
       terruption of the entire transaction.

       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.

       Please see the CAVEATS section below for known limitations of the transactional API.

       The  pmemobj_tx_stage()  function returns the current transaction stage for a thread.  Stages are changed
       only by the pmemobj_tx_*() functions.  Transaction stages are defined as follows:

       • TX_STAGE_NONE - no open transaction in this thread

       • TX_STAGE_WORK - transaction in progress

       • TX_STAGE_ONCOMMIT - successfully committed

       • TX_STAGE_ONABORT - starting the transaction failed or transaction aborted

       • TX_STAGE_FINALLY - ready for clean up

       The pmemobj_tx_begin() 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 env 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 setjmp(3) macro.

       A  new  transaction  may be started only if the current stage is TX_STAGE_NONE or TX_STAGE_WORK.  If suc‐
       cessful, the transaction stage changes to TX_STAGE_WORK.  Otherwise, the stage is changed to TX_STAGE_ON‐
       ABORT.

       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:

       • TX_PARAM_NONE, used as a termination marker.  No following value.

       • TX_PARAM_MUTEX, followed by one value, a pmem-resident PMEMmutex

       • TX_PARAM_RWLOCK, followed by one value, a pmem-resident PMEMrwlock

       • TX_PARAM_CB, followed by two values: a callback function of type pmemobj_tx_callback, and a void point‐
         er

       Using  TX_PARAM_MUTEX or TX_PARAM_RWLOCK causes the specified lock to be acquired at the beginning of the
       transaction.  TX_PARAM_RWLOCK acquires the lock for writing.  It is  guaranteed  that  pmemobj_tx_begin()
       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.

       TX_PARAM_CB registers the specified callback function to be executed  at  each  transaction  stage.   For
       TX_STAGE_WORK,  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 stage parameter will be set to TX_STAGE_NONE.  pmemobj_tx_callback must be compatible with:

       void func(PMEMobjpool *pop, enum pobj_tx_stage stage, void *arg)

       pop is a pool identifier used in pmemobj_tx_begin(), stage is a current transaction stage and arg is  the
       second  parameter of TX_PARAM_CB.  Without considering transaction nesting, this mechanism can be consid‐
       ered an alternative method for executing code between stages (instead of TX_ONCOMMIT,  TX_ONABORT,  etc).
       However, there are 2 significant differences when nested transactions are used:

       • The  registered  function is executed only in the outermost transaction, even if registered in an inner
         transaction.

       • There can be only one callback in the entire transaction, that is, the callback cannot be changed in an
         inner transaction.

       Note that TX_PARAM_CB does not replace the TX_ONCOMMIT, TX_ONABORT, etc.  macros.  They can be  used  to‐
       gether: the callback will be executed before a TX_ONCOMMIT, TX_ONABORT, etc.  section.

       TX_PARAM_CB  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.

       The  pmemobj_tx_lock()  function  acquires  the  lock  lockp of type lock_type and adds it to the current
       transaction.  lock_type may be TX_LOCK_MUTEX or TX_LOCK_RWLOCK; lockp must be of type PMEMmutex or PMEMr‐
       wlock, respectively.  If lock_type is TX_LOCK_RWLOCK the lock is acquired for writing.  If  the  lock  is
       not successfully acquired, the stage is changed to TX_STAGE_ONABORT.  This function must be called during
       TX_STAGE_WORK.

       pmemobj_tx_abort() aborts the current transaction and causes a transition to TX_STAGE_ONABORT.  If errnum
       is  equal  to  0,  the  transaction error code is set to ECANCELED; otherwise, it is set to errnum.  This
       function must be called during TX_STAGE_WORK.

       The pmemobj_tx_commit() function commits  the  current  open  transaction  and  causes  a  transition  to
       TX_STAGE_ONCOMMIT.  If called in the context of the outermost transaction, all the changes may be consid‐
       ered as durably written upon successful completion.  This function must be called during TX_STAGE_WORK.

       The pmemobj_tx_end() function performs a cleanup of the current transaction.  If called in the context of
       the  outermost transaction, it releases all the locks acquired by pmemobj_tx_begin() for outer and nested
       transactions.  If called in the context of a nested transaction, it returns to the context of  the  outer
       transaction  in  TX_STAGE_WORK, without releasing any locks.  The pmemobj_tx_end() function can be called
       during TX_STAGE_NONE if transitioned to  this  stage  using  pmemobj_tx_process().   If  not  already  in
       TX_STAGE_NONE,  it causes the transition to TX_STAGE_NONE.  pmemobj_tx_end must always be called for each
       pmemobj_tx_begin(), even if starting the transaction failed.  This function must  not  be  called  during
       TX_STAGE_WORK.

       The pmemobj_tx_errno() function returns the error code of the last transaction.

       The  pmemobj_tx_process() function performs the actions associated with the current stage of the transac‐
       tion, 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 pmemobj_tx_stage().   pmemobj_tx_process()  performs  the  following
       transitions in the transaction stage flow:

       • TX_STAGE_WORK -> TX_STAGE_ONCOMMITTX_STAGE_ONABORT -> TX_STAGE_FINALLYTX_STAGE_ONCOMMIT -> TX_STAGE_FINALLYTX_STAGE_FINALLY -> TX_STAGE_NONETX_STAGE_NONE -> TX_STAGE_NONE

       pmemobj_tx_process() must not be called after calling pmemobj_tx_end() for the outermost transaction.

       In addition to the above API, libpmemobj(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:

              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 */

              TX_BEGIN_PARAM(PMEMobjpool *pop, ...)
              TX_BEGIN_CB(PMEMobjpool *pop, cb, arg, ...)
              TX_BEGIN(PMEMobjpool *pop)

       The TX_BEGIN_PARAM(), TX_BEGIN_CB() and TX_BEGIN() macros start a new transaction  in  the  same  way  as
       pmemobj_tx_begin(),  except  that instead of the environment buffer provided by a caller, they set up the
       local jmp_buf buffer and use it to catch the transaction abort.  The TX_BEGIN() macro starts  a  transac‐
       tion  without  any  options.   TX_BEGIN_PARAM  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.
       TX_BEGIN_CB is just a wrapper around TX_BEGIN_PARAM that validates the callback signature.  (For compati‐
       bility  there is also a TX_BEGIN_LOCK macro, which is an alias for TX_BEGIN_PARAM).  Each of these macros
       must be followed by a block of code with all the operations that are to be performed atomically.

       The TX_ONABORT macro starts a block of code that will be executed only if starting the transaction  fails
       due  to an error in pmemobj_tx_begin(), 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 TX_ONABORT section, the application can define the POBJ_TX_CRASH_ON_NO_ONABORT macro  be‐
       fore inclusion of <libpmemobj.h>.  This provides a default TX_ONABORT section which just calls abort(3).

       The TX_ONCOMMIT macro starts a block of code that will be executed only if the transaction is successful‐
       ly  committed, which means that the execution of code in the TX_BEGIN() block has not been interrupted by
       an error or by a call to pmemobj_tx_abort().  This block is optional.

       The TX_FINALLY macro starts a block of code that will be executed regardless of whether  the  transaction
       is committed or aborted.  This block is optional.

       The  TX_END  macro  cleans  up  and closes the transaction started by the TX_BEGIN() / TX_BEGIN_PARAM() /
       TX_BEGIN_CB() macros.  It is mandatory to terminate each transaction with this macro.  If the transaction
       was aborted, errno is set appropriately.

RETURN VALUE

       The pmemobj_tx_stage() function returns the stage of the current transaction stage for a thread.

       On success, pmemobj_tx_begin() returns 0.  Otherwise, an error number is returned.

       The pmemobj_tx_begin() and pmemobj_tx_lock() functions return zero if lockp is successfully added to  the
       transaction.  Otherwise, an error number is returned.

       The pmemobj_tx_abort() and pmemobj_tx_commit() functions return no value.

       The  pmemobj_tx_end() function returns 0 if the transaction was successful.  Otherwise it returns the er‐
       ror code set by pmemobj_tx_abort().  Note that pmemobj_tx_abort() can be called  internally  by  the  li‐
       brary.

       The pmemobj_tx_errno() function returns the error code of the last transaction.

       The pmemobj_tx_process() function returns no value.

CAVEATS

       Transaction  flow  control  is governed by the setjmp(3) and longjmp(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 the val‐
       ues of objects of automatic storage duration that are local to the function containing the setjmp invoca‐
       tion that do not have volatile-qualified type and have been changed between  the  setjmp  invocation  and
       longjmp call are indeterminate.

       The following example illustrates the issue described above.

              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 */

       Objects which are not volatile-qualified, are of automatic storage duration and have been changed between
       the  invocations  of setjmp(3) and longjmp(3) (that also means within the work section of the transaction
       after TX_BEGIN()) should not be used after a transaction abort, or should be used with utmost care.  This
       also includes code after the TX_END macro.

       libpmemobj(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
       pthread_cancel(3), it must disable cancellation before calling any libpmemobj(7) APIs  (see  pthread_set‐
       cancelstate(3)  with  PTHREAD_CANCEL_DISABLE),  and  re-enable  it  afterwards.   Deferring  cancellation
       (pthread_setcanceltype(3) with PTHREAD_CANCEL_DEFERRED) is not safe enough, because libpmemobj(7)  inter‐
       nally may call functions that are specified as cancellation points in POSIX.

       libpmemobj(7)  relies  on the library destructor being called from the main thread.  For this reason, all
       functions that might trigger destruction (e.g.  dlclose(3)) should be called in the main thread.   Other‐
       wise some of the resources associated with that thread might not be cleaned up properly.

SEE ALSO

       dlclose(3),    longjmp(3),   pmemobj_tx_add_range(3),   pmemobj_tx_alloc(3),   pthread_setcancelstate(3),
       pthread_setcanceltype(3), setjmp(3), libpmemobj(7) and <http://pmem.io>

PMDK - pmemobj API version 2.3                     2018-05-21                                PMEMOBJ_TX_BEGIN(3)