Exemple #1
0
static void
test_allocs(PMEMobjpool *pop, const char *path)
{
	PMEMoid oid[TEST_ALLOC_SIZE];

	if (pmemobj_alloc(pop, &oid[0], 0, 0, NULL, NULL) == 0)
		UT_FATAL("pmemobj_alloc(0) succeeded");

	for (int i = 1; i < TEST_ALLOC_SIZE; ++i) {
		struct cargs args = { i };
		if (pmemobj_alloc(pop, &oid[i], i, 0,
				test_constructor, &args) != 0)
			UT_FATAL("!pmemobj_alloc");
		UT_ASSERT(!OID_IS_NULL(oid[i]));
	}

	pmemobj_close(pop);

	UT_ASSERT(pmemobj_check(path, LAYOUT_NAME) == 1);

	UT_ASSERT((pop = pmemobj_open(path, LAYOUT_NAME)) != NULL);

	for (int i = 1; i < TEST_ALLOC_SIZE; ++i) {
		pmemobj_free(&oid[i]);
		UT_ASSERT(OID_IS_NULL(oid[i]));
	}

}
Exemple #2
0
/*
 * test_open -- in the open test we should be able to allocate exactly
 * one object.
 */
static void
test_open(const char *path)
{
	PMEMobjpool *pop;
	if ((pop = pmemobj_open(path, LAYOUT_NAME)) == NULL)
		UT_FATAL("!pmemobj_open: %s", path);

	int ret = pmemobj_alloc(pop, NULL, ALLOC_SIZE, 0, NULL, NULL);
	UT_ASSERTeq(ret, 0);

	ret = pmemobj_alloc(pop, NULL, ALLOC_SIZE, 0, NULL, NULL);
	UT_ASSERTne(ret, 0);

	pmemobj_close(pop);
}
Exemple #3
0
int
main(int argc, char *argv[])
{
	START(argc, argv, "obj_heap_state");

	if (argc != 2)
		FATAL("usage: %s file-name", argv[0]);

	const char *path = argv[1];

	PMEMobjpool *pop = NULL;

	if ((pop = pmemobj_create(path, LAYOUT_NAME,
			PMEMOBJ_MIN_POOL, S_IWUSR | S_IRUSR)) == NULL)
		FATAL("!pmemobj_create: %s", path);

	pmemobj_root(pop, ROOT_SIZE); /* just to trigger allocation */

	pmemobj_close(pop);

	pop = pmemobj_open(path, LAYOUT_NAME);
	ASSERTne(pop, NULL);

	for (int i = 0; i < ALLOCS; ++i) {
		PMEMoid oid;
		pmemobj_alloc(pop, &oid, ALLOC_SIZE, 0, NULL, NULL);
		OUT("%d %lu", i, oid.off);
	}

	pmemobj_close(pop);

	DONE(NULL);
}
static void
allocate_objects(PMEMobjpool *pop, size_t size_min, size_t size_max)
{
	size_t allocated_total = 0;

	size_t sstart = 0;

	PMEMoid oid = pmemobj_root(pop, 1);
	uint64_t uuid_lo = oid.pool_uuid_lo;

	while (allocated_total < ALLOC_TOTAL) {
		size_t s = RRAND(seed, size_max, size_min);
		pmemobj_alloc(pop, &oid, s, 0, NULL, NULL);
		s = pmemobj_alloc_usable_size(oid);

		UT_ASSERTeq(OID_IS_NULL(oid), 0);
		objects[nobjects++] = oid.off;
		UT_ASSERT(nobjects < MAX_OBJECTS);
		allocated_total += s;
		allocated_current += s;

		if (allocated_current > ALLOC_CURR) {
			shuffle_objects(sstart, nobjects);
			for (int i = 0; i < FREES_P; ++i) {
				oid.pool_uuid_lo = uuid_lo;
				oid.off = remove_last();
				allocated_current -=
					pmemobj_alloc_usable_size(oid);
				pmemobj_free(&oid);
			}
			sstart = nobjects;
		}
	}
}
Exemple #5
0
int
main(int argc, char *argv[])
{
	START(argc, argv, "obj_direct");

	if (argc != 3)
		FATAL("usage: %s [directory] [# of pools]", argv[0]);

	int npools = atoi(argv[2]);
	const char *dir = argv[1];
	int r;

	PMEMobjpool *pops[npools];

	char path[MAX_PATH_LEN];
	for (int i = 0; i < npools; ++i) {
		snprintf(path, MAX_PATH_LEN, "%s/testfile%d", dir, i);
		pops[i] = pmemobj_create(path, LAYOUT_NAME, PMEMOBJ_MIN_POOL,
				S_IWUSR | S_IRUSR);

		if (pops[i] == NULL)
			FATAL("!pmemobj_create");
	}

	PMEMoid oids[npools];
	PMEMoid tmpoids[npools];

	oids[0] = OID_NULL;
	ASSERTeq(pmemobj_direct(oids[0]), NULL);

	for (int i = 0; i < npools; ++i) {
		oids[i] = (PMEMoid) {pops[i]->uuid_lo, 0};
		ASSERTeq(pmemobj_direct(oids[i]), NULL);

		uint64_t off = pops[i]->heap_offset;
		oids[i] = (PMEMoid) {pops[i]->uuid_lo, off};
		ASSERTeq(pmemobj_direct(oids[i]) - off, pops[i]);

		r = pmemobj_alloc(pops[i], &tmpoids[i], 100, 1, NULL, NULL);
		ASSERTeq(r, 0);
	}

	for (int i = 0; i < npools; ++i) {
		ASSERTne(pmemobj_direct(tmpoids[i]), NULL);

		pmemobj_free(&tmpoids[i]);

		ASSERTeq(pmemobj_direct(tmpoids[i]), NULL);

		pmemobj_close(pops[i]);
		ASSERTeq(pmemobj_direct(oids[i]), NULL);
	}

	DONE(NULL);
}
Exemple #6
0
static void
test_lazy_load(PMEMobjpool *pop, const char *path)
{
	PMEMoid oid[3];

	int ret = pmemobj_alloc(pop, &oid[0], LAZY_LOAD_SIZE, 0, NULL, NULL);
	UT_ASSERTeq(ret, 0);
	ret = pmemobj_alloc(pop, &oid[1], LAZY_LOAD_SIZE, 0, NULL, NULL);
	UT_ASSERTeq(ret, 0);
	ret = pmemobj_alloc(pop, &oid[2], LAZY_LOAD_SIZE, 0, NULL, NULL);
	UT_ASSERTeq(ret, 0);

	pmemobj_close(pop);
	UT_ASSERT((pop = pmemobj_open(path, LAYOUT_NAME)) != NULL);

	pmemobj_free(&oid[1]);

	ret = pmemobj_alloc(pop, &oid[1], LAZY_LOAD_BIG_SIZE, 0, NULL, NULL);
	UT_ASSERTeq(ret, 0);
}
int
main(int argc, char *argv[])
{
	START(argc, argv, "obj_fragmentation2");

	if (argc < 3)
		UT_FATAL("usage: %s filename workload [seed]", argv[0]);

	const char *path = argv[1];

	PMEMobjpool *pop = pmemobj_create(path, LAYOUT_NAME, DEFAULT_FILE_SIZE,
				S_IWUSR | S_IRUSR);
	if (pop == NULL)
		UT_FATAL("!pmemobj_create: %s", path);

	int w = atoi(argv[2]);

	if (argc > 3)
		seed = (unsigned)atoi(argv[3]);
	else
		seed = time(NULL);

	objects = ZALLOC(sizeof(uint64_t) * MAX_OBJECTS);
	UT_ASSERTne(objects, NULL);

	workloads[w - 1](pop);

	PMEMoid oid;
	size_t remaining = 0;
	size_t chunk = 100; /* calc at chunk level */
	while (pmemobj_alloc(pop, &oid, chunk, 0, NULL, NULL) == 0)
		remaining += pmemobj_alloc_usable_size(oid) + 16;

	size_t allocated_sum = 0;
	oid = pmemobj_root(pop, 1);
	for (size_t n = 0; n < nobjects; ++n) {
		if (objects[n] == 0)
			continue;
		oid.off = objects[n];
		allocated_sum += pmemobj_alloc_usable_size(oid) + 16;
	}

	size_t used = DEFAULT_FILE_SIZE - remaining;
	float frag = ((float)used / allocated_sum) - 1.f;

	UT_ASSERT(frag <= workloads_target[w - 1]);

	pmemobj_close(pop);

	FREE(objects);

	DONE(NULL);
}
Exemple #8
0
static void *
oom_worker(void *arg)
{
	allocated = 0;
	while (pmemobj_alloc(pop, NULL, TEST_ALLOC_SIZE, 0, NULL, NULL) == 0)
		allocated++;

	PMEMoid iter, iter2;
	int type;
	POBJ_FOREACH_SAFE(pop, iter, iter2, type)
		pmemobj_free(&iter);

	return NULL;
}
void
make_persistent_atomic(pool_base &pool,
		       typename detail::pp_if_array<T>::type &ptr,
		       std::size_t N)
{
	typedef typename detail::pp_array_type<T>::type I;

	auto ret = pmemobj_alloc(pool.get_handle(), ptr.raw_ptr(),
				 sizeof(I) * N, detail::type_num<I>(),
				 &detail::array_constructor<I>,
				 static_cast<void *>(&N));

	if (ret != 0)
		throw std::bad_alloc();
}
Exemple #10
0
/*
 * pmemlog_append -- add data to a log memory pool
 */
int
pmemlog_append(PMEMlogpool *plp, const void *buf, size_t count)
{
	PMEMobjpool *pop = (PMEMobjpool *)plp;

	struct create_args args = { count, buf };
	size_t obj_size = sizeof(size_t) + count;
	/* alloc-construct to an internal list */
	PMEMoid obj;
	pmemobj_alloc(pop, &obj, obj_size,
			0, create_log_entry,
			&args);

	return 0;
}
Exemple #11
0
static void
test_alloc(PMEMobjpool *pop, size_t size)
{
	unsigned long cnt = 0;

	while (1) {
		struct cargs args = { size };
		if (pmemobj_alloc(pop, NULL, size, 0,
				test_constructor, &args) != 0)
			break;
		cnt++;
	}

	OUT("size: %zu allocs: %lu", size, cnt);
}
Exemple #12
0
/*
 * pmemlog_appendv -- add gathered data to a log memory pool
 */
int
pmemlog_appendv(PMEMlogpool *plp, const struct iovec *iov, int iovcnt)
{
	PMEMobjpool *pop = (PMEMobjpool *)plp;

	/* append the data */
	for (int i = 0; i < iovcnt; ++i) {

		struct create_args args = { iov[i].iov_len, iov[i].iov_base };
		size_t obj_size = sizeof(size_t) + args.size;
		/* alloc-construct to an internal list */
		pmemobj_alloc(pop, NULL, obj_size,
				0, create_log_entry, &args);
	}

	return 0;
}
Exemple #13
0
static void
test_all_classes(PMEMobjpool *pop)
{
	for (int i = 1; i <= MAX_BUCKET_MAP_ENTRIES; ++i) {
		int err;
		int nallocs = 0;
		while ((err = pmemobj_alloc(pop, NULL, i * ALLOC_BLOCK_SIZE, 0,
			NULL, NULL)) == 0) {
			nallocs++;
		}

		UT_ASSERT(nallocs > 0);
		PMEMoid iter, niter;
		POBJ_FOREACH_SAFE(pop, iter, niter) {
			pmemobj_free(&iter);
		}
	}
Exemple #14
0
/*
 * test_create -- allocate all possible objects and log the number. It should
 * exceed what would be possible on a single zone.
 * Additionally, free one object so that we can later check that it can be
 * allocated after the next open.
 */
static void
test_create(const char *path)
{
	PMEMobjpool *pop = NULL;

	if ((pop = pmemobj_create(path, LAYOUT_NAME,
			0, S_IWUSR | S_IRUSR)) == NULL)
		UT_FATAL("!pmemobj_create: %s", path);

	PMEMoid oid;
	int n = 0;
	while (1) {
		if (pmemobj_alloc(pop, &oid, ALLOC_SIZE, 0, NULL, NULL) != 0)
			break;
		n++;
	}

	UT_OUT("allocated: %d", n);
	pmemobj_free(&oid);

	pmemobj_close(pop);
}
Exemple #15
0
/*
 * pobj_init_worker -- worker initialization
 */
static int
pobj_init_worker(struct benchmark *bench, struct benchmark_args *args,
		 struct worker_info *worker)
{
	size_t i, idx = worker->index;
	auto *bench_priv = (struct pobj_bench *)pmembench_get_priv(bench);
	auto *pw = (struct pobj_worker *)calloc(1, sizeof(struct pobj_worker));
	if (pw == nullptr) {
		perror("calloc");
		return -1;
	}

	worker->priv = pw;
	pw->oids = (PMEMoid *)calloc(bench_priv->args_priv->n_objs,
				     sizeof(PMEMoid));
	if (pw->oids == nullptr) {
		free(pw);
		perror("calloc");
		return -1;
	}

	PMEMobjpool *pop = bench_priv->pop[bench_priv->pool(idx)];
	for (i = 0; i < bench_priv->args_priv->n_objs; i++) {
		size_t size = bench_priv->fn_size(bench_priv, i);
		size_t type = bench_priv->fn_type_num(bench_priv, idx, i);
		if (pmemobj_alloc(pop, &pw->oids[i], size, type, nullptr,
				  nullptr) != 0) {
			perror("pmemobj_alloc");
			goto out;
		}
	}
	return 0;
out:
	for (; i > 0; i--)
		pmemobj_free(&pw->oids[i - 1]);
	free(pw->oids);
	free(pw);
	return -1;
}
Exemple #16
0
int
main(int argc, char *argv[])
{
	START(argc, argv, "obj_direct");

	if (argc != 3)
		FATAL("usage: %s [directory] [# of pools]", argv[0]);

	int npools = atoi(argv[2]);
	const char *dir = argv[1];
	int r;

	PMEMobjpool *pops[npools];

	char path[MAX_PATH_LEN];
	for (int i = 0; i < npools; ++i) {
		snprintf(path, MAX_PATH_LEN, "%s/testfile%d", dir, i);
		pops[i] = pmemobj_create(path, LAYOUT_NAME, PMEMOBJ_MIN_POOL,
				S_IWUSR | S_IRUSR);

		if (pops[i] == NULL)
			FATAL("!pmemobj_create");
	}

	PMEMoid oids[npools];
	PMEMoid tmpoids[npools];

	oids[0] = OID_NULL;
	ASSERTeq(pmemobj_direct(oids[0]), NULL);

	for (int i = 0; i < npools; ++i) {
		oids[i] = (PMEMoid) {pops[i]->uuid_lo, 0};
		ASSERTeq(pmemobj_direct(oids[i]), NULL);

		uint64_t off = pops[i]->heap_offset;
		oids[i] = (PMEMoid) {pops[i]->uuid_lo, off};
		ASSERTeq((char *)pmemobj_direct(oids[i]) - off,
			(char *)pops[i]);

		r = pmemobj_alloc(pops[i], &tmpoids[i], 100, 1, NULL, NULL);
		ASSERTeq(r, 0);
	}

	r = pmemobj_alloc(pops[0], &thread_oid, 100, 2, NULL, NULL);
	ASSERTeq(r, 0);
	ASSERTne(pmemobj_direct(thread_oid), NULL);

	pthread_mutex_lock(&lock);

	pthread_t t;
	pthread_create(&t, NULL, test_worker, NULL);

	/* wait for the thread to perform the first direct */
	while (flag)
		;

	for (int i = 0; i < npools; ++i) {
		ASSERTne(pmemobj_direct(tmpoids[i]), NULL);

		pmemobj_free(&tmpoids[i]);

		ASSERTeq(pmemobj_direct(tmpoids[i]), NULL);
		pmemobj_close(pops[i]);
		ASSERTeq(pmemobj_direct(oids[i]), NULL);
	}
	pthread_mutex_unlock(&lock);

	pthread_join(t, NULL);

	DONE(NULL);
}
Exemple #17
0
int
main(int argc, char *argv[])
{
	START(argc, argv, "obj_direct");

	if (argc != 3)
		UT_FATAL("usage: %s [directory] [# of pools]", argv[0]);

	int npools = atoi(argv[2]);
	const char *dir = argv[1];
	int r;

	PMEMobjpool *pops[npools];
	void *guard_after[npools];

	char path[MAX_PATH_LEN];
	for (int i = 0; i < npools; ++i) {
		snprintf(path, MAX_PATH_LEN, "%s/testfile%d", dir, i);
		pops[i] = pmemobj_create(path, LAYOUT_NAME, PMEMOBJ_MIN_POOL,
				S_IWUSR | S_IRUSR);

		/*
		 * Reserve a page after the pool for address checks, if it
		 * doesn't map precisely at that address - it's OK.
		 */
		guard_after[i] =
			MMAP((char *)pops[i] + PMEMOBJ_MIN_POOL, Ut_pagesize,
				PROT_READ | PROT_WRITE,
				MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);

		UT_ASSERTne(guard_after[i], NULL);

		if (pops[i] == NULL)
			UT_FATAL("!pmemobj_create");
	}

	PMEMoid oids[npools];

	for (int i = 0; i < npools; ++i) {
		r = pmemobj_alloc(pops[i], &oids[i], ALLOC_SIZE, 1, NULL, NULL);
		UT_ASSERTeq(r, 0);
	}

	PMEMoid invalid = {123, 321};

	UT_ASSERTeq(pmemobj_pool_by_oid(OID_NULL), NULL);
	UT_ASSERTeq(pmemobj_pool_by_oid(invalid), NULL);

	for (int i = 0; i < npools; ++i) {
		UT_ASSERTeq(pmemobj_pool_by_oid(oids[i]), pops[i]);
	}

	UT_ASSERTeq(pmemobj_pool_by_ptr(NULL), NULL);
	UT_ASSERTeq(pmemobj_pool_by_ptr((void *)0xCBA), NULL);

	for (int i = 0; i < npools; ++i) {
		void *before_pool = (char *)pops[i] - 1;
		void *after_pool = (char *)pops[i] + PMEMOBJ_MIN_POOL + 1;
		void *edge = (char *)pops[i] + PMEMOBJ_MIN_POOL;
		void *middle = (char *)pops[i] + (PMEMOBJ_MIN_POOL / 2);
		void *in_oid = (char *)pmemobj_direct(oids[i]) +
			(ALLOC_SIZE / 2);
		UT_ASSERTeq(pmemobj_pool_by_ptr(before_pool), NULL);
		UT_ASSERTeq(pmemobj_pool_by_ptr(after_pool), NULL);
		UT_ASSERTeq(pmemobj_pool_by_ptr(edge), NULL);
		UT_ASSERTeq(pmemobj_pool_by_ptr(middle), pops[i]);
		UT_ASSERTeq(pmemobj_pool_by_ptr(in_oid), pops[i]);
		pmemobj_close(pops[i]);
		UT_ASSERTeq(pmemobj_pool_by_ptr(middle), NULL);
		UT_ASSERTeq(pmemobj_pool_by_ptr(in_oid), NULL);

		MUNMAP(guard_after[i], Ut_pagesize);
	}

	DONE(NULL);
}
Exemple #18
0
	POBJ_ZREALLOC(pop, &node_constructed, struct dummy_node_c,
			sizeof(struct dummy_node_c) + 2000);

	UT_ASSERTeq(pmemobj_type_num(node_constructed.oid),
			TOID_TYPE_NUM(struct dummy_node_c));

	POBJ_FREE(&node_constructed);

	POBJ_ZALLOC(pop, &node_zeroed, struct dummy_node,
			sizeof(struct dummy_node));

	POBJ_FREE(&node_zeroed);

	int err = 0;

	err = pmemobj_alloc(pop, NULL, SIZE_MAX, 0, NULL, NULL);
	UT_ASSERTeq(err, -1);
	UT_ASSERTeq(errno, ENOMEM);

	err = pmemobj_zalloc(pop, NULL, SIZE_MAX, 0);
	UT_ASSERTeq(err, -1);
	UT_ASSERTeq(errno, ENOMEM);

	err = pmemobj_alloc(pop, NULL, PMEMOBJ_MAX_ALLOC_SIZE + 1, 0, NULL,
		NULL);
	UT_ASSERTeq(err, -1);
	UT_ASSERTeq(errno, ENOMEM);

	err = pmemobj_zalloc(pop, NULL, PMEMOBJ_MAX_ALLOC_SIZE + 1, 0);
	UT_ASSERTeq(err, -1);
	UT_ASSERTeq(errno, ENOMEM);
Exemple #19
0
/*
 * do_alloc -- wrapper for pmemobj_alloc() function with default constructor.
 */
static int
do_alloc(PMEMobjpool *pop, PMEMoid *oidp, size_t size, uint64_t type_num)
{
	return pmemobj_alloc(pop, oidp, size, type_num, NULL, NULL);
}
Exemple #20
0
int
main(int argc, char *argv[])
{
	START(argc, argv, "obj_constructor");

	if (argc != 2)
		UT_FATAL("usage: %s file-name", argv[0]);

	const char *path = argv[1];

	PMEMobjpool *pop = NULL;

	int ret;
	TOID(struct root) root;
	TOID(struct node) node;

	if ((pop = pmemobj_create(path, POBJ_LAYOUT_NAME(constr),
			0, S_IWUSR | S_IRUSR)) == NULL)
		UT_FATAL("!pmemobj_create: %s", path);

	/*
	 * Allocate memory until OOM, so we can check later if the alloc
	 * cancellation didn't damage the heap in any way.
	 */
	int allocs = 0;
	while (pmemobj_alloc(pop, NULL, sizeof (struct node), 1,
			NULL, NULL) == 0)
		allocs++;

	UT_ASSERTne(allocs, 0);

	PMEMoid oid;
	PMEMoid next;
	POBJ_FOREACH_SAFE(pop, oid, next)
		pmemobj_free(&oid);

	errno = 0;
	root.oid = pmemobj_root_construct(pop, sizeof (struct root),
			root_constr_cancel, NULL);
	UT_ASSERT(TOID_IS_NULL(root));
	UT_ASSERTeq(errno, ECANCELED);

	errno = 0;
	ret = pmemobj_alloc(pop, NULL, sizeof (struct node), 1,
			node_constr_cancel, NULL);
	UT_ASSERTeq(ret, -1);
	UT_ASSERTeq(errno, ECANCELED);

	/* the same number of allocations should be possible. */
	while (pmemobj_alloc(pop, NULL, sizeof (struct node), 1,
			NULL, NULL) == 0)
		allocs--;
	UT_ASSERTeq(allocs, 0);
	POBJ_FOREACH_SAFE(pop, oid, next)
		pmemobj_free(&oid);

	root.oid = pmemobj_root_construct(pop, sizeof (struct root),
			NULL, NULL);
	UT_ASSERT(!TOID_IS_NULL(root));

	errno = 0;
	node.oid = pmemobj_list_insert_new(pop, offsetof(struct node, next),
			&D_RW(root)->list, OID_NULL, 0, sizeof (struct node),
			1, node_constr_cancel, NULL);
	UT_ASSERT(TOID_IS_NULL(node));
	UT_ASSERTeq(errno, ECANCELED);

	pmemobj_close(pop);

	DONE(NULL);
}
Exemple #21
0
int
main(int argc, char *argv[])
{
	START(argc, argv, "obj_direct");

	if (argc != 3)
		UT_FATAL("usage: %s [directory] [# of pools]", argv[0]);

	int npools = atoi(argv[2]);
	const char *dir = argv[1];
	int r;

	pthread_mutex_init(&lock1, NULL);
	pthread_mutex_init(&lock2, NULL);
	pthread_cond_init(&sync_cond1, NULL);
	pthread_cond_init(&sync_cond2, NULL);
	cond1 = cond2 = 0;

	PMEMobjpool **pops = MALLOC(npools * sizeof(PMEMobjpool *));
	UT_ASSERTne(pops, NULL);

	char path[MAX_PATH_LEN];
	for (int i = 0; i < npools; ++i) {
		snprintf(path, MAX_PATH_LEN, "%s/testfile%d", dir, i);
		pops[i] = pmemobj_create(path, LAYOUT_NAME, PMEMOBJ_MIN_POOL,
				S_IWUSR | S_IRUSR);

		if (pops[i] == NULL)
			UT_FATAL("!pmemobj_create");
	}

	PMEMoid *oids = MALLOC(npools * sizeof(PMEMoid));
	UT_ASSERTne(oids, NULL);
	PMEMoid *tmpoids = MALLOC(npools * sizeof(PMEMoid));
	UT_ASSERTne(tmpoids, NULL);

	oids[0] = OID_NULL;
	UT_ASSERTeq(pmemobj_direct(oids[0]), NULL);

	for (int i = 0; i < npools; ++i) {
		oids[i] = (PMEMoid) {pops[i]->uuid_lo, 0};
		UT_ASSERTeq(pmemobj_direct(oids[i]), NULL);

		uint64_t off = pops[i]->heap_offset;
		oids[i] = (PMEMoid) {pops[i]->uuid_lo, off};
		UT_ASSERTeq((char *)pmemobj_direct(oids[i]) - off,
			(char *)pops[i]);

		r = pmemobj_alloc(pops[i], &tmpoids[i], 100, 1, NULL, NULL);
		UT_ASSERTeq(r, 0);
	}

	r = pmemobj_alloc(pops[0], &thread_oid, 100, 2, NULL, NULL);
	UT_ASSERTeq(r, 0);
	UT_ASSERTne(pmemobj_direct(thread_oid), NULL);

	pthread_t t;
	PTHREAD_CREATE(&t, NULL, test_worker, NULL);

	/* wait for the worker thread to perform the first check */
	pthread_mutex_lock(&lock1);
	while (!cond1)
		pthread_cond_wait(&sync_cond1, &lock1);
	pthread_mutex_unlock(&lock1);

	for (int i = 0; i < npools; ++i) {
		UT_ASSERTne(pmemobj_direct(tmpoids[i]), NULL);

		pmemobj_free(&tmpoids[i]);

		UT_ASSERTeq(pmemobj_direct(tmpoids[i]), NULL);
		pmemobj_close(pops[i]);
		UT_ASSERTeq(pmemobj_direct(oids[i]), NULL);
	}

	/* signal the worker that we're free and closed */
	pthread_mutex_lock(&lock2);
	cond2 = 1;
	pthread_cond_signal(&sync_cond2);
	pthread_mutex_unlock(&lock2);

	PTHREAD_JOIN(t, NULL);
	pthread_cond_destroy(&sync_cond1);
	pthread_cond_destroy(&sync_cond2);
	pthread_mutex_destroy(&lock1);
	pthread_mutex_destroy(&lock2);
	FREE(pops);
	FREE(tmpoids);
	FREE(oids);

	DONE(NULL);
}