├── README.md ├── log4p.h ├── persistent_skiplist.h └── smartpptr.h /README.md: -------------------------------------------------------------------------------- 1 | # pskiplist 2 | An implementation of the persistent skiplist based on Intel Optane Persistent Memory. The persistent skiplist data structure is firstly introduced in a [VLDB'21 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). 3 | 4 | With the simplicity and good performance, skiplist is widely used in many scenarios where the sorted keys are needed to support efficient scan operations. For example, Redis uses skiplist to implement its 'sorted set' data structure, RocksDB's memtable has a InlineSkiplist implemetation. Internally, our RTIDB, an in-memory database system specifically designed for feature engineering and online feature extraction for AI applications, uses a double-layered skiplist to manage all the in-memory data. 5 | 6 | It is now being actively developed to support more data storage systems (e.g., PmemKV, RocksDB, Kafka and etc.) as the underlying core data structure. 7 | 8 | # Usage 9 | ## PmemStore - PmemKV with the new storage engine PSKIPLIST 10 | Please checkout our [PmemStore](https://github.com/4paradigm/pmemstore) to find out how to use the persistent skiplist storage engine (ENGINE_PSKIPLIST) as the storage engine for a persistent key-value store. 11 | 12 | Latest updates will also be periodically merged to [Intel's PmemKV](https://github.com/pmem/pmemkv) to keep upstream. 13 | 14 | Stay tuned for the integration with more systems/applications. -------------------------------------------------------------------------------- /log4p.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | /* Copyright 2021, 4Paradigm Inc. */ 3 | 4 | #ifndef LOG4P_H 5 | #define LOG4P_H 6 | 7 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define LOG4P_LEVEL_ERROR 0 16 | #define LOG4P_LEVEL_WARNING 1 17 | #define LOG4P_LEVEL_INFO 2 18 | #define LOG4P_LEVEL_DEBUG 3 19 | 20 | #define LOG4P_LEVEL LOG4P_LEVEL_INFO 21 | 22 | const static char* _LOG4P_LEVEL_PREFIX[] = 23 | { 24 | " \033[1;31mE: ", 25 | " \033[1;35mW: ", 26 | " ", 27 | " \033[1;33mD: " 28 | }; 29 | 30 | static void _LOG4P(int logLevel, const char* file, const char* func, unsigned int line, const char* format, ...) 31 | { 32 | // if (logLevel > LOG4P_LEVEL) 33 | // return; 34 | 35 | char message[512]; 36 | va_list args; 37 | va_start(args, format); 38 | vsnprintf(message, 512, format, args); 39 | va_end(args); 40 | 41 | std::cout << file << ":" << line << " \033[1;36m" << func << "\033[0m" << _LOG4P_LEVEL_PREFIX[logLevel] << message << "\033[0m" << std::endl; 42 | } 43 | 44 | #define LOG4P(logLevel, format, ...) \ 45 | {_LOG4P(logLevel, __FILENAME__, __func__, __LINE__, format, ##__VA_ARGS__);} 46 | 47 | #if (LOG4P_LEVEL>=LOG4P_LEVEL_ERROR) 48 | #define LOG4P_ERROR(format, ...) \ 49 | LOG4P(LOG4P_LEVEL_ERROR, format, ##__VA_ARGS__) 50 | #else 51 | #define LOG4P_ERROR(format, ...) {} 52 | #endif 53 | 54 | #if (LOG4P_LEVEL>=LOG4P_LEVEL_WARNING) 55 | #define LOG4P_WARNING(format, ...) \ 56 | LOG4P(LOG4P_LEVEL_WARNING, format, ##__VA_ARGS__) 57 | #else 58 | #define LOG4P_WARNING(format, ...) {} 59 | #endif 60 | 61 | #if (LOG4P_LEVEL>=LOG4P_LEVEL_INFO) 62 | #define LOG4P_INFO(format, ...) \ 63 | LOG4P(LOG4P_LEVEL_INFO, format, ##__VA_ARGS__) 64 | #else 65 | #define LOG4P_INFO(format, ...) {} 66 | #endif 67 | 68 | #if (LOG4P_LEVEL>=LOG4P_LEVEL_DEBUG) 69 | #define LOG4P_DEBUG(format, ...) \ 70 | LOG4P(LOG4P_LEVEL_DEBUG, format, ##__VA_ARGS__) 71 | #else 72 | #define LOG4P_DEBUG(format, ...) {} 73 | #endif 74 | 75 | #endif // LOG4P_H -------------------------------------------------------------------------------- /persistent_skiplist.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | /* Copyright 2021, 4Paradigm Inc. */ 3 | 4 | #ifndef PERSISTENT_SKIPLIST 5 | #define PERSISTENT_SKIPLIST 6 | 7 | // #define DEBUG_LOG4P 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "smartpptr.h" 23 | 24 | #include 25 | 26 | namespace pmem 27 | { 28 | namespace kv 29 | { 30 | namespace internal 31 | { 32 | 33 | using namespace pmem::obj; 34 | 35 | template 36 | class slnode_t { 37 | public: 38 | using self_type = slnode_t; 39 | using key_type = Key; 40 | using mapped_type = T; 41 | using size_type = std::size_t; 42 | using level_type = uint8_t; 43 | 44 | using value_type = std::pair; 45 | using reference = value_type &; 46 | using const_reference = const value_type &; 47 | using pointer = value_type *; 48 | using const_pointer = const value_type *; 49 | 50 | // using slnode_ptr = typename std::conditional::type; 51 | using slnode_ptr = self_type *; 52 | 53 | using slnode_pptr = ::fourpd::SmartPPtr; 54 | using atomic_slnode_pptr = std::atomic; 55 | 56 | template 57 | slnode_t(K &&key, M &&obj, uint8_t height): _ref(1) { 58 | assert(pmemobj_tx_stage() == TX_STAGE_WORK); 59 | assert(height > 0); 60 | try { 61 | _height = height; 62 | LOG4P_DEBUG("_height = %u", unsigned(height)); 63 | pointer x = new (&_entry) value_type(std::forward(key), std::forward(obj)); 64 | assert(x == &_entry); 65 | LOG4P_DEBUG("_entry = %p : first = %s,second = %s", (void*)(&_entry), _entry.first.data(), _entry.second.data()); 66 | _nexts = make_persistent(height); 67 | LOG4P_DEBUG("_nexts = %p", _nexts.get()); 68 | } catch (transaction_error &e) { 69 | std::terminate(); 70 | } 71 | } 72 | 73 | slnode_t(uint8_t height): _ref(1) { // for head & tail 74 | assert(pmemobj_tx_stage() == TX_STAGE_WORK); 75 | try { 76 | _height = height; 77 | LOG4P_DEBUG("_height = %u", unsigned(height)); 78 | if (height > 0) { 79 | _nexts = make_persistent(height); 80 | LOG4P_DEBUG("_nexts = %p", _nexts.get()); 81 | } 82 | } catch (transaction_error &e) { 83 | LOG4P_ERROR("transaction_error"); 84 | std::terminate(); 85 | } 86 | } 87 | 88 | ~slnode_t() 89 | { 90 | assert(pmemobj_tx_stage() == TX_STAGE_WORK); 91 | try { 92 | _entry.first.~key_type(); 93 | _entry.second.~mapped_type(); 94 | delete_persistent(_nexts, _height.get_ro()); 95 | } catch (transaction_error &e) { 96 | std::terminate(); 97 | } 98 | } 99 | 100 | uint8_t height() { 101 | return _height.get_ro(); 102 | } 103 | 104 | const key_type& getKey() { 105 | return _entry.first; 106 | } 107 | 108 | reference getValue() { 109 | return _entry; 110 | } 111 | 112 | const_reference getValue() const { 113 | return _entry; 114 | } 115 | 116 | const bool isTail() { 117 | return (_height == 0); 118 | } 119 | 120 | slnode_ptr get_next_ptr(level_type lv) { 121 | return get_next_pptr(lv).getVptr(pmemobj_pool_by_oid(pmemobj_oid(this))); 122 | } 123 | slnode_pptr get_next_pptr(level_type lv) { 124 | auto expected = _nexts[lv].load(std::memory_order_relaxed); 125 | auto target = slnode_pptr(expected.getOffset(), false, false); 126 | while (true) { 127 | if (expected.isDirty()) { 128 | if (!(_nexts[lv].compare_exchange_strong(expected, target))) { 129 | expected = _nexts[lv].load(std::memory_order_relaxed); 130 | target = slnode_pptr(expected.getOffset(), false, false); 131 | continue; 132 | } 133 | pmemobj_persist(pmemobj_pool_by_oid(pmemobj_oid(this)), &(expected), sizeof(expected)); 134 | } 135 | break; 136 | } 137 | return target; 138 | } 139 | 140 | void set_next_pptr(level_type lv, const slnode_pptr &node) { 141 | assert(lv < _height && lv >= 0); 142 | _nexts[lv].store(node, std::memory_order_relaxed); 143 | } 144 | 145 | void pin() { 146 | _ref++; 147 | } 148 | 149 | void unpin() { 150 | _ref--; 151 | } 152 | 153 | private: 154 | union { 155 | value_type _entry; 156 | }; 157 | persistent_ptr _nexts; 158 | p _height; 159 | std::atomic _ref; 160 | }; 161 | 162 | template 163 | class persistent_skiplist_iterator { 164 | private: 165 | using slnode_type = NodeType; 166 | using slnode_ptr = slnode_type *; 167 | friend class persistent_skiplist_iterator; 168 | 169 | slnode_ptr _current_node; 170 | public: 171 | using iterator_category = std::forward_iterator_tag; 172 | using difference_type = ptrdiff_t; 173 | using value_type = typename slnode_type::value_type; 174 | using reference = typename slnode_type::reference; 175 | using pointer = typename slnode_type::pointer; 176 | 177 | persistent_skiplist_iterator(std::nullptr_t) 178 | : _current_node(nullptr) {} 179 | persistent_skiplist_iterator(slnode_ptr node) 180 | : _current_node(node) {} 181 | persistent_skiplist_iterator(const persistent_skiplist_iterator &other) 182 | : _current_node(other._current_node) {} 183 | template ::type> 184 | persistent_skiplist_iterator(const persistent_skiplist_iterator &other) 185 | : _current_node(other._current_node) {} 186 | 187 | persistent_skiplist_iterator &operator=(const persistent_skiplist_iterator &other) 188 | { 189 | _current_node = other._current_node; 190 | return *this; 191 | } 192 | 193 | persistent_skiplist_iterator &operator++() 194 | { 195 | _current_node = _current_node->get_next_ptr(0); 196 | return *this; 197 | } 198 | persistent_skiplist_iterator operator++(int) 199 | { 200 | persistent_skiplist_iterator tmp = *this; 201 | ++*this; 202 | return tmp; 203 | } 204 | 205 | bool operator==(const persistent_skiplist_iterator &other) const 206 | { 207 | return _current_node == other._current_node; 208 | } 209 | 210 | bool operator!=(const persistent_skiplist_iterator &other) const 211 | { 212 | return !(*this == other); 213 | } 214 | 215 | reference operator*() const 216 | { 217 | return _current_node->getValue(); 218 | } 219 | 220 | pointer operator->() const 221 | { 222 | return &(_current_node->getValue()); 223 | } 224 | }; 225 | 226 | template 227 | class persistent_skiplist_base { 228 | private: 229 | using self_type = persistent_skiplist_base; 230 | using slnode_type = slnode_t; 231 | using key_pptr = persistent_ptr; 232 | // using node_pptr = persistent_ptr; 233 | using node_pptr = ::fourpd::SmartPPtr; 234 | using node_ptr = slnode_type*; 235 | using atomic_node_pptr = std::atomic; 236 | public: 237 | using value_type = typename slnode_type::value_type; 238 | using key_type = typename slnode_type::key_type; 239 | using mapped_type = typename slnode_type::mapped_type; 240 | using key_compare = Compare; 241 | using size_type = std::size_t; 242 | using difference_type = std::ptrdiff_t; 243 | 244 | using reference = typename slnode_type::reference; 245 | using const_reference = typename slnode_type::const_reference; 246 | using pointer = typename slnode_type::pointer; 247 | using const_pointer = typename slnode_type::const_pointer; 248 | 249 | using iterator = persistent_skiplist_iterator; 250 | using const_iterator = persistent_skiplist_iterator; 251 | 252 | persistent_skiplist_base() : 253 | _random((unsigned long)std::chrono::system_clock::now().time_since_epoch().count()) { 254 | assert(pmemobj_tx_stage() == TX_STAGE_WORK); 255 | _head.store(allocate_node(Height), std::memory_order_relaxed); 256 | LOG4P_DEBUG("_head = %x", _head.load().getOffset()); 257 | _tail = allocate_node(0); 258 | LOG4P_DEBUG("_tail = %x", _tail.getOffset()); 259 | for (uint8_t i = 0;i < Height;i++) { 260 | _head.load().getVptr(get_objpool())->set_next_pptr(i, _tail); 261 | LOG4P_DEBUG("_head->next_pptr[%d] = %x", i, _head.load().getVptr(get_objpool())->get_next_pptr(i).getOffset()); 262 | } 263 | _size = 0; 264 | } 265 | 266 | ~persistent_skiplist_base() { 267 | 268 | } 269 | 270 | template 271 | std::pair try_emplace(K &&key, M &&obj) { 272 | std::vector pre(Height); 273 | std::pair res = find_less_or_equal(key, pre); 274 | if (res.second) { //key found { 275 | LOG4P_DEBUG("key found"); 276 | return std::pair(iterator(res.first), false); 277 | } else { 278 | LOG4P_DEBUG("key NOT found"); 279 | return internal_insert(pre, std::forward(key), std::forward(obj)); 280 | } 281 | } 282 | 283 | template 284 | iterator find(const K &key) { 285 | std::vector pre(Height); 286 | std::pair res = find_less_or_equal(key, pre); 287 | LOG4P_DEBUG("result {node=%p, found=%d}", (void*)res.first, res.second); 288 | return res.second ? 289 | iterator(res.first) : 290 | end(); 291 | } 292 | 293 | template 294 | const_iterator find(const K &key) const { 295 | std::vector pre(Height); 296 | std::pair res = find_less_or_equal(key, pre); 297 | LOG4P_DEBUG("result {node=%p, found=%d}", (void*)res.first, res.second); 298 | return res.second ? 299 | const_iterator(res.first) : 300 | cend(); 301 | } 302 | template 303 | iterator lower_bound(const K &key) { 304 | std::vector pre(Height); 305 | std::pair res = find_less_or_equal(key, pre); 306 | if (res.second) 307 | return iterator(res.first); 308 | else { 309 | node_ptr next = res.first->get_next_ptr(0); 310 | if (next->isTail()) 311 | return end(); 312 | else 313 | return iterator(next); 314 | } 315 | } 316 | 317 | template 318 | const_iterator lower_bound(const K &key) const { 319 | std::vector pre(Height); 320 | std::pair res = find_less_or_equal(key, pre); 321 | if (res.second) 322 | return const_iterator(res.first); 323 | else { 324 | node_ptr next = res.first->get_next_ptr(0); 325 | if (next->isTail()) 326 | return cend(); 327 | else 328 | return const_iterator(next); 329 | } 330 | } 331 | 332 | template 333 | iterator upper_bound(const K &key) { 334 | std::vector pre(Height); 335 | std::pair res = find_less_or_equal(key, pre); 336 | node_ptr next = res.first->get_next_ptr(0); 337 | while (!next->isTail()) { 338 | if (_compare(key, next->getKey())) 339 | return iterator(next); 340 | next = next->get_next_ptr(0); 341 | } 342 | return end(); 343 | } 344 | 345 | template 346 | const_iterator upper_bound(const K &key) const { 347 | std::vector pre(Height); 348 | std::pair res = find_less_or_equal(key, pre); 349 | node_ptr next = res.first->get_next_ptr(0); 350 | while (!next->isTail()) { 351 | if (_compare(key, next->getKey())) 352 | return const_iterator(next); 353 | next = next->get_next_ptr(0); 354 | } 355 | return cend(); 356 | } 357 | 358 | template 359 | size_type erase(const K &key) { 360 | std::vector pre(Height); 361 | std::pair res = find_less_or_equal(key, pre); 362 | if (res.second) { //key found 363 | LOG4P_DEBUG("key found"); 364 | return internal_erase(pre, res.first); 365 | } else { 366 | LOG4P_DEBUG("key NOT found"); 367 | return size_type(0); 368 | } 369 | } 370 | 371 | iterator begin() { 372 | return iterator(_head.load(std::memory_order_relaxed).getVptr(get_objpool())->get_next_ptr(0)); 373 | } 374 | iterator end() { 375 | return iterator(_tail.getVptr(get_objpool())); 376 | } 377 | const_iterator begin() const { 378 | return const_iterator(_head.load(std::memory_order_relaxed).getVptr(get_objpool())->get_next_ptr(0)); 379 | } 380 | const_iterator end() const { 381 | return const_iterator(_tail.getVptr(get_objpool())); 382 | } 383 | const_iterator cbegin() const { 384 | return begin(); 385 | } 386 | const_iterator cend() const { 387 | return end(); 388 | } 389 | 390 | /* method */ 391 | 392 | size_type size() const noexcept { 393 | return _size.get_ro(); 394 | } 395 | 396 | reference operator[](size_type pos) { 397 | node_ptr temp = _head.load(std::memory_order_relaxed).getVptr(get_objpool())->get_next_ptr(0); 398 | while (!temp->isTail()) { 399 | if (pos == 0) 400 | return temp->getValue(); 401 | temp = temp->get_next_ptr(0); 402 | pos--; 403 | } 404 | assert(false); 405 | } 406 | 407 | const_reference operator[](size_type pos) const { 408 | node_ptr temp = _head.load(std::memory_order_relaxed).getVptr(get_objpool())->get_next_ptr(0); 409 | while (!temp->isTail()) { 410 | if (pos == 0) 411 | return temp->getValue(); 412 | temp = temp->get_next_ptr(0); 413 | pos--; 414 | } 415 | assert(false); 416 | } 417 | 418 | key_compare &key_comp() { 419 | return _compare; 420 | } 421 | const key_compare &key_comp() const { 422 | return _compare; 423 | } 424 | 425 | private: 426 | atomic_node_pptr _head; 427 | node_pptr _tail; 428 | key_compare _compare; 429 | std::mt19937_64 _random; 430 | pmem::obj::p _size; 431 | 432 | /* helper func */ 433 | template 434 | inline node_pptr allocate_node(Args &&... args) { 435 | auto pptr = make_persistent(std::forward(args)...); 436 | LOG4P_DEBUG("vptr=%p, pmemobj_oid={%llu,%llu}", (void*)pptr.get(), pmemobj_oid(pptr.get()).pool_uuid_lo, pmemobj_oid(pptr.get()).off); 437 | return node_pptr(pptr.raw().off); 438 | } 439 | 440 | inline void deallocate(node_pptr node) { 441 | assert(node.getVptr(get_objpool()) != nullptr); 442 | LOG4P_DEBUG("vptr=%p", node.getVptr(get_objpool())); 443 | pool_base pop = get_pool_base(); 444 | pmem::obj::transaction::run(pop, [&] { 445 | delete_persistent(node.getPptr(get_pool_uuid())); 446 | // node = nullptr; 447 | }); 448 | } 449 | 450 | inline uint8_t random_height() { 451 | uint8_t height = 1; 452 | std::uniform_int_distribution dist(0, Branch-1); 453 | while (height < Height && dist(_random) == 0) { 454 | height ++; 455 | } 456 | return height; 457 | } 458 | 459 | template 460 | std::pair find_less_or_equal(const K &key, std::vector &pre) 461 | { 462 | node_ptr head = _head.load().getVptr(get_objpool()); 463 | node_ptr node = head; 464 | LOG4P_DEBUG("head = %p", (void*)head); 465 | uint8_t level = Height - 1; 466 | while (true) { 467 | node_ptr next = node->get_next_ptr(level); 468 | LOG4P_DEBUG("head->next[%u]=%p", unsigned(level), (void*)next); 469 | if (is_after_node(key, next)) { 470 | LOG4P_DEBUG("is_after_node returns TRUE"); 471 | node = next; 472 | } else { 473 | LOG4P_DEBUG("is_after_node returns FALSE"); 474 | pre[level] = node; 475 | if (level == 0) { 476 | if (next->isTail()) { 477 | LOG4P_DEBUG("next->isTail=TURE, key=%s", key.data()); 478 | return ((node != head) && (!_compare(key, node->getKey()) && !_compare(node->getKey(), key))) ? 479 | std::pair(node, true) : 480 | std::pair(node, false); 481 | } 482 | else if (!_compare(key, next->getKey()) && !_compare(next->getKey(), key)) { 483 | LOG4P_DEBUG("next is the target, key=%s, next=%p->key=", key.data(), (void*)next, next->getKey().data()); 484 | return std::pair(next, true); 485 | } else { 486 | LOG4P_DEBUG("next is NOT the target, key=%s, node=%p", key.data(), (void*)node); 487 | return ((node != head) && (!_compare(key, node->getKey()) && !_compare(node->getKey(), key))) ? 488 | std::pair(node, true) : 489 | std::pair(node, false); 490 | } 491 | } 492 | level--; 493 | } 494 | } 495 | } 496 | 497 | template 498 | std::pair internal_insert(std::vector &pre, K &&key, M &&obj) { 499 | auto pop = get_pool_base(); 500 | 501 | node_pptr newNode; 502 | uint8_t height = random_height(); 503 | LOG4P_DEBUG("random_height=%u", unsigned(height)); 504 | pmem::obj::transaction::run(pop, [&] { 505 | newNode = allocate_node(std::forward(key), std::forward(obj), height); 506 | }); 507 | for (uint64_t i = 0; i < height; i++) { 508 | LOG4P_DEBUG("pre[%llu]=%p, ->next_pptr=%x", i, pre[i], pre[i]->get_next_pptr(i).getOffset()); 509 | newNode.getVptr(get_objpool())->set_next_pptr(i, pre[i]->get_next_pptr(i)); 510 | LOG4P_DEBUG("set newNode->next_pptr[%llu]=%x", i, newNode.getVptr(get_objpool())->get_next_pptr(i).getOffset()); 511 | pre[i]->set_next_pptr(i, newNode); 512 | LOG4P_DEBUG("pre[%llu]=%p, ->next_pptr=%x", i, pre[i], pre[i]->get_next_pptr(i).getOffset()); 513 | } 514 | _size++; 515 | return std::pair(iterator(newNode.getVptr(get_objpool())), true); 516 | } 517 | 518 | size_type internal_erase(std::vector &pre, node_ptr node) { 519 | auto pop = get_pool_base(); 520 | auto target = pre[0]->get_next_pptr(0); 521 | LOG4P_DEBUG("target= pre[0]=%p, ->next_pptr[0]=%x", pre[0], target.getOffset()); 522 | for (int i = node->height()-1; i >= 0; i--) { 523 | LOG4P_DEBUG("pre[%llu]=%p, ->next_pptr=%p", i, pre[unsigned(i)], pre[unsigned(i)]->get_next_pptr(i).getOffset()); 524 | pre[unsigned(i)]->set_next_pptr(i, node->get_next_pptr(i)); 525 | LOG4P_DEBUG("set pre[%llu]=%p, ->next_pptr=%x", i, pre[unsigned(i)], node->get_next_pptr(i).getOffset()); 526 | } 527 | deallocate(target); 528 | _size--; 529 | return 1; 530 | } 531 | 532 | PMEMobjpool *get_objpool() { 533 | PMEMoid oid = pmemobj_oid(this); 534 | return pmemobj_pool_by_oid(oid); 535 | } 536 | 537 | uint64_t get_pool_uuid() { 538 | PMEMoid oid = pmemobj_oid(this); 539 | return oid.pool_uuid_lo; 540 | } 541 | 542 | pool_base get_pool_base() { 543 | return pool_base(get_objpool()); 544 | } 545 | template 546 | bool is_after_node(const K& key, node_ptr node) const { 547 | return (!node->isTail()) && (_compare(node->getKey(), key)); 548 | } 549 | }; 550 | 551 | } /* namespace internal */ 552 | 553 | template , 554 | std::size_t height = 8, std::size_t branch = 4> 555 | class persistent_skiplist : public internal::persistent_skiplist_base { 556 | private: 557 | using base_type = internal::persistent_skiplist_base; 558 | 559 | public: 560 | using base_type::begin; 561 | using base_type::end; 562 | using base_type::erase; 563 | using base_type::find; 564 | using base_type::try_emplace; 565 | 566 | /* type definitions */ 567 | using key_type = typename base_type::key_type; 568 | using mapped_type = typename base_type::mapped_type; 569 | using value_type = typename base_type::value_type; 570 | using iterator = typename base_type::iterator; 571 | using const_iterator = typename base_type::const_iterator; 572 | // using reverse_iterator = typename base_type::reverse_iterator; 573 | 574 | explicit persistent_skiplist() : base_type() 575 | { 576 | } 577 | 578 | ~persistent_skiplist() 579 | { 580 | } 581 | 582 | persistent_skiplist(const persistent_skiplist &) = delete; 583 | persistent_skiplist &operator=(const persistent_skiplist &) = delete; 584 | }; 585 | 586 | } // namespace kv 587 | } // namespace persistent 588 | #endif // PERSISTENT_SKIPLIST 589 | -------------------------------------------------------------------------------- /smartpptr.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | /* Copyright 2021, 4Paradigm Inc. */ 3 | 4 | #pragma once 5 | 6 | #include "libpmemobj++/p.hpp" 7 | #include "libpmemobj++/persistent_ptr.hpp" 8 | #include "libpmemobj++/pool.hpp" 9 | #include "libpmemobj++/utils.hpp" 10 | #include "libpmemobj++/make_persistent.hpp" 11 | #include "libpmemobj++/make_persistent_array.hpp" 12 | #include "libpmemobj++/transaction.hpp" 13 | #include 14 | #include 15 | 16 | #include "log4p.h" 17 | 18 | namespace fourpd 19 | { 20 | using pmem::obj::persistent_ptr; 21 | using pmem::obj::delete_persistent; 22 | using pmem::obj::make_persistent; 23 | using pmem::obj::p; 24 | using pmem::obj::pool; 25 | using pmem::obj::pool_by_vptr; 26 | using pmem::obj::pool_by_pptr; 27 | using pmem::obj::pool_base; 28 | using pmem::obj::transaction; 29 | 30 | using size_type = size_t; 31 | using offset_type = uint64_t; 32 | 33 | static const uint64_t kDeleteFlag = 1; 34 | static const uint64_t kDirtyFlag = 1 << 1; 35 | static const uint64_t mask = 1 | (1 << 1); 36 | 37 | template 38 | class SmartPPtr { 39 | private: 40 | offset_type val; 41 | //uint8_t refcount; 42 | public: 43 | SmartPPtr() = default; 44 | SmartPPtr(pool_base& pop, T * ref, bool deleted, bool dirty) { 45 | val = (((offset_type) ref - (offset_type) pop.handle()) & ~mask) 46 | | (deleted ? kDeleteFlag : 0) 47 | | (dirty ? kDirtyFlag : 0); 48 | } 49 | SmartPPtr(offset_type offset, bool deleted, bool dirty): 50 | val(offset | (deleted ? kDeleteFlag : 0) | (dirty ? kDirtyFlag : 0)) {} 51 | explicit SmartPPtr(persistent_ptr pptr): val(pptr.raw().off) {} 52 | explicit SmartPPtr(offset_type offset): val(offset) {} 53 | 54 | offset_type getOffset(void) { 55 | return (val & ~mask); 56 | } 57 | T * getVptr(PMEMobjpool * pool) { 58 | return (val == 0) ? nullptr : (T *) (((offset_type) pool + val ) & ~mask); 59 | } 60 | persistent_ptr getPptr(uint64_t pool_uuid) { 61 | return persistent_ptr(PMEMoid{pool_uuid, val & ~mask}); 62 | } 63 | 64 | bool isDelete(void) { return (val & kDeleteFlag); } 65 | bool isDirty(void) { return (val & kDirtyFlag); } 66 | void clearDirty(void) { val &= ~kDirtyFlag; return; } 67 | }; 68 | 69 | // template 70 | // inline T* OFFSET2PTR(PMEMobjpool * base_ptr_, offset_type offset) { 71 | // return reinterpret_cast((offset_type) (base_ptr_) + (offset_type) (offset)); 72 | // } 73 | 74 | // template 75 | // inline offset_type PTR2OFFSET(PMEMobjpool * base_ptr_, T* ptr) { 76 | // return ((offset_type) (ptr) - (offset_type) base_ptr_); 77 | // } 78 | 79 | // template 80 | // inline void PERSISTREAD(PMEMobjpool * base_ptr_, std::atomic>* addr) { 81 | // while (true) { 82 | // auto expected = addr->load(); 83 | // if (expected.isDirty()) { 84 | // LOG_("expected is dirty"); 85 | // auto target = SmartPPtr(base_ptr_, expected.getRef(base_ptr_), false, false); 86 | // if (!(addr->compare_exchange_strong(expected, target))) 87 | // continue; 88 | // pmemobj_persist(base_ptr_, addr, sizeof(std::atomic>)); 89 | // break; 90 | // } 91 | // break; 92 | // } 93 | // } 94 | } /* namespace fourpd */ 95 | --------------------------------------------------------------------------------