From ee292aa558dbc536586b0396dd5db1353b70e8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Tomic=CC=8Cevic=CC=81?= <dominik.tomicevic@gmail.com> Date: Mon, 7 Dec 2015 21:50:07 +0100 Subject: [PATCH] implemented correct skiplist --- data_structures/skiplist/skiplist.hpp | 511 ++++++++++++++++++++------ examples/skiplist | Bin 11792 -> 0 bytes examples/skiplist.cpp | 45 ++- memory/deferred_recycler.hpp | 29 ++ memory/lazy_gc.hpp | 2 + memory/recycler.hpp | 19 +- utils/auto_scope.hpp | 2 +- 7 files changed, 484 insertions(+), 124 deletions(-) delete mode 100755 examples/skiplist create mode 100644 memory/deferred_recycler.hpp diff --git a/data_structures/skiplist/skiplist.hpp b/data_structures/skiplist/skiplist.hpp index 573a1da99..61047c5ff 100644 --- a/data_structures/skiplist/skiplist.hpp +++ b/data_structures/skiplist/skiplist.hpp @@ -2,22 +2,26 @@ #include <algorithm> #include <memory> +#include <cassert> -#include "threading/hazard_ptr.hpp" #include "threading/sync/lockable.hpp" #include "threading/sync/spinlock.hpp" #include "utils/random/fast_binomial.hpp" +#include "memory/lazy_gc.hpp" // concurrent skiplist based on the implementation described in // "A Provably Correct Scalable Concurrent Skip List" // https://www.cs.tau.ac.il/~shanir/nir-pubs-web/Papers/OPODIS2006-BA.pdf template <class K, class T, size_t H=32, class lock_t=SpinLock> -class SkipList +class SkipList : LazyGC<SkipList<K, T, H, lock_t>>, Lockable<lock_t> { +public: static thread_local FastBinomial<H> rnd; + using data_t = std::pair<const K, T>; + struct Flags { enum node_flags : uint8_t { @@ -51,62 +55,20 @@ class SkipList class Node : Lockable<lock_t> { + public: friend class SkipList; - public: - class reference - { - public: - reference() {} - - reference(Node* node, bool store_hazard = true) - : node(node), hazard(store_hazard ? node : nullptr) {} - - reference(const reference&) = delete; - - reference(reference&& other) - : node(other.node), hazard(std::move(other.hazard)) - { - other.node = nullptr; - } - - reference& operator=(const reference& other) = delete; - - reference& operator=(const reference&& other) - { - node = other.node; - other.node = nullptr; - hazard = std::move(other.hazard); - - return *this; - } - - T& operator*() { return *node; } - const T& operator*() const { return *node; } - - T* operator->() { return node; } - const T* operator->() const { return node; } - - operator T*() { return node; } - const operator T*() const { return node; } - - const hazard_ptr& get_hazard() const - { - return hazard; - } - - private: - hazard_ptr hazard; - Node* node; - }; - - K key; - T item; + data_t data; const uint8_t height; Flags flags; - static Node* create(K key, T item, uint8_t height) + static Node* create(const K& key, const T& item, uint8_t height) + { + return create({key, item}, height); + } + + static Node* create(data_t&& data, uint8_t height) { // [ Node ][Node*][Node*][Node*]...[Node*] // | | | | | @@ -120,7 +82,7 @@ class SkipList // we have raw memory and we need to construct an object // of type Node on it - return new (node) Node(key, item, height); + return new (node) Node(std::move(data), height); } static void destroy(Node* node) @@ -129,20 +91,19 @@ class SkipList free(node); } - Node::reference forward(size_t level) const + Node* forward(size_t level) const { - while(true) - { - auto ref = Node::reference(load_tower(level)); + return tower[level].load(std::memory_order_acquire); + } - if(ref.get_hazard() == load_tower(level)) - return std::move(ref); - } + void forward(size_t level, Node* next) + { + tower[level].store(next, std::memory_order_release); } private: - Node(K key, T item, uint8_t height) - : key(key), item(item), height(height) + Node(data_t data, uint8_t height) + : data(data), height(height) { // here we assume, that the memory for N towers (N = height) has // been allocated right after the Node structure so we need to @@ -157,16 +118,6 @@ class SkipList tower[i].~atomic(); } - void forward(size_t level, Node* next) - { - tower[level].store(next, std::memory_order_release); - } - - Node* load_tower(size_t level) - { - return tower[level].load(std::memory_order_acquire); - } - // this creates an array of the size zero. we can't put any sensible // value here since we don't know what size it will be untill the // node is allocated. we could make it a Node** but then we would @@ -176,52 +127,254 @@ class SkipList std::atomic<Node*> tower[0]; }; - using node_ref_t = typename Node::reference; - public: - class iterator + class ConstIterator { + friend class SkipList; + ConstIterator(Node* node) : node(node) {} + public: - iterator() = default; - iterator(node_ref_t&& node) : node(std::move(node)) {} - iterator(const iterator&) = delete; + ConstIterator() = default; + ConstIterator(const ConstIterator&) = default; - T& operator*() { return node; } - T* operator->() { return node; } - - operator T*() { return node; } - - iterator& operator++() + const data_t& operator*() { + assert(node != nullptr); + return node->data; + } + + const data_t* operator->() + { + assert(node != nullptr); + return &node->data; + } + + operator const data_t() + { + assert(node != nullptr); + return node->data; + } + + ConstIterator& operator++() + { + assert(node != nullptr); node = node->forward(0); return *this; } - iterator& operator++(int) + ConstIterator& operator++(int) { return operator++(); } + friend bool operator==(const ConstIterator& a, const ConstIterator& b) + { + return a.node == b.node; + } + + friend bool operator!=(const ConstIterator& a, const ConstIterator& b) + { + return !(a == b); + } + private: - node_ref_t node; + Node* node {nullptr}; + }; + + class Iterator + { + friend class SkipList; + Iterator(Node* node) : node(node) {} + + public: + Iterator() = default; + Iterator(const Iterator&) = default; + + data_t& operator*() + { + assert(node != nullptr); + return node->data; + } + + data_t* operator->() + { + assert(node != nullptr); + return &node->data; + } + + operator data_t() + { + assert(node != nullptr); + return node->data; + } + + Iterator& operator++() + { + assert(node != nullptr); + node = node->forward(0); + return *this; + } + + Iterator& operator++(int) + { + return operator++(); + } + + friend bool operator==(const Iterator& a, const Iterator& b) + { + return a.node == b.node; + } + + friend bool operator!=(const Iterator& a, const Iterator& b) + { + return !(a == b); + } + + private: + Node* node {nullptr}; }; SkipList() : header(Node::create(K(), T(), H)) {} - auto begin() { return iterator(header->forward(0)); } - auto end() { return iterator(); } + friend class Accessor; + + class Accessor + { + friend class SkipList; + + Accessor(SkipList& skiplist) : skiplist(skiplist) {} + public: + Accessor(const Accessor&) = delete; + Accessor(Accessor&&) = default; + + Iterator begin() + { + return skiplist.begin(); + } + + ConstIterator begin() const + { + return skiplist.cbegin(); + } + + ConstIterator cbegin() const + { + return skiplist.cbegin(); + } + + Iterator end() + { + return skiplist.end(); + } + + ConstIterator end() const + { + return skiplist.cend(); + } + + ConstIterator cend() const + { + return skiplist.cend(); + } + + std::pair<Iterator, bool> insert_unique(const K& key, const T& item) + { + return skiplist.insert({key, item}, preds, succs); + } + + std::pair<Iterator, bool> insert_unique(const K& key, T&& item) + { + return skiplist.insert({key, std::move(item)}, preds, succs); + } + + ConstIterator find(const K& key) const + { + return skiplist.find(key); + } + + Iterator find(const K& key) + { + return skiplist.find(key); + } + + bool remove(const K& key) + { + return skiplist.remove(key, preds, succs); + } + + private: + SkipList& skiplist; + Node* preds[H], *succs[H]; + }; + + Accessor access() + { + return Accessor(*this); + } + +private: + using guard_t = std::unique_lock<lock_t>; + + Iterator begin() + { + return Iterator(header->forward(0)); + } + + ConstIterator begin() const + { + return ConstIterator(header->forward(0)); + } + + ConstIterator cbegin() const + { + return ConstIterator(header->forward(0)); + } + + Iterator end() + { + return Iterator(); + } + + ConstIterator end() const + { + return ConstIterator(); + } + + ConstIterator cend() const + { + return ConstIterator(); + } size_t size() const { return count.load(std::memory_order_acquire); } - iterator find(const K& key) + bool greater(const K& key, const Node* const node) { - auto pred = node_ref_t(header); - node_ref_t node; + return node && key > node->data.first; + } - uint8_t h = pred->height - 1; + bool less(const K& key, const Node* const node) + { + return (node == nullptr) || key < node->data.first; + } + + ConstIterator find(const K& key) const + { + return find_node<ConstIterator>(key); + } + + Iterator find(const K& key) + { + return find_node<Iterator>(key); + } + + template <class It> + It find_node(const K& key) + { + Node* node, *pred = header; + int h = static_cast<int>(pred->height) - 1; while(true) { @@ -230,7 +383,7 @@ public: // if we overshoot at every layer, item doesn't exist if(h < 0) - return iterator(); + return It(); // the item is farther to the right, continue going right as long // as the key is greater than the current node's key @@ -238,47 +391,169 @@ public: pred = node, node = node->forward(h); // check if we have a hit. if not, we need to descend down again - if(!less(key, node)) - return iterator(std::move(node)); + if(!less(key, node) && !node->flags.is_marked()) + return It(node); } } -private: - std::atomic<size_t> count {0}; - Node* header; - - bool greater(const K& key, const Node* const node) + int find_path(Node* from, int start, const K& key, + Node* preds[], Node* succs[]) { - return node && key > node->key; - } - - bool less(const K& key, const Node* const node) - { - return (node == nullptr) || key < node->key; - } - - int find_path(Node& from, int start, const K& key, - node_ref_t (preds&)[], - node_ref_t (succs&)[]) - { - int lfound = -1; + int level_found = -1; Node* pred = from; - for(int level = start_level; level >= 0; --level) + for(int level = start; level >= 0; --level) { Node* node = pred->forward(level); while(greater(key, node)) pred = node, node = pred->forward(level); - - if(lfound == -1 && !less(key, node)) - lfound = level; + if(level_found == -1 && !less(key, node)) + level_found = level; preds[level] = pred; succs[level] = node; } - return lfound; + return level_found; } + + template <bool ADDING> + bool lock_nodes(uint8_t height, guard_t guards[], + Node* preds[], Node* succs[]) + { + Node *prepred, *pred, *succ = nullptr; + bool valid = true; + + for(int level = 0; valid && level < height; ++level) + { + pred = preds[level], succ = succs[level]; + + if(pred != prepred) + guards[level] = pred->acquire_unique(), prepred = pred; + + valid = !pred->flags.is_marked() && pred->forward(level) == succ; + + if(ADDING) + valid = valid && (succ == nullptr || !succ->flags.is_marked()); + } + + return valid; + } + + std::pair<Iterator, bool> + insert(data_t&& data, Node* preds[], Node* succs[]) + { + while(true) + { + auto level = find_path(header, H - 1, data.first, preds, succs); + + if(level != -1) + { + auto found = succs[level]; + + if(found->flags.is_marked()) + continue; + + while(!found->flags.is_fully_linked()) + usleep(250); + + return {Iterator {succs[level]}, false}; + } + + auto height = rnd(); + guard_t guards[H]; + + // try to acquire the locks for predecessors up to the height of + // the new node. release the locks and try again if someone else + // has the locks + if(!lock_nodes<true>(height, guards, preds, succs)) + continue; + + // you have the locks, create a new node + auto new_node = Node::create(std::move(data), height); + + // link the predecessors and successors, e.g. + // + // 4 HEAD ... P ------------------------> S ... NULL + // 3 HEAD ... ... P -----> NEW ---------> S ... NULL + // 2 HEAD ... ... P -----> NEW -----> S ... ... NULL + // 1 HEAD ... ... ... P -> NEW -> S ... ... ... NULL + for(uint8_t level = 0; level < height; ++level) + { + new_node->forward(level, succs[level]); + preds[level]->forward(level, new_node); + } + + new_node->flags.set_fully_linked(); + count.fetch_add(1, std::memory_order_relaxed); + + return {Iterator {new_node}, true}; + } + } + + bool ok_delete(Node* node, int level) + { + return node->flags.is_fully_linked() + && node->height - 1 == level + && !node->flags.is_marked(); + } + + bool remove(const K& key, Node* preds[], Node* succs[]) + { + Node* node = nullptr; + guard_t node_guard; + bool marked = false; + int height = 0; + + while(true) + { + auto level = find_path(header, H - 1, key, preds, succs); + + if(!marked && (level == -1 || !ok_delete(succs[level], level))) + return false; + + if(!marked) + { + node = succs[level]; + height = node->height; + node_guard = node->acquire_unique(); + + if(node->flags.is_marked()) + return false; + + node->flags.set_marked(); + } + + guard_t guards[H]; + + if(!lock_nodes<false>(height, guards, preds, succs)) + continue; + + for(int level = height - 1; level >= 0; --level) + preds[level]->forward(level, node->forward(level)); + + // TODO recycle(node) + count.fetch_sub(1, std::memory_order_relaxed); + return true; + } + } + + guard_t gc_lock_acquire() + { + return this->acquire_unique(); + } + + void vacuum() + { + + } + + std::atomic<size_t> count {0}; + Node* header; }; + +template <class K, class T, size_t H, class lock_t> +thread_local FastBinomial<H> SkipList<K, T, H, lock_t>::rnd; + diff --git a/examples/skiplist b/examples/skiplist deleted file mode 100755 index dc0c8c70b19e5dc59eb2051e696eb2bd6df8ce60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11792 zcmcgyeQ;FQb-$~XKnAfAU@*osY%sWdn6*}bEMgMBl~{OURv;tECItLg?Jj8J{m^LN zV&Q>_7#vrNOiG$&#+mW7bo%9X+%bP-JUI0PX>gHe;<j=-Y3Y=y6;Euh9AYbt3vz?K z{hj;nS-pMQCGK>lSMzl4`9AmDkG=2S_pM-PSCz{pxKxR+3BsmpHA%Am3(@(2L=~+? z+$A)zTC5WDK&io_Ne)4)#&AZNW>~A_+<?`1TB<BUOO+-o3|CoXc(4jbNRa%^7EM(x zIJ3k;84BT*1k$5enLe}Rh@>;TsN%S!AnOrIdMlPnalj+8!cefhg4{l88$I2M-d&{u zBU~e(qMC&yOJzPDs#l7WWT>z_!_N22DAk|!NH(A*D-7ST$bup3JqA5$-xn<nO7NjX zytjGlLy73-L_C!p+&tLQyt#R+FO&8)$=FDaa=v?SkJR8idL<?V_5Wf#i}2LrVY{eG zJx(j(6f4C@I+UNg@GQVX<ACzL5DzuG4o{^*_Pu48t1Hm|0(3Q$O0>o+(C<fM%gJx7 zKwn;genQ!ARP&FWZ$ZV&hz;(mK#zl7E}p-sK>x0yA9477r-J-v73h~iUxS{wus}mi zi2LM!{KI(3n9<0lO*BTDg<%{^rc*}73=f!wA&fo8js3A>@r)T8XiJ1MnOH^`?cE(l zG&Z2f-5qU-bSl;z?oGrjUU|;JE^|AqB;%>D89OeFgL}KomUud2^oBFBt?j9}xzis6 z-8eQlX!OShGU-$}5jRg5#~UFWO>`13G8i^`;W%YP!e*pTF_Pu1wMlCSQ%@(UjY+F{ zs2z($j0|CZVf3V)j;Eq%VJ4f5kywPYK#maSVB8eRa3Yb8z&Y_%W|HA}O6+cHGq(5| z#qLmhYn##JYw~RqZ3hknTH6i3Z;M4fZ~!7(IoVZMmfZ5snnzWbL)D<w$hk#QF8qBA z$n&D}ffJZUHDU+GB^Bb=q0ap5$~dN-TkI_Is~|BuZ~ys~r!XC>MW@m+H~aynW|jDc zl3%gx2$_}hSdk>&qUbcQ?R3pXr+Lpsx6yeGBVMPCZvTBaV58gDxg$0@`R3ARqw`u$ zynY*<)*CK^HoATN8M4u7jpH(GqpPJ_vd-J+v>tNN&)lfhM{1Ibyh8u>s9BSz38kNT zqxOm=QP|cCR$*Nf|La$4s1e>r%&Ex&%DRJuQ);IsB)*?;8WvNd65m5OrFiP1#9Ilc z)J|QH_*V$0lun(OcoX52%BdlVZzP;jIMpxlHH1^@rjAH_CE>JWPjyOs3E{MSPiYcg zKscpzsu6gPo_h~%ZqP5ktTK{s2U)+&3!?sGX6Qe<z#@-BBp1AmyfhrXaumf^zxr92 z6oTyK{}SxOSL#>q6vF?@_T1Y~>bZCIGe4j1>~0?)?e}OxA0HDEj8EUCiTth43x!XP zO8HB#z(<e1`6U4T%!~)-j^B5QEK_$ZK75%Ng-Hlo{>Y6cX8r$|_&xgRkEDUI&)w9% z+_XNHe^hr})!+WyTvoKUkge6P7HEd_KlD{somYtLV%d-sHRNi|M+?2e^=s+ra>G&z z>`{@e`Nj93S2Xr3QYcIuo_Yp4v+=0>HjW2xQzI_X{LYQ%V`#CS8`pDVdhXOUS?id{ zKMLj)iTOwMk&X#H*D;M0Hy*z7npFFT+}nCCn9p_mTpu}gE&mt<QOW1m!x-m7`D^fx z=B~o@3<PEXr}J&n?2o(vx$)f9d_-!)K`u~h^`mS*>r35Sb~?Z5Gej!;+0OSeUmwYm z512WX*K@ak=JVGu+M(h_PR}cQ+VDi~)a_h$W;l4O{?(paXvx^m-1_M;Y8cv?mkoL6 zcA?<^C)vtLeIz)ke-&J?gUC?FME$dCWX!>9BiY*{J!sZ={i_|fz<x8o%;LNWWCEHK zaIewOkmC{KyKxw-Q!}~jC%K;K;o!%Vmi8QFW-^5Ccn<0#9iM<YncG)A(lMReS9>=2 z@$l|C{q#63CwUre<H0=si;-X+em~y+X8pO{K=nuv(RWO8W5y=kx@$~7{VB;J?rZhW zQX^%DjC7zS|CoOQIIPsrDuE82`fCgsM2wE+d^O1YBFOwt$jY4jH~4QBCBANx;PzVj z=s#*GI%PUf`)SM0?BL}5&^VE@+?QXY+zmmaXipoaE~f4yi}kN&Z{=w_>9i>0FO@wT zyfr+3jy+rav{rEo*`HV0SnINRJoCN_G30Z%@((~|IQW5_D`$frtaYIS^207z!Jwn@ zGoI_1%r~m|wc#0m;WbJ&nJb(P7G84I2R}Vs07cY)e{`*@{?$>65bG6>|NNi+H_g!C zO#Un?vniAO-;%GWK2p7oZ0qNSFru*(&guuA2s|Fh6#_kh?$Eh)?^k=_@!aN*0K3|A zH`{YR_5XWl1Fgul`q*dgeDYI7@$FxlU*h>4%6%Bh-P{Qag}bloXRf&P?Z3!=h=$jG z<8a_m;Ber{fN|ygtiNC5cS~ARKVdHH5nT7V9~nh>_u$z92ej{~Ln}johX9|)uTui> zI{1m-00;d#;LCuk(4e;fcL3f1OaRtlzTW_R5b#AT7a_pc0TY1N0iOq~!y5H6;Ddl~ z0UiRp0eBLy4*p&Qd=T&kU<hyr@|FNR1o$%GNx-*=4|sRA5O*UsDz0bt3)f(S>%O}A zwdY;)8;DNtu)}EAMiNk^ygWt(_$0r6rcjt5Sx>_*&x$?u3!knX5|6HYWaE~#YlzJH z?h=2NF6m!_ej5n}JPprPwJqYk5AmqDpXm(ANawq0%kQ@0we%^5YlxS25!F9jqS<Yb z{z*LR(I1Zh+IfT$|3y4H__S3i$A4Y%eHHlEK|cWfQx*8PLB9n4D;4;wFb;H#k4pY& zk4yaDsi1#I@$av|KMDQ;=-*p`|Dxi{IaenCuY=!r2mR}cUn#yf6u%Sxt0(97&nk@L z7hr!=h4^-W|K=V1A0qzELV-4A<@)a=_%A_!c^N*IEOA}&W5jRuH2i*5t7pY`+(D1` zTy?8w{qr@tr}4RYx~JvL{Cys+-_sKCGzL8DTRmRLLB7>fE64X*%&!Uffw_`WaBpz` zE>XVBWU4ga*+m3Do`<PIrQlxXUbG4o+HBK=P-k7E*ymB1#NelLLE#d?KcrX|Cw$LA zr*|kSELW#0brxA%n6JqizXO=qW>S%32awO7<X8nxt)Q~f0-|>55m}=qP@#=A6)ZWj z3|46p*2w=DSJf8I^L?Vq!WSt1F%_TeThLZ0I&E0&1*w<Ci|s2F?gOvV=k<p+{#0n~ zrZO~7laT3s3g@`lAN|NuVI0eBn(&z#jex+iDshfuTHzClFNOXe{uR&p9KQZV_3Qfz z-c)d&8c)j=d_ciw1$Qa<xPnI&Jg(q#3Od};8NgS(>w9{$DKqQch9dy~=BB2sM4L8P z1aDKLf1AJ2-zbz=BoR&>^B%_$M?9VKZt*qynl}5r>&@`7%tQNwp<tjZXl!Y2dgv&Q zN)j8q>*;_b>V;@1^mvCQzoFFeR^N8A(P%mL`!{$cMF?N!MA8iR0-6IB?BiNIh0~aR z;Y+2>m=9+bo8e#h`oft$;ftO~LD7QdfbbnlWqm5%62t&+AeIP|f~xf=OyP^;G)MSw ze1iXIMI5|zG;D^2FV<%q9SA35Mqd=VjKL^QWnzPoSifl?PMjq9%-&4KvSfq@2Er#S zOI-hE1nTe_PR1k9PQwxY`J{KtlT?qSlgU`hboN>)?#3$7Ql*s=fw?iey35ktVz4Z| zTJXHK8*}6QhUb4NeV!;cZ`?Rd<@r`BUyC<<%}$baH%?x8e%qODoW}AxS4v+fM%BD1 zrPqn`WyZN1r@_2Fmdbm?kXkoO>Gj2P?oxV#(CaljN!Hysai;Bzv$%0m%ImP5=N3yV z(C;p;%cb(m1ig+si(4!&-W%9?ZmgPoE>=okA$Z*_rQa)FQ0s9ieWjq|1!r;3p1u}Y zw0u{KU&&XEA`un(y>f}U@zd{A6}Vo<?<4Kqh<?OD4=B3x_mo~SUHDbwIMvUHI4bFL z^Lb9$8FkqIfs~)yFPA_s*RH<<oo*VO{r<0tPTMDY`49-Ti}wZmJ2?*lQ~Z4R#PKgf zh2(iW(-wmY@yo@tN!n=;7ae}LOZwdWldW=gf>M6&II+%Km(CfV2@0spc>b2OGk1Ir zgYLyH#W~)-2l^W7_d1OXiu>h%A$~&iD&%dECMj}nh}yfLFM&LrvQgoE#D6OKv|0x? zO0E!}g6>syJE(<XIr=Kl%iVJ{f=+P?6_4CY!(c$l&s}GBSFn>%cFwEz^1hAU9n1M0 ztw4VV^yM}U-oJgK>>pX6$>z#;D%9@5#;2TpKj@U_3ywVNL`QqQDo)-{MwJ~tK9x@J zF9*7p5gYt-WoOdS-Z9V@V-r23`jO+Gs9@(q&>PB(pIgd)|3XcAeMR~HG7M7un)1v2 zwMWq}D*8Gl|0L*(tL`nXCyFe-33|D_{hpG);8-XBP|9PRsCLDap+5ti;^|j;-lXVb ziax6P%ctl+1--$xzHvN1Q}l}tzwawMUS+>iDSW2r)5`uSMSrlSe7p95PI=~amHj@Z z<mu(!UIsv?c8w~(Jf6Q(!Tz6ApueW<Tu^cHcpH=Qb%M^(sr1SJLR^*fGW&$cfSEC~ zII*XjuRU%1jZk}6H*N%HFa8WOX+-GiFC+NI&qgwgXxccINcV;lM$}9XWQ=fjP+&jS zpNN^UsPExz%`LE8M#!KYqqWa7uy-3cA*>x^G@DGGfQk(>Xroyont3YTpTKpcFVf#H zFX-%>O8;ESR$=T8?Q0E$3?q~6HQJ350|qWFLoJ38Ps<%G-9HX;qmG9Mq<ciPYk#04 zXax7}q#IM2ELB&g_wCx%73?;;1FfMTNt8q#EhWgCS7Ge@`rbfCdz+xWtaayT-ElUD z%`~>W?VWpDy3o`Rn%W+Zhmu>Gf-PPB@l+@sc`CSdZ#o(a2HTo~U0dPrVD}Sy{eH7A zo-xe6fmk@&6psesRo-VtGFh2$!Ct<=1+cu0)iinUsV+Eiom=z|zqqTF`~EVwymT)+ zN4LxLZ!vJ;N^O%D!vC8FS{Jm+rn-lnb6e|RA1s#<YD2}OTCI{d(ALGU!y@JaUh8l# zok$qPdv5Bq7U~mvr)e){h`OfY6}Tfgce-t-J9KB_`p7tL#@qLyN29nY&*JUzP8ah= z=@`313gozg^?00a-tUyGZMzb9o1JYX4_gk-z*hr2VM;#?aJ1ftX3|DqI2EOj3uZ^G zH63O1{c6-4g~x}DE;+e^y`{4yNjbq+0tQ|Rd7Q^m$jTfGkdwN<Eep*NOe8aArNa4H zf~MI%12AIpeYD(n4W)ABI+FvZ&lnt;DfxB*JoPbx^{GS>UoGGZ1#5v)Oa1=|(C-Mg zfW>_xzc2H5Vtfw9PDs{X?gv6=PuNz;!td4m9hO(o8Q|x(q|o6ZeyWQ4{Qf<t3?5L@ ztXJ#6>EMyx8QA|sl?Dvwn2PX%3YdIQmGxJU5DHznVvAJN=l6MeBM&;C-=Qjtv;Dh4 zrL(i5e|26qK@L&YSOD)7R$)1Y-5}Fh9OHaGFr)PQl^&rKC&#bIB7zFpW_><KkY5ad z&h2FXtk3kM(r*Q%GBi(9Py*pq4JLicvs3?R&`5;OKlxnZ0u2fjK3BCcD^{)(9Kd7O z=ktqEr9WX8M!iAd3~9e+*XQ@b^D5AHRUX+4>oI=Dq0i?brpJ=tb5oY*JTjhB`n(4o zQv2E=rSFtyoZ)wsKKCDgKgiz|zQK+ZWIZPTu|uEFVf1=SB0+=3R=EF|^jD}*{@6di zxBpNzXeSFQ==A@$pp#9vV(_^Re^2R@cj9BvrBrd4PJL&8IphC(=u%TzpU>}W)%T-L zc_;pF(53dXKA$Hwd_n#HfG&-F*5~tU{(hAc%fpcUbGtrp_~-Mf73zY3<sl{u;|y<r zYLB1KvAjxuQ0XyXJ%<0~(C71q7nDB7>C|WZH>i*;Za=>ly;LE7Xa7_16n`TdRFL)m z$U!GrDh%l(1okZ%>fbJPAaWNAD!K!ec6jXD^*iseutv22d<iIh6Uuqx^^@4;{117- PKzWNh-a?CUD^&asYb5q6 diff --git a/examples/skiplist.cpp b/examples/skiplist.cpp index 601b9ec2b..062dfce7c 100644 --- a/examples/skiplist.cpp +++ b/examples/skiplist.cpp @@ -2,9 +2,52 @@ #include "data_structures/skiplist/skiplist.hpp" +using std::cout; +using std::endl; + +using skiplist_t = SkipList<int, int>; + +void print_skiplist(const skiplist_t::Accessor& skiplist) +{ + cout << "---- skiplist now has: "; + + for(auto& kv : skiplist) + cout << "(" << kv.first << ", " << kv.second << ") "; + + cout << "----" << endl; +} + int main(void) { - auto skiplist = new SkipList<int, int>(); + cout << std::boolalpha; + skiplist_t skiplist; + + auto accessor = skiplist.access(); + + cout << "added non-existing (1, 10)? (true) " + << accessor.insert_unique(1, 10).second << endl; + + cout << "added already existing (1, 10)? (false) " + << accessor.insert_unique(1, 10).second << endl; + + accessor.insert_unique(2, 20); + print_skiplist(accessor); + + cout << "value at key 3 exists? (false) " + << (accessor.find(3) == accessor.end()) << endl; + + cout << "value at key 2 exists? (true) " + << (accessor.find(2) != accessor.end()) << endl; + + cout << "at key 2 is? (20) " << accessor.find(2)->second << endl; + + cout << "removed existing (1)? (true) " << accessor.remove(1) << endl; + cout << "removed non-existing (3)? (false) " << accessor.remove(3) << endl; + + accessor.insert_unique(1, 10); + accessor.insert_unique(4, 40); + + print_skiplist(accessor); return 0; } diff --git a/memory/deferred_recycler.hpp b/memory/deferred_recycler.hpp new file mode 100644 index 000000000..f8b3c4b8d --- /dev/null +++ b/memory/deferred_recycler.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "recycler.hpp" + +template <class T, class Allocator> +class DeferredRecycler : Recycler<T, Allocator> +{ +public: + using Recycler<T, Allocator>::acquire; + + void recycle(T* item) + { + auto guard = this->acquire_unique(); + dirty.push_back(item); + } + + void clean() + { + auto guard = this->acquire_unique(); + + for(auto item : dirty) + this->recycle_or_delete(item); + + dirty.clear(); + } + +private: + std::queue<T*> dirty; +}; diff --git a/memory/lazy_gc.hpp b/memory/lazy_gc.hpp index 75f52b9c9..c3a969192 100644 --- a/memory/lazy_gc.hpp +++ b/memory/lazy_gc.hpp @@ -33,6 +33,8 @@ public: return; this->derived().vacuum(); + + dirty.store(false, std::memory_order_release); } protected: diff --git a/memory/recycler.hpp b/memory/recycler.hpp index b9fe797ac..1d4c174b6 100644 --- a/memory/recycler.hpp +++ b/memory/recycler.hpp @@ -7,7 +7,7 @@ #include "threading/sync/spinlock.hpp" template <class T, class Allocator=std::allocator<T>> -class Recycler : Lockable<SpinLock> +class Recycler : public Lockable<SpinLock> { static constexpr size_t default_max_reserved = 100; @@ -19,17 +19,28 @@ public: T* acquire(Args&&... args) { auto guard = acquire_unique(); - return new T(std::forward<Args>(args)...); // todo refactor :D + return fetch_or_create(std::forward<Args>(args)...); } void release(T* item) { auto guard = acquire_unique(); - delete item; // todo refactor :D + return recycle_or_delete(item); } -private: +protected: Allocator alloc; size_t max_reserved {default_max_reserved}; std::queue<T*> items; + + template <class... Args> + T* fetch_or_create(Args&&... args) + { + return new T(std::forward<Args>(args)...); // todo refactor :D + } + + void recycle_or_delete(T* item) + { + delete item; // todo refactor :D + } }; diff --git a/utils/auto_scope.hpp b/utils/auto_scope.hpp index caa7f09d4..bdc71f411 100644 --- a/utils/auto_scope.hpp +++ b/utils/auto_scope.hpp @@ -18,7 +18,7 @@ * and the app is left in an inconsistent state. ideally, you would like * to call resource.disable regardles of the exception being thrown. * OnScopeExit makes this possible and very convenient via a 'Auto' macro - * + * * void hard_worker() * { * resource.enable();