POBJ_LIST_MOVE_ELEMENT_TAIL(3)

πŸŽ€ NAME πŸŽ€

okay bestie, gather round, because this page is a whole toolbox of macros from the PMDK Programmer's Manual β€” and it's literally called type-safe non-transactional persistent atomic lists πŸ’…βœ¨

here's the full squad, no cap, nobody left out:

  • POBJ_LIST_HEAD(), POBJ_LIST_ENTRY(), POBJ_LIST_FIRST(), POBJ_LIST_LAST(), POBJ_LIST_EMPTY(), POBJ_LIST_NEXT(), POBJ_LIST_PREV()
  • POBJ_LIST_FOREACH(), POBJ_LIST_FOREACH_REVERSE()
  • POBJ_LIST_INSERT_HEAD(), POBJ_LIST_INSERT_TAIL(), POBJ_LIST_INSERT_AFTER(), POBJ_LIST_INSERT_BEFORE(), POBJ_LIST_INSERT_NEW_HEAD(), POBJ_LIST_INSERT_NEW_TAIL(), POBJ_LIST_INSERT_NEW_AFTER(), POBJ_LIST_INSERT_NEW_BEFORE()
  • POBJ_LIST_REMOVE(), POBJ_LIST_REMOVE_FREE()
  • POBJ_LIST_MOVE_ELEMENT_HEAD(), POBJ_LIST_MOVE_ELEMENT_TAIL(), POBJ_LIST_MOVE_ELEMENT_AFTER(), POBJ_LIST_MOVE_ELEMENT_BEFORE()

and what are they, you ask? a type-safe non-transactional persistent atomic list situation. iconic. πŸ‘‘


✨ SYNOPSIS ✨

#include <libpmemobj.h>

POBJ_LIST_HEAD(HEADNAME, TYPE)
POBJ_LIST_ENTRY(TYPE)
POBJ_LIST_FIRST(POBJ_LIST_HEAD *head)
POBJ_LIST_LAST(POBJ_LIST_HEAD *head, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_EMPTY(POBJ_LIST_HEAD *head)
POBJ_LIST_NEXT(TOID elm, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_PREV(TOID elm, POBJ_LIST_ENTRY FIELD)

POBJ_LIST_FOREACH(TOID var, POBJ_LIST_HEAD *head, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_FOREACH_REVERSE(TOID var, POBJ_LIST_HEAD *head, POBJ_LIST_ENTRY FIELD)

POBJ_LIST_INSERT_HEAD(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID elm, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_INSERT_TAIL(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID elm, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_INSERT_AFTER(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID listelm, TOID elm, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_INSERT_BEFORE(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID listelm, TOID elm, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_INSERT_NEW_HEAD(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    POBJ_LIST_ENTRY FIELD, size_t size,
    pmemobj_constr constructor, void *arg)
POBJ_LIST_INSERT_NEW_TAIL(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    POBJ_LIST_ENTRY FIELD, size_t size,
    pmemobj_constr constructor, void *arg)
POBJ_LIST_INSERT_NEW_AFTER(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID listelm, POBJ_LIST_ENTRY FIELD, size_t size,
    pmemobj_constr constructor, void *arg)
POBJ_LIST_INSERT_NEW_BEFORE(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID listelm, POBJ_LIST_ENTRY FIELD, size_t size,
    pmemobj_constr constructor, void *arg)

POBJ_LIST_REMOVE(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID elm, POBJ_LIST_ENTRY FIELD)
POBJ_LIST_REMOVE_FREE(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    TOID elm, POBJ_LIST_ENTRY FIELD)

POBJ_LIST_MOVE_ELEMENT_HEAD(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    POBJ_LIST_HEAD *head_new, TOID elm, POBJ_LIST_ENTRY FIELD,
    POBJ_LIST_ENTRY field_new)
POBJ_LIST_MOVE_ELEMENT_TAIL(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    POBJ_LIST_HEAD *head_new, TOID elm, POBJ_LIST_ENTRY FIELD,
    POBJ_LIST_ENTRY field_new)
POBJ_LIST_MOVE_ELEMENT_AFTER(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    POBJ_LIST_HEAD *head_new, TOID listelm, TOID elm,
    POBJ_LIST_ENTRY FIELD, POBJ_LIST_ENTRY field_new)
POBJ_LIST_MOVE_ELEMENT_BEFORE(PMEMobjpool *pop, POBJ_LIST_HEAD *head,
    POBJ_LIST_HEAD *head_new, TOID listelm, TOID elm,
    POBJ_LIST_ENTRY FIELD, POBJ_LIST_ENTRY field_new)

πŸ’– DESCRIPTION πŸ’–

okay so. these macros define and operate on a type-safe persistent atomic circular doubly linked list πŸŒ€ β€” and that list is made up of a set of persistent objects of a well-known type.

here's the glow-up vs. the functions from the previous section: these macros give you type enforcement. how? by making you declare the type of the objects stored in a given list, and by flat-out not allowing you to mix objects of different types in a single list. no chaos, only vibes. πŸ’…

the functionality and semantics of these macros? very similar to the circular queues defined in queue(3). iykyk.

the majority of these macros need you to hand over two things: the handle to the memory pool pop, and the name of the field in your user-defined structure. that field has to be of type POBJ_LIST_ENTRY, and it's the lil connector that links the elements together in the list. 🫢

a list is headed by a structure defined by the POBJ_LIST_HEAD() macro. this structure holds an object handle of the first element on the list. the elements are doubly linked, so any arbitrary element can be removed without needing to traverse the whole list β€” obsessed, truly. new elements can be added before or after an existing element, at the head of the list, or at the end of the list. and a list can be traversed in either direction. 🌈

a POBJ_LIST_HEAD structure is declared like this:

#define POBJ_LIST_HEAD(HEADNAME, TYPE)
struct HEADNAME
{
    TOID(TYPE) pe_first;
    PMEMmutex lock;
};

in the macro definitions, TYPE is the name of a user-defined structure that must contain a field of type POBJ_LIST_ENTRY. the argument HEADNAME is the name of a user-defined structure that must be declared using the macro POBJ_LIST_HEAD. peek the examples below for the full explanation of how these macros get used. πŸ‘€

the macro POBJ_LIST_ENTRY declares a structure that connects the elements in the list:

#define POBJ_LIST_ENTRY(TYPE)
struct
{
    TOID(TYPE) pe_next;
    TOID(TYPE) pe_prev;
};

🌸 the reading macros

  • the macro POBJ_LIST_FIRST() returns the first element on the list referenced by head. and if the list is empty? OID_NULL gets returned. simple as.
  • the macro POBJ_LIST_LAST() returns the last element on the list referenced by head. if the list is empty, again, OID_NULL. πŸŽ€
  • the macro POBJ_LIST_EMPTY() evaluates to 1 if the list referenced by head is empty. otherwise, 0 is returned. yes queen, a vibe check in boolean form.
  • the macro POBJ_LIST_NEXT() returns the element next to the element elm.
  • the macro POBJ_LIST_PREV() returns the element preceding the element elm.

πŸ¦‹ the traversal macros

  • the macro POBJ_LIST_FOREACH() traverses the list referenced by head, assigning a handle to each element in turn to the var variable.
  • the macro POBJ_LIST_FOREACH_REVERSE() traverses the list referenced by head in reverse order, assigning a handle to each element in turn to the var variable.
psst β€” for POBJ_LIST_FOREACH_REVERSE(), the field argument is the name of the field of type POBJ_LIST_ENTRY in the element structure. 🧚

πŸ‘‘ the insert macros

  • the macro POBJ_LIST_INSERT_HEAD() inserts the element elm at the head of the list referenced by head.
  • the macro POBJ_LIST_INSERT_TAIL() inserts the element elm at the end of the list referenced by head.
  • the macro POBJ_LIST_INSERT_AFTER() inserts the element elm into the list referenced by head after the element listelm. and if listelm value is TOID_NULL, the object gets inserted at the end of the list.
  • the macro POBJ_LIST_INSERT_BEFORE() inserts the element elm into the list referenced by head before the element listelm. and if listelm value is TOID_NULL, the object gets inserted at the head of the list.

πŸ’œ the insert-NEW macros (they allocate, bestie)

  • the macro POBJ_LIST_INSERT_NEW_HEAD() atomically allocates a new object of size size and inserts it at the head of the list referenced by head. the newly allocated object is also added to the internal object container associated with a type number which is retrieved from the typed OID of the first element on list. ✨
  • the macro POBJ_LIST_INSERT_NEW_TAIL() atomically allocates a new object of size size and inserts it at the tail of the list referenced by head. the newly allocated object is also added to the internal object container associated with a type number which is retrieved from the typed OID of the first element on list.
  • the macro POBJ_LIST_INSERT_NEW_AFTER() atomically allocates a new object of size size and inserts it into the list referenced by head after the element listelm. if listelm value is TOID_NULL, the object is inserted at the end of the list. the newly allocated object is also added to the internal object container associated with a type number which is retrieved from the typed OID of the first element on list.
  • the macro POBJ_LIST_INSERT_NEW_BEFORE() atomically allocates a new object of size size and inserts it into the list referenced by head before the element listelm. if listelm value is TOID_NULL, the object is inserted at the head of the list. the newly allocated object is also added to the internal object container associated with a type number which is retrieved from the typed OID of the first element on list.

πŸ™ˆ the remove macros

  • the macro POBJ_LIST_REMOVE() removes the element elm from the list referenced by head. bye bestie πŸ‘‹
  • the macro POBJ_LIST_REMOVE_FREE() removes the element elm from the list referenced by head and frees the memory space represented by this element. gone, deleted, no notes. πŸ’€

πŸ”₯ the move-element macros

  • the macro POBJ_LIST_MOVE_ELEMENT_HEAD() moves the element elm from the list referenced by head to the head of the list head_new. the field and field_new arguments are the names of the fields of type POBJ_LIST_ENTRY in the element structure that are used to connect the elements in both lists.
  • the macro POBJ_LIST_MOVE_ELEMENT_TAIL() moves the element elm from the list referenced by head to the end of the list head_new. the field and field_new arguments are the names of the fields of type POBJ_LIST_ENTRY in the element structure that are used to connect the elements in both lists.
  • the macro POBJ_LIST_MOVE_ELEMENT_AFTER() atomically removes the element elm from the list referenced by head and inserts it into the list referenced by head_new after the element listelm. if listelm value is TOID_NULL, the object is inserted at the end of the list. the field and field_new arguments are the names of the fields of type POBJ_LIST_ENTRY in the element structure that are used to connect the elements in both lists.
  • the macro POBJ_LIST_MOVE_ELEMENT_BEFORE() atomically removes the element elm from the list referenced by head and inserts it into the list referenced by head_new before the element listelm. if listelm value is TOID_NULL, the object is inserted at the head of the list. the field and field_new arguments are the names of the fields of type POBJ_LIST_ENTRY in the element structure that are used to connect the elements in both lists.

🌟 SEE ALSO 🌟

queue(3), libpmemobj(7) and https://pmem.io β€” go read those too, bestie, they're part of the lore 🫢


and that's the whole page!! this was the PMDK Programmer's Manual, dated 2025-07-24. thanks for coming to my ted talk, go build something iconic with your persistent atomic lists πŸ’–βœ¨πŸ“ mwah!!

πŸ“¦ where this page comes from