POBJ_RESERVE_NEW(3)
π PMEMOBJ_ACTION β bestie edition π
okay so this is a page from the PMDK Programmer's Manual, section 3, and it's all about PMEMOBJ_ACTION β aka the delayed atomicity actions situation. ngl this one is β¨ EXPERIMENTAL β¨ so read the whole thing before you go feral in production, okay? π«Ά
β¨ NAME β¨
pmemobj_reserve(), pmemobj_xreserve(), pmemobj_defer_free(), pmemobj_set_value(), pmemobj_publish(), pmemobj_tx_publish(), pmemobj_tx_xpublish(), pmemobj_cancel(), POBJ_RESERVE_NEW(), POBJ_RESERVE_ALLOC(), POBJ_XRESERVE_NEW(), POBJ_XRESERVE_ALLOC() β Delayed atomicity actions (EXPERIMENTAL)
like literally all of them. the whole girl group. π
β¨ SYNOPSIS β¨
#include <libpmemobj.h>
PMEMoid pmemobj_reserve(PMEMobjpool *pop, struct pobj_action *act,
size_t size, uint64_t type_num); (EXPERIMENTAL)
PMEMoid pmemobj_xreserve(PMEMobjpool *pop, struct pobj_action *act,
size_t size, uint64_t type_num, uint64_t flags); (EXPERIMENTAL)
void pmemobj_defer_free(PMEMobjpool *pop, PMEMoid oid, struct pobj_action *act);
void pmemobj_set_value(PMEMobjpool *pop, struct pobj_action *act,
uint64_t *ptr, uint64_t value); (EXPERIMENTAL)
int pmemobj_publish(PMEMobjpool *pop, struct pobj_action *actv,
size_t actvcnt); (EXPERIMENTAL)
int pmemobj_tx_publish(struct pobj_action *actv, size_t actvcnt); (EXPERIMENTAL)
int pmemobj_tx_xpublish(struct pobj_action *actv, size_t actvcnt, uint64_t flags); (EXPERIMENTAL)
void pmemobj_cancel(PMEMobjpool *pop, struct pobj_action *actv,
size_t actvcnt); (EXPERIMENTAL)
POBJ_RESERVE_NEW(pop, t, act) (EXPERIMENTAL)
POBJ_RESERVE_ALLOC(pop, t, size, act) (EXPERIMENTAL)
POBJ_XRESERVE_NEW(pop, t, act, flags) (EXPERIMENTAL)
POBJ_XRESERVE_ALLOC(pop, t, size, act, flags) (EXPERIMENTAL)
β¨ DESCRIPTION β¨
okay so every function we've talked about so far? immediate effect on the persistent state of the pool. the cost of keeping things fail-safe is paid outright, and most importantly, it's paid in the calling thread. which lowkey makes implementing algorithms with relaxed consistency guarantees hard β not even difficult, honestly just straight up impossible. π
so here's the glow-up: this set of functions introduces a mechanism that lets you delay the persistent publication of a set of prepared actions to an arbitrary moment in time during your program's execution. main character scheduling energy. π
the publication is fail-safe atomic across the entire collection of actions. and if your program exits without publishing the actions, or the actions get canceled, then any resources those actions reserved are released and placed back in the pool. no leaks, we love to see it. πΈ
a single action is represented by a single struct pobj_action. functions that create actions take that structure by pointer, whereas functions that publish actions take an array of actions plus the size of the array. the actions can be created and published from different threads β it's giving flexible. when creating actions, the act argument must be non-NULL and point to a struct pobj_action; the structure will be populated by the function and must not be modified or deallocated until after publishing. don't touch it, bestie, it's busy. π
the pmemobj_reserve() functions perform a transient reservation of an object. behaves similarly to pmemobj_alloc(3), but performs no modification to the persistent state. the object returned by this function can be freely modified without worrying about fail-safe atomicity until the object has been published. any modifications of the object must be manually persisted, just like in the case of the atomic API. iykyk. β¨
pmemobj_xreserve() is equivalent to pmemobj_reserve(), but with an additional flags argument that's a bitmask of these values:
POBJ_XALLOC_ZEROβ zero the allocated object (and persist it)POBJ_CLASS_ID(class_id)β allocate an object from the allocation class class_id. the class id cannot be 0.POBJ_ARENA_ID(arena_id)β allocate an object from the arena specified by arena_id. the arena must exist, otherwise, the behavior is undefined. if arena_id is equal0, then the arena assigned to the current thread will be used.
pmemobj_defer_free() creates a deferred free action, meaning the provided object will be freed when the action is published. calling this function with a NULL OID is invalid and causes undefined behavior. so likeβ¦ don't. π«
the pmemobj_set_value function prepares an action that, once published, will modify the memory location pointed to by ptr to value.
the pmemobj_publish function publishes the provided set of actions. the publication is fail-safe atomic. once done, the persistent state will reflect the changes contained in the actions. slay. π
the pmemobj_tx_publish function moves the provided actions to the scope of the transaction in which it's called. only object reservations are supported in transactional publish. once done, the reserved objects will follow normal transactional semantics. can only be called during TX_STAGE_WORK.
the pmemobj_tx_xpublish() function behaves exactly the same as pmemobj_tx_publish() when flags equals zero. flags is a bitmask of these values:
POBJ_XPUBLISH_NO_ABORTβ if the function does not end successfully, do not abort the transaction.
the pmemobj_cancel function releases any resources held by the provided set of actions and invalidates all actions.
the POBJ_RESERVE_NEW macro is a typed variant of pmemobj_reserve. the size of the reservation is determined from the provided type t.
the POBJ_RESERVE_ALLOC macro is a typed variant of pmemobj_reserve. the size of the reservation is user-provided.
the POBJ_XRESERVE_NEW and the POBJ_XRESERVE_ALLOC macros are equivalent to POBJ_RESERVE_NEW and the POBJ_RESERVE_ALLOC, but with an additional flags argument defined for pmemobj_xreserve(). π
β¨ EXAMPLES β¨
the following code shows atomic append of two objects into a singly linked list. look at her go. π₯
struct list_node {
int value;
PMEMoid next;
};
/* statically allocate the array of actions */
struct pobj_action actv[4];
/* reserve, populate and persist the first object */
PMEMoid tail = pmemobj_reserve(pop, &actv[0], sizeof(struct list_node), 0);
if (TOID_IS_NULL(tail))
return -1;
D_RW(tail)->value = 1;
D_RW(tail)->next = OID_NULL;
pmemobj_persist(pop, D_RW(tail), sizeof(struct list_node));
/* reserve, populate and persist the second object */
PMEMoid head = pmemobj_reserve(pop, &actv[1], sizeof(struct list_node), 0);
if (TOID_IS_NULL(head))
return -1;
D_RW(head)->value = 2;
D_RW(head)->next = tail;
pmemobj_persist(pop, D_RW(head), sizeof(struct list_node));
/* create actions to set the PMEMoid to the new values */
pmemobj_set_value(pop, &actv[2], &D_RO(root)->head.pool_uuid_lo, head.pool_uuid_lo);
pmemobj_set_value(pop, &actv[3], &D_RO(root)->head.off, head.off);
/* atomically publish the above actions */
pmemobj_publish(pop, actv, 4);
β¨ RETURN VALUE β¨
on success, pmemobj_reserve() functions return a handle to the newly reserved object. otherwise an OID_NULL is returned. π
on success, pmemobj_tx_publish() returns 0. otherwise, the transaction is aborted, the stage is changed to TX_STAGE_ONABORT and errno is set appropriately.
on success, pmemobj_tx_xpublish() returns 0. otherwise, the error number is returned, errno is set and when flags do not contain POBJ_XPUBLISH_NO_ABORT, the transaction is aborted.
on success, pmemobj_publish() returns 0. otherwise, returns -1 and errno is set appropriately.
β¨ SEE ALSO β¨
pmemobj_alloc(3), pmemobj_tx_alloc(3), libpmemobj(7) and https://pmem.io
and that's the whole page bestie π this is PMDK, dated 2025-07-24, and if you made it all the way down here you're genuinely iconic. go forth and publish your actions atomically. periodt. πβ¨π¦
π¦ where this page comes from
- fedora 43: libpmemobj-devel 2.1.0-4.fc43 Β· BSD-3-Clause
- π the page's license: BSD-3-Clause
- Copyright 2017-2019, Intel Corporation
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms