diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..92f652b44 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/engines-experimental/pskiplist"] + path = src/engines-experimental/pskiplist + url = https://github.com/4paradigm/pskiplist.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ae9b84f0f..328302e73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ option(ENGINE_RADIX "enable experimental radix engine" OFF) option(ENGINE_ROBINHOOD "enable experimental robinhood engine (requires CXX_STANDARD to be set to value >= 14)" OFF) option(ENGINE_DRAM_VCMAP "enable testing dram_vcmap engine" OFF) +option(ENGINE_PSKIPLIST "enable experimental pskiplist engine" OFF) + # ----------------------------------------------------------------- # ## Set required and useful variables # ----------------------------------------------------------------- # @@ -140,6 +142,27 @@ if(ENGINE_RADIX) src/engines-experimental/radix.cc ) endif() +# checkout submodule for ENGINE_PSKIPLIST +find_package(Git QUIET) +option(GIT_SUBMODULE "Check submodules during build" ON) +if(GIT_SUBMODULE) + message(STATUS "Submodule update for ENGINE_PSKIPLIST") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_PSKIPLIST) +endif() +if(ENGINE_PSKIPLIST) + if(NOT GIT_SUBMOD_PSKIPLIST EQUAL "0") + message(FATAL_ERROR "git submodule update for ENGINE_PSKIPLIST failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + list(APPEND SOURCE_FILES + src/engines-experimental/pskiplist.h + src/engines-experimental/pskiplist.cc + src/engines-experimental/pskiplist/persistent_skiplist.h + src/engines-experimental/pskiplist/smartpptr.h + src/engines-experimental/pskiplist/log4p.h + ) +endif() if(ENGINE_ROBINHOOD) list(APPEND SOURCE_FILES src/engines-experimental/robinhood.h @@ -205,6 +228,12 @@ if(ENGINE_RADIX) else() message(STATUS "RADIX engine is OFF") endif() +if(ENGINE_PSKIPLIST) + add_definitions(-DENGINE_PSKIPLIST) + message(STATUS "PSKIPLIST engine is ON") +else() + message(STATUS "PSKIPLIST engine is OFF") +endif() if(ENGINE_ROBINHOOD) add_definitions(-DENGINE_ROBINHOOD) message(STATUS "ROBINHOOD engine is ON") diff --git a/README.md b/README.md index d23e468ec..e7c6169ac 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ all language bindings and utilities. Engines are loaded by name at runtime. | [tree3](doc/ENGINES-experimental.md#tree3) | Persistent B+ tree | Yes | No | No | | [stree](doc/ENGINES-experimental.md#stree) | Sorted persistent B+ tree | Yes | No | Yes | | [robinhood](doc/ENGINES-experimental.md#robinhood) | Persistent hash map with Robin Hood hashing | Yes | Yes | No | +| [pskiplist](doc/ENGINES-experimental.md#pskiplist) | Sorted persistent skiplist (contributed by [4Paradigm](https://github.com/4paradigm/pskiplist))| Yes | Yes | Yes | | [dram_vcmap](doc/ENGINES-testing.md#dram_vcmap) | Volatile concurrent hash map placed entirely on DRAM | Yes | Yes | No | The production quality engines are described in the [libpmemkv(7)](doc/libpmemkv.7.md#engines) manual diff --git a/doc/ENGINES-experimental.md b/doc/ENGINES-experimental.md index eb687c401..06549c489 100644 --- a/doc/ENGINES-experimental.md +++ b/doc/ENGINES-experimental.md @@ -5,6 +5,7 @@ - [radix](#radix) - [stree](#stree) - [robinhood](#robinhood) +- [pskiplist](#pskiplist) # tree3 @@ -178,6 +179,28 @@ There are two parameters to be optionally modified by env variables: No additional packages are required. +# pskiplist + +A persistent and concurrent engine, backed by a skiplist implemented using Persistent Compare and Swap (PCAS), contributed by [4Paradigm Inc.](http://www.4paradigm.com/). The skiplist data structure is firstly introduced in a [VLDB paper](http://vldb.org/pvldb/vol14/p799-chen.pdf) **("Optimizing In-memory Database Engine for AI-powered On-line Decision Augmentation Using Persistent Memory". Cheng Chen, Jun Yang, Mian Lu, Taize Wang, Zhao Zheng, Yuqiang Chen, Wenyuan Dai, Bingsheng He, Weng-Fai Wong, Guoan Wu, Yuping Zhao, Andy Rudoff)**. It can be enabled in CMakeLists.txt using the `ENGINE_PSKIPLIST` option. + +### Configuration + +* **path** -- Path to the database file (layout "pmemkv_stree") + + type: string +* **force_create** -- If 0, pmemkv opens file specified by 'path', otherwise it creates it + + type: uint64_t + + default value: 0 +* **size** -- Only needed when force_create is not 0, specifies size of the database [in bytes] + + type: uint64_t + +### Internals + +(TBD) + +### Prerequisites + +No additional packages are required. + # Related Work --------- diff --git a/src/engines-experimental/pskiplist b/src/engines-experimental/pskiplist new file mode 160000 index 000000000..52471fa35 --- /dev/null +++ b/src/engines-experimental/pskiplist @@ -0,0 +1 @@ +Subproject commit 52471fa355b77aef44c83a4e7e6b5f6c10f13253 diff --git a/src/engines-experimental/pskiplist.cc b/src/engines-experimental/pskiplist.cc new file mode 100644 index 000000000..02c613714 --- /dev/null +++ b/src/engines-experimental/pskiplist.cc @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2021, 4Paradigm Inc. */ + +#include +#include + +#include +#include + +#include "../out.h" +#include "pskiplist.h" + +using pmem::detail::conditional_add_to_tx; +using pmem::obj::make_persistent_atomic; +using pmem::obj::transaction; + +namespace pmem +{ +namespace kv +{ + +pskiplist::pskiplist(std::unique_ptr cfg) + : pmemobj_engine_base(cfg, "pmemkv_pskiplist"), config(std::move(cfg)) +{ + Recover(); + LOG("Started ok"); +} + +pskiplist::~pskiplist() +{ + LOG("Stopped ok"); +} + +std::string pskiplist::name() +{ + return "pskiplist"; +} + +status pskiplist::count_all(std::size_t &cnt) +{ + LOG("count_all"); + check_outside_tx(); + + cnt = my_skiplist->size(); + + return status::OK; +} + +template +static std::size_t size(It first, It last) +{ + auto dist = std::distance(first, last); + assert(dist >= 0); + + return static_cast(dist); +} + +/* above key, key exclusive */ +status pskiplist::count_above(string_view key, std::size_t &cnt) +{ + LOG("count_above key>=" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->upper_bound(key); + auto last = my_skiplist->end(); + + cnt = size(first, last); + + return status::OK; +} + +/* above or equal to key, key inclusive */ +status pskiplist::count_equal_above(string_view key, std::size_t &cnt) +{ + LOG("count_equal_above key>=" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->lower_bound(key); + auto last = my_skiplist->end(); + + cnt = size(first, last); + + return status::OK; +} + +/* below key, key exclusive */ +status pskiplist::count_below(string_view key, std::size_t &cnt) +{ + LOG("count_below key<" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->begin(); + auto last = my_skiplist->lower_bound(key); + + cnt = size(first, last); + + return status::OK; +} + +/* below or equal to key, key inclusive */ +status pskiplist::count_equal_below(string_view key, std::size_t &cnt) +{ + LOG("count_equal_below key>=" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->begin(); + auto last = my_skiplist->upper_bound(key); + + cnt = size(first, last); + + return status::OK; +} + +status pskiplist::count_between(string_view key1, string_view key2, std::size_t &cnt) +{ + LOG("count_between key range=[" << std::string(key1.data(), key1.size()) << "," + << std::string(key2.data(), key2.size()) << ")"); + check_outside_tx(); + + if (my_skiplist->key_comp()(key1, key2)) { + auto first = my_skiplist->upper_bound(key1); + auto last = my_skiplist->lower_bound(key2); + + cnt = size(first, last); + } else { + cnt = 0; + } + + return status::OK; +} + +status pskiplist::iterate(container_iterator first, container_iterator last, + get_kv_callback *callback, void *arg) +{ + for (auto it = first; it != last; ++it) { + auto ret = callback(it->first.c_str(), it->first.size(), + it->second.c_str(), it->second.size(), arg); + + if (ret != 0) + return status::STOPPED_BY_CB; + } + + return status::OK; +} + +status pskiplist::get_all(get_kv_callback *callback, void *arg) +{ + LOG("get_all"); + check_outside_tx(); + + auto first = my_skiplist->begin(); + auto last = my_skiplist->end(); + + return iterate(first, last, callback, arg); +} + +/* (key, end), above key */ +status pskiplist::get_above(string_view key, get_kv_callback *callback, void *arg) +{ + LOG("get_above start key>=" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->upper_bound(key); + auto last = my_skiplist->end(); + + return iterate(first, last, callback, arg); +} + +/* [key, end), above or equal to key */ +status pskiplist::get_equal_above(string_view key, get_kv_callback *callback, void *arg) +{ + LOG("get_equal_above start key>=" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->lower_bound(key); + auto last = my_skiplist->end(); + + return iterate(first, last, callback, arg); +} + +/* [start, key], below or equal to key */ +status pskiplist::get_equal_below(string_view key, get_kv_callback *callback, void *arg) +{ + LOG("get_equal_below start key>=" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->begin(); + auto last = my_skiplist->upper_bound(key); + + return iterate(first, last, callback, arg); +} + +/* [start, key), less than key, key exclusive */ +status pskiplist::get_below(string_view key, get_kv_callback *callback, void *arg) +{ + LOG("get_below key<" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto first = my_skiplist->begin(); + auto last = my_skiplist->lower_bound(key); + + return iterate(first, last, callback, arg); +} + +/* get between (key1, key2), key1 exclusive, key2 exclusive */ +status pskiplist::get_between(string_view key1, string_view key2, + get_kv_callback *callback, void *arg) +{ + LOG("get_between key range=[" << std::string(key1.data(), key1.size()) << "," + << std::string(key2.data(), key2.size()) << ")"); + check_outside_tx(); + + if (my_skiplist->key_comp()(key1, key2)) { + auto first = my_skiplist->upper_bound(key1); + auto last = my_skiplist->lower_bound(key2); + + return iterate(first, last, callback, arg); + } + + return status::OK; +} + +status pskiplist::exists(string_view key) +{ + LOG("exists for key=" << std::string(key.data(), key.size())); + check_outside_tx(); + + internal::pskiplist::skiplist_type::iterator it = my_skiplist->find(key); + if (it == my_skiplist->end()) { + LOG(" key not found"); + return status::NOT_FOUND; + } + return status::OK; +} + +status pskiplist::get(string_view key, get_v_callback *callback, void *arg) +{ + LOG("get using callback for key=" << std::string(key.data(), key.size())); + check_outside_tx(); + + internal::pskiplist::skiplist_type::iterator it = my_skiplist->find(key); + if (it == my_skiplist->end()) { + LOG(" key not found"); + return status::NOT_FOUND; + } + + callback(it->second.c_str(), it->second.size(), arg); + return status::OK; +} + +status pskiplist::put(string_view key, string_view value) +{ + LOG("put key=" << std::string(key.data(), key.size()) + << ", value.size=" << std::to_string(value.size())); + check_outside_tx(); + + auto result = my_skiplist->try_emplace(key, value); + if (!result.second) { // key already exists, so update + typename internal::pskiplist::skiplist_type::value_type &entry = + *result.first; + transaction::manual tx(pmpool); + entry.second = value; + transaction::commit(); + } + return status::OK; +} + +status pskiplist::remove(string_view key) +{ + LOG("remove key=" << std::string(key.data(), key.size())); + check_outside_tx(); + + auto result = my_skiplist->erase(key); + return (result == 1) ? status::OK : status::NOT_FOUND; +} + +void pskiplist::Recover() +{ + if (!OID_IS_NULL(*root_oid)) { + my_skiplist = + (internal::pskiplist::skiplist_type *)pmemobj_direct(*root_oid); + my_skiplist->key_comp().runtime_initialize( + internal::extract_comparator(*config)); + } else { + pmem::obj::transaction::run(pmpool, [&] { + pmem::obj::transaction::snapshot(root_oid); + *root_oid = pmem::obj::make_persistent< + internal::pskiplist::skiplist_type>() + .raw(); + my_skiplist = + (internal::pskiplist::skiplist_type *)pmemobj_direct( + *root_oid); + my_skiplist->key_comp().initialize( + internal::extract_comparator(*config)); + }); + } +} + +internal::iterator_base *pskiplist::new_iterator() +{ + return new pskiplist_iterator{my_skiplist}; +} + +internal::iterator_base *pskiplist::new_const_iterator() +{ + return new pskiplist_iterator{my_skiplist}; +} + +pskiplist::pskiplist_iterator::pskiplist_iterator(container_type *c) + : container(c), it_(nullptr), pop(pmem::obj::pool_by_vptr(c)) +{ +} + +pskiplist::pskiplist_iterator::pskiplist_iterator(container_type *c) + : pskiplist::pskiplist_iterator(c) +{ +} + +status pskiplist::pskiplist_iterator::seek(string_view key) +{ + init_seek(); + + it_ = container->find(key); + if (it_ != container->end()) + return status::OK; + + return status::NOT_FOUND; +} + +status pskiplist::pskiplist_iterator::seek_lower(string_view key) +{ + init_seek(); + + it_ = container->lower_bound(key); + if (it_ == container->begin()) { + it_ = container->end(); + return status::NOT_FOUND; + } + + // --it_; + + return status::OK; +} + +status pskiplist::pskiplist_iterator::seek_lower_eq(string_view key) +{ + init_seek(); + + it_ = container->upper_bound(key); + if (it_ == container->begin()) { + it_ = container->end(); + return status::NOT_FOUND; + } + + // --it_; + + return status::OK; +} + +status pskiplist::pskiplist_iterator::seek_higher(string_view key) +{ + init_seek(); + + it_ = container->upper_bound(key); + if (it_ == container->end()) + return status::NOT_FOUND; + + return status::OK; +} + +status pskiplist::pskiplist_iterator::seek_higher_eq(string_view key) +{ + init_seek(); + + it_ = container->lower_bound(key); + if (it_ == container->end()) + return status::NOT_FOUND; + + return status::OK; +} + +status pskiplist::pskiplist_iterator::seek_to_first() +{ + init_seek(); + + if (container->size() == 0) + return status::NOT_FOUND; + + it_ = container->begin(); + + return status::OK; +} + +status pskiplist::pskiplist_iterator::seek_to_last() +{ + // init_seek(); + + // if (container->size() == 0) + // return status::NOT_FOUND; + + // it_ = container->end(); + // --it_; + + // return status::OK; + + return status::NOT_SUPPORTED; +} + +status pskiplist::pskiplist_iterator::is_next() +{ + auto tmp = it_; + if (tmp == container->end() || ++tmp == container->end()) + return status::NOT_FOUND; + + return status::OK; +} + +status pskiplist::pskiplist_iterator::next() +{ + init_seek(); + + if (it_ == container->end() || ++it_ == container->end()) + return status::NOT_FOUND; + + return status::OK; +} + +status pskiplist::pskiplist_iterator::prev() +{ + // init_seek(); + + // if (it_ == container->begin()) + // return status::NOT_FOUND; + + // --it_; + + // return status::OK; + return status::NOT_SUPPORTED; +} + +result pskiplist::pskiplist_iterator::key() +{ + assert(it_ != container->end()); + + return {it_->first.cdata()}; +} + +result> +pskiplist::pskiplist_iterator::read_range(size_t pos, size_t n) +{ + assert(it_ != container->end()); + + if (pos + n > it_->second.size() || pos + n < pos) + n = it_->second.size() - pos; + + return {it_->second.crange(pos, n)}; +} + +result> +pskiplist::pskiplist_iterator::write_range(size_t pos, size_t n) +{ + assert(it_ != container->end()); + + if (pos + n > it_->second.size() || pos + n < pos) + n = it_->second.size() - pos; + + log.push_back({{it_->second.cdata() + pos, n}, pos}); + auto &val = log.back().first; + + return {{&val[0], &val[0] + n}}; +} + +status pskiplist::pskiplist_iterator::commit() +{ + pmem::obj::transaction::run(pop, [&] { + for (auto &p : log) { + auto dest = it_->second.range(p.second, p.first.size()); + std::copy(p.first.begin(), p.first.end(), dest.begin()); + } + }); + log.clear(); + + return status::OK; +} + +void pskiplist::pskiplist_iterator::abort() +{ + log.clear(); +} + +static factory_registerer register_pskiplist( + std::unique_ptr(new pskiplist_factory)); + +} // namespace kv +} // namespace pmem diff --git a/src/engines-experimental/pskiplist.h b/src/engines-experimental/pskiplist.h new file mode 100644 index 000000000..70ee82d0c --- /dev/null +++ b/src/engines-experimental/pskiplist.h @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2021, 4Paradigm Inc. */ + +#pragma once + +#include +#include +#include + +#include "../comparator/pmemobj_comparator.h" +#include "../iterator.h" +#include "../pmemobj_engine.h" +#include "pskiplist/persistent_skiplist.h" + +using pmem::obj::persistent_ptr; +using pmem::obj::pool; + +using namespace pmem::obj; +using namespace pmem::obj::experimental; + +#define DEFAULT_HEIGHT 8 +#define DEFAULT_BRANCH 4 + +namespace pmem +{ +namespace kv +{ +namespace internal +{ +namespace pskiplist +{ + +/** + * Indicates the maximum number of descendants a single node can have. + * DEGREE - 1 is the maximum number of entries a node can have. + */ +const size_t HEIGHT = 8; +const size_t BRANCH = 4; + +using string_t = pmem::obj::string; + +using key_type = string_t; +using value_type = string_t; +using skiplist_type = persistent_skiplist; + +} /* namespace pskiplist */ +} /* namespace internal */ + +class pskiplist : public pmemobj_engine_base { +private: + using container_type = internal::pskiplist::skiplist_type; + using container_iterator = typename container_type::iterator; + + template + class pskiplist_iterator; + +public: + pskiplist(std::unique_ptr cfg); + ~pskiplist(); + + std::string name() final; + + status count_all(std::size_t &cnt) final; + status count_above(string_view key, std::size_t &cnt) final; + status count_equal_above(string_view key, std::size_t &cnt) final; + status count_equal_below(string_view key, std::size_t &cnt) final; + status count_below(string_view key, std::size_t &cnt) final; + status count_between(string_view key1, string_view key2, std::size_t &cnt) final; + + status get_all(get_kv_callback *callback, void *arg) final; + status get_above(string_view key, get_kv_callback *callback, void *arg) final; + status get_equal_above(string_view key, get_kv_callback *callback, + void *arg) final; + status get_equal_below(string_view key, get_kv_callback *callback, + void *arg) final; + status get_below(string_view key, get_kv_callback *callback, void *arg) final; + status get_between(string_view key1, string_view key2, get_kv_callback *callback, + void *arg) final; + status exists(string_view key) final; + status get(string_view key, get_v_callback *callback, void *arg) final; + status put(string_view key, string_view value) final; + status remove(string_view key) final; + + internal::iterator_base *new_iterator() final; + internal::iterator_base *new_const_iterator() final; + +private: + pskiplist(const pskiplist &); + void operator=(const pskiplist &); + status iterate(container_iterator first, container_iterator last, + get_kv_callback *callback, void *arg); + void Recover(); + + internal::pskiplist::skiplist_type *my_skiplist; + std::unique_ptr config; +}; + +template <> +class pskiplist::pskiplist_iterator : virtual public internal::iterator_base { + using container_type = pskiplist::container_type; + +public: + pskiplist_iterator(container_type *container); + + status seek(string_view key) final; + status seek_lower(string_view key) final; + status seek_lower_eq(string_view key) final; + status seek_higher(string_view key) final; + status seek_higher_eq(string_view key) final; + + status seek_to_first() final; + status seek_to_last() final; + + status is_next() final; + status next() final; + status prev() final; + + result key() final; + + result> read_range(size_t pos, size_t n) final; + +protected: + container_type *container; + container_type::iterator it_; + pmem::obj::pool_base pop; +}; + +template <> +class pskiplist::pskiplist_iterator : public pskiplist::pskiplist_iterator { + using container_type = pskiplist::container_type; + +public: + pskiplist_iterator(container_type *container); + + result> write_range(size_t pos, size_t n) final; + + status commit() final; + void abort() final; + +private: + std::vector> log; +}; + +class pskiplist_factory : public engine_base::factory_base { +public: + std::unique_ptr + create(std::unique_ptr cfg) override + { + check_config_null(get_name(), cfg); + return std::unique_ptr(new pskiplist(std::move(cfg))); + }; + std::string get_name() override + { + return "pskiplist"; + }; +}; + +} /* namespace kv */ +} /* namespace pmem */ diff --git a/src/pmemobj_engine.h b/src/pmemobj_engine.h index bffe0bfbd..ebeaba526 100644 --- a/src/pmemobj_engine.h +++ b/src/pmemobj_engine.h @@ -69,7 +69,7 @@ class pmemobj_engine_base : public engine_base { bool failed_open = false; try { pop = pmem::obj::pool::open(path, layout); - } catch (pmem::pool_invalid_argument &e) { + } catch (pmem::pool_error &e) { failed_open = true; } @@ -81,7 +81,7 @@ class pmemobj_engine_base : public engine_base { } else { /* no flags set, just open */ try { pop = pmem::obj::pool::open(path, layout); - } catch (pmem::pool_invalid_argument &e) { + } catch (pmem::pool_error &e) { throw internal::invalid_argument(e.what()); } } @@ -124,7 +124,7 @@ class pmemobj_engine_base : public engine_base { try { return pmem::obj::pool::create(path, layout, size, S_IRWXU); - } catch (pmem::pool_invalid_argument &e) { + } catch (pmem::pool_error &e) { throw internal::invalid_argument(e.what()); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ee2a4e7b2..6779f97fa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -235,6 +235,44 @@ add_engine_test(ENGINE blackhole TRACERS none memcheck SCRIPT blackhole/default.cmake) ################################################################################ + +###################################### PSKIPLIST ############################## +if(ENGINE_PSKIPLIST) + add_engine_test(ENGINE pskiplist + BINARY c_api_null_db_config + TRACERS none memcheck + SCRIPT pskiplist/default.cmake) + + add_engine_test(ENGINE pskiplist + BINARY put_get_remove + TRACERS none #memcheck pmemcheck + SCRIPT pskiplist/default.cmake) + add_engine_test(ENGINE pskiplist + BINARY put_get_remove_not_aligned + TRACERS none #memcheck pmemcheck + SCRIPT pskiplist/default.cmake) + + + add_engine_test(ENGINE pskiplist + BINARY put_get_remove_charset_params + TRACERS none #memcheck pmemcheck + SCRIPT pskiplist/default.cmake + PARAMS 16 8) + + add_engine_test(ENGINE pskiplist + BINARY put_get_remove_long_key + TRACERS none #memcheck pmemcheck + SCRIPT pskiplist/default.cmake) + + add_engine_test(ENGINE pskiplist + BINARY put_get_remove_params + TRACERS none + SCRIPT pskiplist/default.cmake + DB_SIZE 4G PARAMS 400000) + +endif(ENGINE_PSKIPLIST) +################################################################################ + ##################################### CMAP #################################### if(ENGINE_CMAP) add_engine_test(ENGINE cmap diff --git a/tests/engines/pskiplist/default.cmake b/tests/engines/pskiplist/default.cmake new file mode 100644 index 000000000..9492efca9 --- /dev/null +++ b/tests/engines/pskiplist/default.cmake @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2020, Intel Corporation + +include(${PARENT_SRC_DIR}/helpers.cmake) +include(${PARENT_SRC_DIR}/engines/pmemobj_based/helpers.cmake) + +setup() + +if ((${TRACER} STREQUAL "drd") OR (${TRACER} STREQUAL "helgrind")) + check_is_pmem(${DIR}/testfile) +endif() + +execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory /mnt/pmem0/${TEST_NAME}) +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory /mnt/pmem0/${TEST_NAME}) +pmempool_execute(create -l ${LAYOUT} -s ${DB_SIZE} obj /mnt/pmem0/${TEST_NAME}/testfile) + +make_config({"path":"/mnt/pmem0/${TEST_NAME}/testfile"}) +execute(${TEST_EXECUTABLE} ${ENGINE} ${CONFIG} ${PARAMS}) + +finish() diff --git a/tests/engines/pskiplist/helpers.cmake b/tests/engines/pskiplist/helpers.cmake new file mode 100644 index 000000000..88a09a46a --- /dev/null +++ b/tests/engines/pskiplist/helpers.cmake @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2020, Intel Corporation + +# +# helpers.cmake - helper functions for tests' (cmake) scripts (pmemobj_based engines only) +# + +set(LAYOUT "pmemkv") +if (NOT ${ENGINE} STREQUAL "cmap") + string(CONCAT LAYOUT "pmemkv_" ${ENGINE}) +endif() diff --git a/tests/wrong_engine_name_test.cc b/tests/wrong_engine_name_test.cc index 4d8e3a549..44dfc0d0f 100644 --- a/tests/wrong_engine_name_test.cc +++ b/tests/wrong_engine_name_test.cc @@ -82,6 +82,9 @@ int main() #ifndef ENGINE_DRAM_VCMAP UT_ASSERT(wrong_engine_name_test("dram_vcmap")); #endif +#ifndef ENGINE_PSKIPLIST + UT_ASSERT(wrong_engine_name_test("pskiplist")); +#endif errormsg_test();