An introduction to pmemobj (part 7) - persistent lists
The pmemobj library provides non-transactional persistent atomic circular doubly-linked lists (or NTPACDLL for short) API with an interface familiar to anyone who have ever included
sys/queue.h header file - it’s in fact so similar that I considered not writing this post at all, you can just search the web for
Fun fact: The exact same list code is used internally by libpmemobj in the transaction undo log implementation.
Declaring a list
Let’s start by defining the structure of the thing we want on the list:
It’s a pmem notepad :) All of the notes are going to be stored in the root object:
The head of this list does not have to be initialized (maybe you have noticed that this is a recurring theme throughout the library, not initializing things that is).
Inserting new list entries
You can either insert an existing object to a list (
POBJ_LIST_INSERT) or allocate directly to the list (
POBJ_LIST_INSERT_NEW). You can also insert the entries anywhere in the list you want thanks to the self-explanatory
_BEFORE macro variants.
Continuing our example, we want to add a new entry to the head of the list:
Quite a lot happening here. You should remember the constructor from the non-transactional allocation API. Also worth noting is the allocation of flexible array in the
Accessing list entries
Reading things from the list is done using:
POBJ_LIST_PREV. They pretty much do what it says in the name.
Let’s write functions to read our notes:
I’ll leave it up to the reader to correctly use these functions in a CLI/GUI.
Iterating through the list
You can iterate a list in a similar fashion it’s done for internal collections. There are two macros:
POBJ_LIST_FOREACH_REVERSE. No magic here.
Reading all the notes:
Just like you can insert an existing object or a new one, you can just remove entry from the list (
POBJ_LIST_REMOVE) or remove and free it (
Removing current note:
Moving entries between lists
This is something new,
POBJ_LIST_MOVE_ELEMENT allows you to move an object from one list to another. A good use case example can be found in the PI code here. It’s used there as a task queue and once worker thread completes a task is then moved from todo list to done list.