OID_IS_NULL(3)
π OID_EQUALS(3) β the PMDK Programmer's Manual glow-up β¨
okay bestie, so this page lives in the PMDK Programmer's Manual, and it's all about the little functions that let you translate between object addresses, object handles, oids and type numbers π¦ no cap, it's giving "address book for your persistent memory pool" energy.
πΈ NAME πΈ
OID_IS_NULL(), OID_EQUALS(), pmemobj_direct(), pmemobj_oid(), pmemobj_type_num(), pmemobj_pool_by_oid(), pmemobj_pool_by_ptr() β these are the functions that let you do mapping operations between object addresses, object handles, oids or type numbers π
β¨ SYNOPSIS β¨
#include <libpmemobj.h>
OID_IS_NULL(PMEMoid oid)
OID_EQUALS(PMEMoid lhs, PMEMoid rhs)
void *pmemobj_direct(PMEMoid oid);
PMEMoid pmemobj_oid(const void *addr);
uint64_t pmemobj_type_num(PMEMoid oid);
PMEMobjpool *pmemobj_pool_by_oid(PMEMoid oid);
PMEMobjpool *pmemobj_pool_by_ptr(const void *addr);
void *pmemobj_volatile(PMEMobjpool *pop, struct pmemvlt *vlt,
size_t size, void *ptr,
int (*constr)(void *ptr, void *arg), void *arg); (EXPERIMENTAL)
π DESCRIPTION π
so, every single object chillin' inside a persistent memory pool gets represented by an object handle of type PMEMoid. and in practice? that handle is literally a unique Object IDentifier (OID) of global scope, which means two objects from different pools will never have the same OID. iconic, honestly. π
the special OID_NULL macro defines a NULL-like handle that doesn't represent any object at all. and the size of a single object is limited by PMEMOBJ_MAX_ALLOC_SIZE β so an allocation with a requested size greater than this value will fail. sad but true π
now, an OID cannot be used as a direct pointer to an object, okay? every time the program tries to read or write object data, it must obtain the current memory address of the object by converting its OID into a pointer. that's just the rules, bestie.
in contrast to the memory address, the OID value for a given object does not change during the life of an object (except for realloc), and it stays valid after closing and reopening the pool. so if an object contains a reference to another persistent object β like, say, to build some kind of linked data structure β that reference must be an OID and not a memory address. periodt. π
here's the squad, one by one:
pmemobj_direct()returns a pointer to thePMEMoidobject with handle oid.pmemobj_oid()returns aPMEMoidhandle to the object pointed to by addr.pmemobj_type_num()returns the type number of thePMEMoidobject with handle oid.pmemobj_pool_by_oid()returns aPMEMobjpool*handle to the pool containing thePMEMoidobject with handle oid.pmemobj_pool_by_ptr()returns aPMEMobjpool*handle to the pool containing the address addr.
at the time of allocation (or reallocation), each object may be assigned a number representing its type. such a type number may be used to arrange the persistent objects based on their actual user-defined structure type β which lowkey facilitates implementation of a simple run-time type safety mechanism. this also allows iterating through all the objects of a given type that are stored in the persistent memory pool. see pmemobj_first(3) for more info π«Ά
the OID_IS_NULL() macro checks if PMEMoid represents a NULL object.
the OID_EQUALS() macro compares two PMEMoid objects. (yes bestie, that's literally the main character of this page π)
π§ the volatile/transient thing (EXPERIMENTAL zone)
for special cases where volatile (transient) variables need to be stored on persistent memory, there's a mechanism composed of the struct pmemvlt type and the pmemobj_volatile() function. to use it, the struct pmemvlt needs to be placed in the neighborhood of the transient data region. the PMEMvlt macro can be used to construct such a region. π
the struct pmemvlt must be zeroed prior to use. easy enough β you can do it in an object constructor or in a transaction directly after an allocation. when the pmemobj_volatile() function is called on a struct pmemvlt, it will return the pointer to the data and it will ensure that the provided constructor function is called exactly once in the current instance of the pmemobj pool. the constructor is called with the ptr pointer to the data, and this function will return the same pointer if the constructor returns 0, otherwise NULL is returned.
the size argument must accurately describe the total size of the volatile memory region that will be accessed. calling pmemobj_volatile() on the same region with different sizes is undefined behavior π¨ so don't do that, fr fr.
for this mechanism to be effective, all accesses to transient variables must go through it β otherwise there's a risk of the constructor not being called on the first load. π¬
maintaining transient state on persistent memory is challenging, due to difficulties with dynamic resources acquisition and subsequent resource release. for example, one needs to consider what happens with volatile state of an object which is being freed inside of a transaction, especially with regards to the possibility of an abort. it's generally recommended to entirely separate the persistent and transient states, and when it's not possible, to only store types which do not require lifecycle management (i.e., primitive types) inside of volatile regions. π
π RETURN VALUE π
- the
pmemobj_direct()function returns a pointer to the object represented by oid. if oid isOID_NULL,pmemobj_direct()returnsNULL. - the
pmemobj_oid()function returns aPMEMoidhandle to the object pointed to by addr. if addr is not from within a pmemobj pool,OID_NULLis returned. if addr is not the start of an object (does not point to the beginning of a valid allocation), the resultingPMEMoidcan be safely used only with:pmemobj_pool_by_oid()pmemobj_direct()pmemobj_tx_add_range(3)
- the
pmemobj_type_num()function returns the type number of the object represented by oid. - the
pmemobj_pool_by_oid()function returns a handle to the pool that contains the object represented by oid. if the pool is not open or oid isOID_NULL,pmemobj_pool_by_oid()returnsNULL. - the
pmemobj_pool_by_ptr()function returns a handle to the pool that contains the address, orNULLif the address does not belong to any open pool.
bestie, note that the "safely used only with" list is a whole only β it's not a suggestion, it's the vibe check you must pass π
β NOTES β
for performance reasons, the pmemobj_direct() function is inlined by default. if you want to use the non-inlined variant of pmemobj_direct(), define PMEMOBJ_DIRECT_NON_INLINE prior to the #include of <libpmemobj.h> β either with #define or with the -D option to the compiler. π οΈ
π₯ EXAMPLES π₯
the following code shows how to store transient variables on persistent memory.
struct my_data {
PMEMvlt(uint64_t) foo;
uint64_t bar;
};
int
my_data_constructor(void *ptr, void *arg)
{
uint64_t *foo = ptr;
*foo = 0;
return 0;
}
PMEMobjpool *pop = ...;
struct my_data *data = D_RW(...);
uint64_t *foo = pmemobj_volatile(pop, &data->foo.vlt, &data->foo.value,
my_data_constructor, NULL);
assert(*foo == 0);
like, look at that constructor returning 0 and that assert(*foo == 0) hitting different πβ¨ ate and left no crumbs.
π SEE ALSO π
libpmemobj(7) and https://pmem.io
okay that's the whole page, you're officially caught up π«Ά this was the PMDK Programmer's Manual, page OID_EQUALS(3), brought to you by PMDK - and dated 2025-07-24 β go forth and map those oids, bestie ππ¦πΈ
π¦ 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-2018, Intel Corporation
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms