#include "libpmemobj.h"
#include "unittest.h"
POBJ_LAYOUT_BEGIN(convert);
POBJ_LAYOUT_ROOT(convert, struct root);
POBJ_LAYOUT_TOID(convert, struct foo);
POBJ_LAYOUT_TOID(convert, struct bar);
POBJ_LAYOUT_END(convert);
#define SMALL_ALLOC (64)
#define BIG_ALLOC (1024 * 200)
struct bar {
char value[BIG_ALLOC];
};
struct foo {
unsigned char value[SMALL_ALLOC];
};
#define TEST_VALUE 5
#define TEST_NVALUES 10
#define TEST_RECURSION_NUM 5
struct root {
TOID(struct foo) foo;
TOID(struct bar) bar;
int value[TEST_NVALUES];
};
static int trap = 0;
enum operation {
ADD,
DRW,
SET
};
#define TEST_GEN(type)\
static void \
type ## _tx(PMEMobjpool *pop, TOID(struct type) var, int array_size,\
int recursion, enum operation oper)\
{\
--recursion;\
\
TX_BEGIN(pop) {\
if (oper == ADD) {\
TX_ADD(var);\
oper = DRW;\
}\
\
if (recursion >= 1)\
TEST_CALL(type, pop, var, array_size, recursion,\
oper);\
\
for (int i = 0; i < array_size; ++i) {\
if (oper == SET)\
TX_SET(var, value[i], TEST_VALUE +\
D_RO(var)->value[i]);\
else if (oper == DRW)\
D_RW(var)->value[i] = TEST_VALUE +\
D_RO(var)->value[i];\
}\
} TX_END\
}
#define TEST_CALL(type, pop, var, array_size, recursion, oper)\
type ## _tx(pop, var, array_size, recursion, oper)
TEST_GEN(foo);
TEST_GEN(bar);
TEST_GEN(root);
static void
sc0_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
trap = 1;
TX_BEGIN(pop) {
TX_ADD(rt);
D_RW(rt)->value[0] = TEST_VALUE;
} TX_END
}
static void
sc0_verify_abort(PMEMobjpool *pop)
{
UT_ASSERTeq(pmemobj_root_size(pop), sizeof(struct root));
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
UT_ASSERTeq(D_RW(rt)->value[0], 0);
}
static void
sc0_verify_commit(PMEMobjpool *pop)
{
UT_ASSERTeq(pmemobj_root_size(pop), sizeof(struct root));
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
UT_ASSERTeq(D_RW(rt)->value[0], TEST_VALUE);
}
static void
sc1_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
POBJ_ZALLOC(pop, &D_RW(rt)->foo, struct foo,
sizeof(struct foo));
trap = 1;
TX_BEGIN(pop) {
TX_ADD(D_RW(rt)->foo);
D_RW(D_RW(rt)->foo)->value[0] = TEST_VALUE;
} TX_END
}
static void
sc1_verify_abort(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
UT_ASSERTeq(D_RW(D_RW(rt)->foo)->value[0], 0);
}
static void
sc1_verify_commit(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
UT_ASSERTeq(D_RW(D_RW(rt)->foo)->value[0], TEST_VALUE);
}
static void
sc2_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
TX_BEGIN(pop) {
TEST_CALL(root, pop, rt, TEST_NVALUES, TEST_RECURSION_NUM, ADD);
trap = 1;
TEST_CALL(root, pop, rt, TEST_NVALUES, TEST_RECURSION_NUM, ADD);
} TX_END
}
static void
sc2_verify_abort(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < TEST_NVALUES; ++i)
UT_ASSERTeq(D_RW(rt)->value[i], 0);
}
static void
sc2_verify_commit(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < TEST_NVALUES; ++i)
UT_ASSERTeq(D_RW(rt)->value[i],
2 * TEST_RECURSION_NUM * TEST_VALUE);
}
static void
sc3_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
POBJ_ZALLOC(pop, &D_RW(rt)->bar, struct bar,
sizeof(struct bar));
TX_BEGIN(pop) {
TEST_CALL(bar, pop, D_RW(rt)->bar, BIG_ALLOC,
TEST_RECURSION_NUM, SET);
trap = 1;
TEST_CALL(bar, pop, D_RW(rt)->bar, BIG_ALLOC,
TEST_RECURSION_NUM, SET);
} TX_END
}
static void
sc3_verify_abort(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < TEST_NVALUES; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->bar)->value[i], 0);
}
static void
sc3_verify_commit(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < TEST_NVALUES; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->bar)->value[i],
2 * TEST_RECURSION_NUM * TEST_VALUE);
}
static void
sc4_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
POBJ_ZALLOC(pop, &D_RW(rt)->foo, struct foo,
sizeof(struct foo));
TX_BEGIN(pop) {
TEST_CALL(foo, pop, D_RW(rt)->foo, SMALL_ALLOC,
TEST_RECURSION_NUM, ADD);
trap = 1;
TEST_CALL(foo, pop, D_RW(rt)->foo, SMALL_ALLOC,
TEST_RECURSION_NUM, ADD);
} TX_END
}
static void
sc4_verify_abort(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < SMALL_ALLOC; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->foo)->value[i], 0);
}
static void
sc4_verify_commit(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < SMALL_ALLOC; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->foo)->value[i],
2 * TEST_RECURSION_NUM * TEST_VALUE);
}
static void
sc5_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
POBJ_ZALLOC(pop, &D_RW(rt)->foo, struct foo,
sizeof(struct foo));
TX_BEGIN(pop) {
TEST_CALL(foo, pop, D_RW(rt)->foo, SMALL_ALLOC,
TEST_RECURSION_NUM, SET);
trap = 1;
TEST_CALL(foo, pop, D_RW(rt)->foo, SMALL_ALLOC,
TEST_RECURSION_NUM, SET);
} TX_END
}
static void
sc5_verify_abort(PMEMobjpool *pop)
{
sc4_verify_abort(pop);
}
static void
sc5_verify_commit(PMEMobjpool *pop)
{
sc4_verify_commit(pop);
}
static void
sc6_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
TX_BEGIN(pop) {
TX_SET(rt, foo, TX_NEW(struct foo));
TX_SET(rt, bar, TX_NEW(struct bar));
} TX_ONABORT {
UT_ASSERT(0);
} TX_END
trap = 1;
TX_BEGIN(pop) {
TX_FREE(D_RO(rt)->foo);
TX_FREE(D_RO(rt)->bar);
} TX_END
}
static void
sc6_verify_abort(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
TX_BEGIN(pop) {
TX_FREE(D_RO(rt)->foo);
TX_FREE(D_RO(rt)->bar);
} TX_ONABORT {
UT_ASSERT(0);
} TX_END
}
static void
sc6_verify_commit(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
TOID(struct foo) f;
POBJ_FOREACH_TYPE(pop, f) {
UT_ASSERT(TOID_EQUALS(f, D_RO(rt)->foo));
}
TOID(struct bar) b;
POBJ_FOREACH_TYPE(pop, b) {
UT_ASSERT(TOID_EQUALS(b, D_RO(rt)->bar));
}
}
static void
sc7_create(PMEMobjpool *pop)
{
int nallocs = 0;
TX_BEGIN(pop) {
for (;;) {
(void) TX_NEW(struct foo);
nallocs++;
}
} TX_END
trap = 1;
TX_BEGIN(pop) {
for (int i = 0; i < nallocs; ++i) {
(void) TX_NEW(struct foo);
}
} TX_ONABORT {
UT_ASSERT(0);
} TX_END
}
static void
sc7_verify_abort(PMEMobjpool *pop)
{
int nallocs = 0;
TOID(struct foo) f;
POBJ_FOREACH_TYPE(pop, f) {
nallocs++;
}
UT_ASSERTeq(nallocs, 0);
}
static void
sc7_verify_commit(PMEMobjpool *pop)
{
int nallocs = 0;
TOID(struct foo) f;
POBJ_FOREACH_TYPE(pop, f) {
nallocs++;
}
UT_ASSERTne(nallocs, 0);
}
static void
sc8_create(PMEMobjpool *pop)
{
int nallocs = 0;
TX_BEGIN(pop) {
for (;;) {
(void) TX_NEW(struct bar);
nallocs++;
}
} TX_END
trap = 1;
TX_BEGIN(pop) {
for (int i = 0; i < nallocs; ++i) {
(void) TX_NEW(struct bar);
}
} TX_ONABORT {
UT_ASSERT(0);
} TX_END
}
static void
sc8_verify_abort(PMEMobjpool *pop)
{
TX_BEGIN(pop) {
TOID(struct bar) f = TX_NEW(struct bar);
(void) f;
} TX_ONABORT {
UT_ASSERT(0);
} TX_END
}
static void
sc8_verify_commit(PMEMobjpool *pop)
{
int nallocs = 0;
TOID(struct bar) f;
POBJ_FOREACH_TYPE(pop, f) {
nallocs++;
}
UT_ASSERTne(nallocs, 0);
}
static void
sc9_create(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
POBJ_ZALLOC(pop, &D_RW(rt)->bar, struct bar,
sizeof(struct bar));
POBJ_ZALLOC(pop, &D_RW(rt)->foo, struct foo,
sizeof(struct foo));
TX_BEGIN(pop) {
TEST_CALL(foo, pop, D_RW(rt)->foo, SMALL_ALLOC,
TEST_RECURSION_NUM, SET);
TEST_CALL(bar, pop, D_RW(rt)->bar, BIG_ALLOC,
TEST_RECURSION_NUM, SET);
TEST_CALL(root, pop, rt, TEST_NVALUES, TEST_RECURSION_NUM, SET);
trap = 1;
TEST_CALL(foo, pop, D_RW(rt)->foo, SMALL_ALLOC,
TEST_RECURSION_NUM, ADD);
TEST_CALL(bar, pop, D_RW(rt)->bar, BIG_ALLOC,
TEST_RECURSION_NUM, ADD);
TEST_CALL(root, pop, rt, TEST_NVALUES, TEST_RECURSION_NUM, ADD);
} TX_END
}
static void
sc9_verify_abort(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < SMALL_ALLOC; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->foo)->value[i], 0);
for (int i = 0; i < BIG_ALLOC; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->bar)->value[i], 0);
for (int i = 0; i < TEST_NVALUES; ++i)
UT_ASSERTeq(D_RW(rt)->value[i], 0);
}
static void
sc9_verify_commit(PMEMobjpool *pop)
{
TOID(struct root) rt = POBJ_ROOT(pop, struct root);
for (int i = 0; i < SMALL_ALLOC; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->foo)->value[i],
2 * TEST_RECURSION_NUM * TEST_VALUE);
for (int i = 0; i < BIG_ALLOC; ++i)
UT_ASSERTeq(D_RW(D_RW(rt)->bar)->value[i],
2 * TEST_RECURSION_NUM * TEST_VALUE);
for (int i = 0; i < TEST_NVALUES; ++i)
UT_ASSERTeq(D_RW(rt)->value[i],
2 * TEST_RECURSION_NUM * TEST_VALUE);
}
typedef void (*scenario_func)(PMEMobjpool *pop);
struct {
scenario_func create;
scenario_func verify_abort;
scenario_func verify_commit;
} scenarios[] = {
{sc0_create, sc0_verify_abort, sc0_verify_commit},
{sc1_create, sc1_verify_abort, sc1_verify_commit},
{sc2_create, sc2_verify_abort, sc2_verify_commit},
{sc3_create, sc3_verify_abort, sc3_verify_commit},
{sc4_create, sc4_verify_abort, sc4_verify_commit},
{sc5_create, sc5_verify_abort, sc5_verify_commit},
{sc6_create, sc6_verify_abort, sc6_verify_commit},
{sc7_create, sc7_verify_abort, sc7_verify_commit},
{sc8_create, sc8_verify_abort, sc8_verify_commit},
{sc9_create, sc9_verify_abort, sc9_verify_commit},
};
int
main(int argc, char *argv[])
{
START(argc, argv, "obj_convert");
UT_COMPILE_ERROR_ON(POBJ_LAYOUT_TYPES_NUM(convert) != 2);
if (argc != 4)
UT_FATAL("usage: %s file [c|cs|va|vc] scenario", argv[0]);
const char *path = argv[1];
int create_pool = argv[2][0] == 'c';
int create_zero = argv[2][1] == 's';
int verify_abort = argv[2][1] == 'a';
int sc = atoi(argv[3]);
PMEMobjpool *pop;
if (create_pool) {
if ((pop = pmemobj_create(path, POBJ_LAYOUT_NAME(convert),
create_zero ? 0 : 2 * PMEMOBJ_MIN_POOL,
0666)) == NULL) {
UT_FATAL("failed to create pool\n");
}
scenarios[sc].create(pop);
} else {
if ((pop = pmemobj_open(path, POBJ_LAYOUT_NAME(convert)))
== NULL) {
UT_FATAL("failed to open pool\n");
}
if (verify_abort)
scenarios[sc].verify_abort(pop);
else
scenarios[sc].verify_commit(pop);
}
pmemobj_close(pop);
DONE(NULL);
}