/* * pocli_free -- free main context */ static void pocli_free(struct pocli *pcli) { while (pmemobj_tx_stage() != TX_STAGE_NONE) { while (pmemobj_tx_stage() != TX_STAGE_NONE) pmemobj_tx_process(); pmemobj_tx_end(); } pmemobj_close(pcli->ctx.pop); free(pcli->inbuf); free(pcli); }
/* * pocli_pmemobj_tx_strdup -- pmemobj_tx_strdup() command */ static enum pocli_ret pocli_pmemobj_tx_strdup(struct pocli_ctx *ctx, struct pocli_args *args) { if (args->argc != 4) return POCLI_ERR_ARGS; if (pmemobj_tx_stage() != TX_STAGE_WORK) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot use in stage different than TX_STAGE_WORK\n"); PMEMoid *oidp = NULL; uint64_t type_num; enum pocli_ret ret; ret = pocli_args_obj(ctx, args, 1, &oidp); if (ret) return ret; if (oidp == &ctx->root) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot use root object\n"); ret = pocli_args_number(args, 3, &type_num); if (ret) return ret; *oidp = pmemobj_tx_strdup(args->argv[2], type_num); pocli_printf(ctx, "%s(%s, %llu): off = 0x%llx uuid = 0x%llx\n", args->argv[0], args->argv[2], type_num, oidp->off, oidp->pool_uuid_lo); return ret; }
/* * pocli_pmemobj_tx_free -- pmemobj_tx_free() command */ static enum pocli_ret pocli_pmemobj_tx_free(struct pocli_ctx *ctx, struct pocli_args *args) { if (args->argc != 2) return POCLI_ERR_ARGS; if (pmemobj_tx_stage() != TX_STAGE_WORK) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot use in stage different than TX_STAGE_WORK\n"); PMEMoid *oidp = NULL; enum pocli_ret ret; ret = pocli_args_obj(ctx, args, 1, &oidp); if (ret) return ret; if (oidp == &ctx->root) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot free root object\n"); int r = pmemobj_tx_free(*oidp); if (r != POCLI_RET_OK) return pocli_err(ctx, POCLI_ERR_ARGS, "pmemobj_tx_free() failed\n"); pocli_printf(ctx, "%s(%p): off = 0x%llx uuid = 0x%llx\n", args->argv[0], oidp, oidp->off, oidp->pool_uuid_lo); return ret; }
/* * pocli_pmemobj_do_tx_alloc -- pmemobj_tx_zalloc() and pmemobj_tx_zalloc() * commands common part */ static enum pocli_ret pocli_pmemobj_do_tx_alloc(struct pocli_ctx *ctx, struct pocli_args *args, PMEMoid (*fn_alloc)(size_t size, uint64_t type_num)) { if (args->argc != 4) return POCLI_ERR_ARGS; if (pmemobj_tx_stage() != TX_STAGE_WORK) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot use in stage different than TX_STAGE_WORK\n"); PMEMoid *oidp = NULL; uint64_t type_num = 0; size_t size = 0; enum pocli_ret ret; ret = pocli_args_obj(ctx, args, 1, &oidp); if (ret) return ret; if (oidp == &ctx->root) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot allocate to root object\n"); ret = pocli_args_size(args, 2, &size); if (ret) return ret; ret = pocli_args_number(args, 3, &type_num); if (ret) return ret; *oidp = fn_alloc(size, type_num); pocli_printf(ctx, "%s(%zu, %llu): off = 0x%llx uuid = 0x%llx\n", args->argv[0], size, type_num, oidp->off, oidp->pool_uuid_lo); return ret; }
/* * pocli_pmemobj_tx_end -- pmemobj_tx_end() command */ static enum pocli_ret pocli_pmemobj_tx_end(struct pocli_ctx *ctx, struct pocli_args *args) { if (args->argc != 1) return POCLI_ERR_ARGS; if (pmemobj_tx_stage() == TX_STAGE_NONE || pmemobj_tx_stage() == TX_STAGE_WORK) return pocli_err(ctx, POCLI_ERR_ARGS, "transaction in improper stage\n"); ctx->tx_aborted = false; int ret = pmemobj_tx_end(); pocli_printf(ctx, "%s: %d\n", args->argv[0], ret); return POCLI_RET_OK; }
p& operator=(const p &rhs) noexcept { if (pmemobj_tx_stage() == TX_STAGE_WORK) pmemobj_tx_add_range_direct(this, sizeof(T)); this_type(rhs).swap(*this); return *this; }
inline void conditional_add_to_tx(const T *that) { if (pmemobj_tx_stage() != TX_STAGE_WORK) return; /* 'that' is not in any open pool */ if (!pmemobj_pool_by_ptr(that)) return; if (pmemobj_tx_add_range_direct(that, sizeof(*that))) throw transaction_error("Could not add an object to the" " transaction."); }
/* * _pobj_debug_notice -- logs notice message if used inside a transaction */ void _pobj_debug_notice(const char *api_name, const char *file, int line) { #ifdef DEBUG if (pmemobj_tx_stage() != TX_STAGE_NONE) { if (file) LOG(4, "Notice: non-transactional API" " used inside a transaction (%s in %s:%d)", api_name, file, line); else LOG(4, "Notice: non-transactional API" " used inside a transaction (%s)", api_name); } #endif /* DEBUG */ }
/* * pocli_pmemobj_tx_commit -- pmemobj_tx_commit() command */ static enum pocli_ret pocli_pmemobj_tx_commit(struct pocli_ctx *ctx, struct pocli_args *args) { if (args->argc != 1) return POCLI_ERR_ARGS; if (pmemobj_tx_stage() != TX_STAGE_WORK) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot use in stage different than TX_STAGE_WORK\n"); pmemobj_tx_commit(); pocli_printf(ctx, "%s\n", args->argv[0]); return POCLI_RET_OK; }
void delete_persistent(typename detail::pp_if_not_array<T>::type ptr) { if (pmemobj_tx_stage() != TX_STAGE_WORK) throw transaction_scope_error( "refusing to free " "memory outside of transaction scope"); if (ptr == nullptr) return; /* * At this point, everything in the object should be tracked * and reverted on transaction abort. */ ptr->T::~T(); if (pmemobj_tx_free(*ptr.raw_ptr()) != 0) throw transaction_alloc_error("failed to delete " "persistent memory object"); }
/* * pocli_pmemobj_tx_abort -- pmemobj_tx_abort() command */ static enum pocli_ret pocli_pmemobj_tx_abort(struct pocli_ctx *ctx, struct pocli_args *args) { if (args->argc != 2) return POCLI_ERR_ARGS; if (pmemobj_tx_stage() != TX_STAGE_WORK) return pocli_err(ctx, POCLI_ERR_ARGS, "cannot use in stage different than TX_STAGE_WORK\n"); int err; int count = sscanf(args->argv[1], "%d", &err); if (count != 1) return POCLI_ERR_PARS; ctx->tx_aborted = true; free(args); pmemobj_tx_abort(err); pocli_printf(ctx, "pmemobj_tx_abort: %d", err); return POCLI_RET_OK; }
typename detail::pp_if_not_array<T>::type make_persistent(Args &&... args) { if (pmemobj_tx_stage() != TX_STAGE_WORK) throw transaction_scope_error( "refusing to allocate " "memory outside of transaction scope"); persistent_ptr<T> ptr = pmemobj_tx_alloc(sizeof(T), detail::type_num<T>()); if (ptr == nullptr) throw transaction_alloc_error("failed to allocate " "persistent memory object"); try { new (ptr.get()) T(args...); } catch (...) { pmemobj_tx_free(*ptr.raw_ptr()); throw; } return ptr; }
/* * parse_stage -- return proper string variable referring to transaction state */ static const char * parse_stage() { int st = pmemobj_tx_stage(); const char *stage = ""; switch (st) { case TX_STAGE_NONE: stage = "TX_STAGE_NONE"; break; case TX_STAGE_WORK: stage = "TX_STAGE_WORK"; break; case TX_STAGE_ONCOMMIT: stage = "TX_STAGE_ONCOMMIT"; break; case TX_STAGE_ONABORT: stage = "TX_STAGE_ONABORT"; break; case TX_STAGE_FINALLY: stage = "TX_STAGE_FINALLY"; break; } return stage; }
int main(int argc, char *argv[]) { START(argc, argv, "obj_tx_flow"); if (argc != 2) FATAL("usage: %s [file]", argv[0]); PMEMobjpool *pop; if ((pop = pmemobj_create(argv[1], LAYOUT_NAME, PMEMOBJ_MIN_POOL, S_IWUSR | S_IRUSR)) == NULL) FATAL("!pmemobj_create"); int a = 0; int b = 0; int c = 0; TX_BEGIN(pop) { a = TEST_VALUE_A; } TX_ONCOMMIT { ASSERT(a == TEST_VALUE_A); b = TEST_VALUE_B; } TX_ONABORT { /* not called */ a = TEST_VALUE_B; } TX_FINALLY { ASSERT(b == TEST_VALUE_B); c = TEST_VALUE_C; } TX_END ASSERT(a == TEST_VALUE_A); ASSERT(b == TEST_VALUE_B); ASSERT(c == TEST_VALUE_C); a = 0; b = 0; c = 0; TX_BEGIN(pop) { a = TEST_VALUE_A; pmemobj_tx_abort(EINVAL); a = TEST_VALUE_B; } TX_ONCOMMIT { /* not called */ a = TEST_VALUE_B; } TX_ONABORT { ASSERT(a == TEST_VALUE_A); b = TEST_VALUE_B; } TX_FINALLY { ASSERT(b == TEST_VALUE_B); c = TEST_VALUE_C; } TX_END ASSERT(a == TEST_VALUE_A); ASSERT(b == TEST_VALUE_B); ASSERT(c == TEST_VALUE_C); a = 0; b = 0; c = 0; TX_BEGIN(pop) { TX_BEGIN(pop) { a = TEST_VALUE_A; } TX_ONCOMMIT { ASSERT(a == TEST_VALUE_A); b = TEST_VALUE_B; } TX_END } TX_ONCOMMIT { c = TEST_VALUE_C; } TX_END ASSERT(a == TEST_VALUE_A); ASSERT(b == TEST_VALUE_B); ASSERT(c == TEST_VALUE_C); a = 0; b = 0; c = 0; TX_BEGIN(pop) { a = TEST_VALUE_C; TX_BEGIN(pop) { a = TEST_VALUE_A; pmemobj_tx_abort(EINVAL); a = TEST_VALUE_B; } TX_ONCOMMIT { /* not called */ a = TEST_VALUE_C; } TX_ONABORT { ASSERT(a == TEST_VALUE_A); b = TEST_VALUE_B; } TX_FINALLY { ASSERT(b == TEST_VALUE_B); c = TEST_VALUE_C; } TX_END a = TEST_VALUE_B; } TX_ONCOMMIT { /* not called */ ASSERT(a == TEST_VALUE_A); c = TEST_VALUE_C; } TX_ONABORT { ASSERT(a == TEST_VALUE_A); ASSERT(b == TEST_VALUE_B); ASSERT(c == TEST_VALUE_C); a = TEST_VALUE_B; } TX_FINALLY { ASSERT(a == TEST_VALUE_B); b = TEST_VALUE_A; } TX_END ASSERT(a == TEST_VALUE_B); ASSERT(b == TEST_VALUE_A); ASSERT(c == TEST_VALUE_C); a = 0; b = 0; c = 0; pmemobj_tx_begin(pop, NULL, TX_LOCK_NONE); pmemobj_tx_abort(EINVAL); ASSERT(pmemobj_tx_stage() == TX_STAGE_ONABORT); a = TEST_VALUE_A; pmemobj_tx_end(); ASSERT(a == TEST_VALUE_A); pmemobj_close(pop); DONE(NULL); }