Chris_M_Thomasson

Simple Proxy Collector DWCAS - Relacy

Mar 27th, 2018
436
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.32 KB | None | 0 0
  1. /* Simple Proxy Collector (DWCAS) - Relacy Version
  2.  
  3. http://www.1024cores.net/home/relacy-race-detector
  4.  
  5. Copyright 3/27/2018
  6. ___________________________________________________*/
  7.  
  8.  
  9. #include <relacy/relacy_std.hpp>
  10. #include <cstdio>
  11. #include <climits>
  12.  
  13.  
  14. /* Membar Abstraction
  15. ___________________________________________________*/
  16. #define ct_mb_relaxed std::memory_order_relaxed
  17. #define ct_mb_acquire std::memory_order_acquire
  18. #define ct_mb_release std::memory_order_release
  19. #define ct_mb_seq_cst std::memory_order_seq_cst
  20. #define ct_mb_fence(mb_membar) std::atomic_thread_fence(mb_membar)
  21.  
  22.  
  23. /* Simple Proxy Collector C++11
  24. ___________________________________________________*/
  25. namespace ct {
  26. namespace proxy {
  27.  
  28.     // User object base
  29.     struct object
  30.     {
  31.         virtual ~object() {}
  32.     };
  33.  
  34.  
  35.     // Proxy node
  36.     struct node
  37.     {
  38.         std::atomic<std::intptr_t> count;
  39.         VAR_T(node*) next;
  40.         VAR_T(object*) obj;
  41.  
  42.         node(std::intptr_t count_, node* next_, object* obj_)
  43.             : count(count_), next(next_), obj(obj_)
  44.         {
  45.  
  46.         }
  47.  
  48.         ~node()
  49.         {
  50.  
  51.         }
  52.     };
  53.  
  54.  
  55.     // DWCAS target
  56.     struct anchor
  57.     {
  58.         std::intptr_t count;
  59.         node* head;
  60.  
  61.         anchor()
  62.         {
  63.  
  64.         }
  65.  
  66.         anchor(std::intptr_t count_, node* head_)
  67.         {
  68.             count = count_;
  69.             head = head_;
  70.         }
  71.  
  72.         bool operator == (anchor const& right) const
  73.         {
  74.             return count == right.count && head == right.head;
  75.         }
  76.  
  77.         anchor operator + (anchor const& right) const
  78.         {
  79.             anchor res;
  80.             res.count = count + right.count;
  81.             res.head = right.head;
  82.             return res;
  83.         }
  84.  
  85.         anchor operator - (anchor const& right) const
  86.         {
  87.             anchor res;
  88.             res.count = count - right.count;
  89.             res.head = right.head;
  90.             return res;
  91.         }
  92.     };
  93.  
  94.     std::ostream& operator << (std::ostream& s, anchor const& right)
  95.     {
  96.         return s << "{" << right.count << "," << right.head << "}";
  97.     }
  98.  
  99.  
  100.     // Collector Logic
  101.     struct gc
  102.     {
  103.         std::atomic<anchor> head;
  104.  
  105.         gc() : head(anchor{ 0, new node(0, nullptr, nullptr) }) {}
  106.  
  107.         ~gc()
  108.         {
  109.             anchor cmp = head.load(ct_mb_relaxed);
  110.             RL_ASSERT(cmp.count > -1);
  111.             RL_ASSERT(VAR(cmp.head->next) == nullptr);
  112.             prv_dtor(cmp.head);
  113.         }
  114.  
  115.         // Drops a reference count on a node
  116.         bool prv_release(node* n)
  117.         {
  118.             std::intptr_t count = n->count.fetch_sub(2, ct_mb_relaxed);
  119.             if (count == 3) return true;
  120.             return false;
  121.         }
  122.  
  123.         // Deletes a node
  124.         void prv_dtor(node* n)
  125.         {
  126.             object* obj = VAR(n->obj);
  127.             if (obj != nullptr) delete obj;
  128.             delete n;
  129.         }
  130.  
  131.         // Dumps backlinked nodes
  132.         void prv_dump(node* n)
  133.         {
  134.             node* cur = VAR(n->next);
  135.             VAR(n->next) = nullptr;
  136.  
  137.             // Release and collect in cur LIFO
  138.             while (prv_release(cur))
  139.             {
  140.                 ct_mb_fence(ct_mb_acquire);
  141.                 node* next = VAR(cur->next);
  142.                 VAR(cur->next) = n;
  143.                 n = cur;
  144.                 cur = next;
  145.             }
  146.  
  147.             // Destroy cur LIFO
  148.             while (n)
  149.             {
  150.                 node* next = VAR(n->next);
  151.                 prv_dtor(n);
  152.                 n = next;
  153.             }
  154.         }
  155.  
  156.         // Collects a node
  157.         void prv_collect(node* n)
  158.         {
  159.             anchor xchg = { 0, n };
  160.  
  161.             ct_mb_fence(ct_mb_release);
  162.  
  163.             // Swap in new node
  164.             anchor cmp = head.exchange(xchg, ct_mb_relaxed);
  165.  
  166.             // Backlink
  167.             ct_mb_fence(ct_mb_acquire);
  168.             VAR(cmp.head->next) = xchg.head;
  169.             ct_mb_fence(ct_mb_release);
  170.  
  171.             // Release and mark as collected
  172.             std::intptr_t count = cmp.head->count.fetch_add(cmp.count + 1, ct_mb_relaxed);
  173.  
  174.             if (count + cmp.count == 0)
  175.             {
  176.                 prv_dump(cmp.head);
  177.             }
  178.         }
  179.  
  180.        
  181.         // Acquires current node
  182.         node* acquire()
  183.         {
  184.             anchor cmp = head.load(ct_mb_relaxed);
  185.             anchor xchg = { cmp.count + 2, cmp.head };
  186.  
  187.             while (! head.compare_exchange_weak(cmp, xchg, ct_mb_relaxed))
  188.             {
  189.                 xchg = { cmp.count + 2, cmp.head };
  190.             }
  191.  
  192.             ct_mb_fence(ct_mb_acquire);
  193.  
  194.             return cmp.head;
  195.         }
  196.        
  197.  
  198.         // Release a node
  199.         void release(node* n)
  200.         {
  201.             ct_mb_fence(ct_mb_release);
  202.  
  203.             if (prv_release(n))
  204.             {
  205.                 ct_mb_fence(ct_mb_acquire);
  206.                 prv_dump(n);
  207.             }
  208.         }
  209.  
  210.         // Collects an object
  211.         void collect(object* obj)
  212.         {
  213.             prv_collect(new node(2, nullptr, obj));
  214.         }
  215.     };
  216. }}
  217.  
  218.  
  219. // Test object
  220. struct foo : public ct::proxy::object
  221. {
  222.     std::atomic<foo*> next;
  223.  
  224.     foo(foo* next_ = nullptr) : next(next_) { }
  225.  
  226.     virtual ~foo()
  227.     {
  228.         //std::cout << "foo::~foo()\n";
  229.     }
  230. };
  231.  
  232. // An atomic LIFO of test objects
  233. struct foo_lifo
  234. {
  235.     std::atomic<foo*> head;
  236.  
  237.     foo_lifo(foo* next_ = nullptr) : head(next_) { }
  238.  
  239.     void push(foo* n)
  240.     {
  241.         foo* cmp = head.load(ct_mb_relaxed);
  242.  
  243.         do {
  244.             n->next.store(cmp, ct_mb_relaxed);
  245.             ct_mb_fence(ct_mb_release);
  246.         }  while (! head.compare_exchange_weak(cmp, n, ct_mb_relaxed));
  247.     }
  248.  
  249.     foo* flush()
  250.     {
  251.         foo* cmp = head.exchange(nullptr, ct_mb_relaxed);
  252.         if (cmp) ct_mb_fence(ct_mb_acquire);
  253.         return cmp;
  254.     }
  255. };
  256.  
  257.  
  258. // Program Logic
  259. #define READERS 5
  260. #define WRITERS 3
  261. #define ITERS 5
  262. #define THREADS (READERS + WRITERS)
  263.  
  264. struct ct_proxy_test
  265.     : rl::test_suite<ct_proxy_test, THREADS>
  266. {
  267.     ct::proxy::gc g_gc;
  268.     foo_lifo g_lifo;
  269.  
  270.  
  271.     void before()
  272.     {
  273.  
  274.         //std::printf("before()\n");
  275.     }
  276.  
  277.  
  278.     void after()
  279.     {
  280.         //std::printf("after()\n");
  281.     }
  282.  
  283.  
  284.     // Reader threads...
  285.     void thread_reader(unsigned int tidx)
  286.     {
  287.         //std::printf("thread_reader::%u\n", tidx);
  288.  
  289.         ct::proxy::node* pcn = g_gc.acquire();
  290.  
  291.         {
  292.             foo* cur = g_lifo.head.load(ct_mb_relaxed);
  293.             ct_mb_fence(ct_mb_acquire);
  294.  
  295.             while (cur)
  296.             {
  297.                 foo* next = cur->next.load(ct_mb_relaxed);
  298.                 rl::yield(1, $);
  299.                 cur = next;
  300.             }
  301.         }
  302.  
  303.         g_gc.release(pcn);
  304.     }
  305.  
  306.  
  307.     // Writer threads...
  308.     void thread_writer(unsigned int tidx)
  309.     {
  310.         //std::printf("thread_writer::%u\n", tidx);
  311.  
  312.         for (unsigned int i = 0; i < ITERS; ++i)
  313.         {
  314.             // Add some nodes, and peform some collections...
  315.             g_lifo.push(new foo());
  316.             g_lifo.push(new foo());
  317.  
  318.             if (! (i % 2)) g_gc.collect(nullptr);
  319.  
  320.             rl::yield(1, $);
  321.  
  322.             g_lifo.push(new foo());
  323.  
  324.             if (! (i % 3)) g_lifo.push(new foo());
  325.  
  326.             // Flush
  327.             foo* cur = g_lifo.flush();
  328.  
  329.             // Collect the foo's... ;^)
  330.             while (cur)
  331.             {
  332.                 foo* next = cur->next.load(ct_mb_relaxed);
  333.                 rl::yield(1, $);
  334.                 g_gc.collect(cur);
  335.                 cur = next;
  336.             }
  337.         }
  338.     }
  339.    
  340.  
  341.     // Entry...
  342.     void thread(unsigned int tidx)
  343.     {
  344.         if (tidx < READERS)
  345.         {
  346.             thread_reader(tidx);
  347.         }
  348.  
  349.         else
  350.         {
  351.             thread_writer(tidx);
  352.         }
  353.     }
  354. };
  355.  
  356.  
  357.  
  358. int main()
  359. {
  360.     {
  361.         rl::test_params p;
  362.  
  363.         p.iteration_count = 12345600; // for existing proxy gc code
  364.         //p.execution_depth_limit = 3333;
  365.         //p.search_type = rl::random_scheduler_type;
  366.  
  367.         //p.search_type = rl::sched_bound;
  368.         //p.search_type = rl::fair_full_search_scheduler_type;
  369.         //p.search_type = rl::fair_context_bound_scheduler_type;
  370.  
  371.         rl::simulate<ct_proxy_test>(p);
  372.     }
  373.  
  374.     std::puts("\nTest Complete!\n");
  375.     std::fflush(stdout);
  376.     std::getchar();
  377.  
  378.     return 0;
  379. }
Advertisement
Add Comment
Please, Sign In to add comment