#include "stdafx.h" #include #include "../../relacy/relacy_std.hpp" struct pc_sys_anchor; struct pc_region; struct pc_master; typedef pc_region pc_node; typedef void (pc_fp_dtor) (pc_region*); struct pc_sys_anchor { int refcnt; pc_region* region; pc_sys_anchor() { } pc_sys_anchor(int rc, pc_region* r = 0) { refcnt = rc; region = r; } bool operator == (pc_sys_anchor const& right) const { return refcnt == right.refcnt && region == right.region; } pc_sys_anchor operator + (pc_sys_anchor const& right) const { pc_sys_anchor res; res.refcnt = refcnt + right.refcnt; res.region = (pc_region*)((intptr_t)region + (intptr_t)right.region); return res; } pc_sys_anchor operator - (pc_sys_anchor const& right) const { pc_sys_anchor res; res.refcnt = refcnt - right.refcnt; res.region = (pc_region*)((intptr_t)region - (intptr_t)right.region); return res; } }; std::ostream& operator << (std::ostream& s, pc_sys_anchor const& right) { return s << "{" << right.refcnt << "," << right.region << "}"; } struct pc_region { std::atomic next; std::atomic defer; pc_region() { next($) = pc_sys_anchor(0, 0); defer($) = 0; } void link(pc_region* next) { defer.store(next, rl::memory_order_relaxed); } void defer_node(pc_region* node) { pc_region* region = defer.exchange(node, rl::memory_order_release); node->defer.store(region, rl::memory_order_relaxed); } }; struct pc_master { std::atomic head; pc_region stub_region; pc_fp_dtor* fp_dtor; pc_master(pc_fp_dtor* const dtor) { pc_sys_anchor src (0, &stub_region); head.store(src, rl::memory_order_relaxed); fp_dtor = dtor; } pc_region* acquire() { pc_sys_anchor cmp (head.load(rl::memory_order_relaxed)); pc_sys_anchor xchg; do { xchg.refcnt = cmp.refcnt + 2; xchg.region = cmp.region; } while (false == head.compare_exchange_weak(cmp, xchg, rl::memory_order_acquire)); return cmp.region; } void release(pc_region* region) { pc_sys_anchor prev = region->next.fetch_sub(2, rl::memory_order_acq_rel); if (prev.refcnt == 3) sys_dtor(region); } void mutate(pc_region* node) { pc_sys_anchor src (2, 0); node->next.store(src, rl::memory_order_relaxed); pc_sys_anchor xchg (0, node); pc_sys_anchor cmp = head.load(rl::memory_order_relaxed); while (false == head.compare_exchange_weak(cmp, xchg, std::memory_order_acq_rel)); pc_sys_anchor cmp2 = cmp.region->next.load(rl::memory_order_relaxed); pc_sys_anchor xchg2; do { xchg2 = pc_sys_anchor(cmp2.refcnt, node); } while (false == cmp.region->next.compare_exchange_weak(cmp2, xchg2, rl::memory_order_release)); pc_sys_anchor prev = cmp.region->next.fetch_add(cmp.refcnt + 1, rl::memory_order_acq_rel); if (prev.refcnt == -cmp.refcnt) sys_dtor(cmp.region); } void sys_dtor(pc_region* region) { int reset = 0; pc_region* head = region; pc_region* tail = region; pc_sys_anchor nx = region->next.load(rl::memory_order_relaxed); pc_region* next = nx.region; while (next) { pc_sys_anchor prev = next->next.fetch_sub(2, rl::memory_order_acq_rel); if (prev.refcnt != 3) break; tail = next; nx = next->next.load(rl::memory_order_relaxed); next = nx.region; } nx = tail->next.load(rl::memory_order_relaxed); nx.region = 0; tail->next.store(nx, rl::memory_order_relaxed); while (head) { nx = head->next.load(rl::memory_order_relaxed); pc_region* const next = nx.region; pc_region* defer = head->defer.load(rl::memory_order_relaxed); nx = head->next.load(rl::memory_order_relaxed); RL_ASSERT(nx.refcnt == 1); if (head != &stub_region) { head->defer.store(defer, rl::memory_order_relaxed); defer = head; } else { reset = 1; } while (defer) { pc_region* const next = defer->defer.load(rl::memory_order_relaxed); fp_dtor(defer); defer = next; } head = next; } if (reset) { stub_region.defer.store(0, rl::memory_order_relaxed); mutate(&stub_region); } } }; struct foo_node { pc_node pcn; std::atomic next; rl::var data; }; void foo_node_dtor(pc_node* pcn) { // yes, very fragile foo_node* const n = (foo_node*)pcn; delete n; } struct foo_list { std::atomic head; pc_master pc; foo_list() : head(0) , pc(foo_node_dtor) { } }; struct proxy_collector_test : rl::test_suite { foo_list m_list; void before() { m_list.head($) = 0; } void after() { foo_node* node = new foo_node; m_list.pc.mutate(&node->pcn); } void thread(unsigned index) { if (index < 2) { pc_region* pcr = m_list.pc.acquire(); for (int i = 0; i != 4; ++i) { foo_node* node = m_list.head.load(rl::memory_order_acquire); while (node) { foo_node* const next = node->next.load(rl::memory_order_acquire); intptr_t volatile data = node->data($); (void)data; node = next; } if (2 == i) { m_list.pc.release(pcr); pcr = m_list.pc.acquire(); } } m_list.pc.release(pcr); } else { pc_region* pcr = m_list.pc.acquire(); for (int i = 0; i != 4; ++i) { if (0 == (i % 2)) { foo_node* node = new foo_node; node->data($) = 1; foo_node* cmp = m_list.head.load(rl::memory_order_relaxed); do { node->next.store(cmp, rl::memory_order_relaxed); } while (false == m_list.head.compare_exchange_weak(cmp, node, rl::memory_order_release)); } else { foo_node* node = m_list.head.load(rl::memory_order_acquire); foo_node* next; do { if (0 == node) break; next = node->next.load(rl::memory_order_relaxed); } while (false == m_list.head.compare_exchange_weak(node, next, rl::memory_order_acquire)); if (node) { //if (1 == i) { m_list.pc.mutate(&node->pcn); } //else //{ // pcr->defer_node(&node->pcn); //} } } if (i % 2) { m_list.pc.release(pcr); pcr = m_list.pc.acquire(); } } m_list.pc.release(pcr); } } }; int main() { rl::test_params params; params.iteration_count = 1000; rl::simulate(params); }