One of the biggest hurdles and error prone things about our C API is that the user has to manually keep track of modifications to the persistent memory resident variables while in a transaction. A special semi-transparent template property class has been implemented to automatically add variable modifications to the transaction undo log.
Let’s start with the vector example from the previous tutorial series. It looked like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct vector {
int x;
int y;
int z;
}
PMEMoid root = pmemobj_root(pop, sizeof (struct vector));
struct vector *vectorp = pmemobj_direct(root);
TX_BEGIN(pop) {
pmemobj_tx_add_range(root, 0, sizeof (struct vector));
vectorp->x = 5;
vectorp->y = 10;
vectorp->z = 15;
} TX_END
As you can see, the programmer has to remember to call pmemobj_tx_add_range
function before any modifications to the memory. In a simple case like this one
it might not be such a big deal, but once the code gets complex it may lead to
some difficult to find consistency issues.
By using the C++ API we can simplify this code like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <libpmemobj/p.hpp>
using namespace pmem::obj;
struct vector {
p<int> x;
p<int> y;
p<int> z;
}
PMEMoid root = pmemobj_root(pop, sizeof (struct vector));
struct vector *vectorp = pmemobj_direct(root);
TX_BEGIN(pop) {
vectorp->x = 5;
vectorp->y = 10;
vectorp->z = 15;
} TX_END
The template class pmem::obj::p
does not add storage overhead. The size of
the vector structure is exactly the same as in the C version.
This mechanism works overriding operator=
and adding the memory to the undo log
before modification. It’s pretty straightforward.