- /*
- Word-Based Portable Proxy Garbage Collector
- Copyright (C) 2010 Christopher Michael Thomasson
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- //#define RL_DEBUGBREAK_ON_ASSERT
- //#define RL_MSVC_OUTPUT
- //#define RL_FORCE_SEQ_CST
- #include <relacy/relacy_std.hpp>
- #include <cstdio>
- #include <cstddef>
- #if ! defined (NDEBUG)
- # define DBG_PRINTF(e) std::printf e
- #else
- # define DBG_PRINTF(e) ((void)0)
- #endif
- struct node
- {
- std::atomic<node*> m_next;
- VAR_T(node*) m_defer_next;
- };
- template<size_t T_defer_limit>
- class proxy
- {
- static void prv_destroy(node* n)
- {
- while (n)
- {
- node* next = VAR(n->m_defer_next);
- delete n;
- n = next;
- }
- }
- public:
- class collector
- {
- friend class proxy;
- private:
- std::atomic<node*> m_defer;
- std::atomic<unsigned int> m_defer_count;
- std::atomic<unsigned int> m_count;
- public:
- collector()
- : m_defer(NULL),
- m_defer_count(0),
- m_count(0)
- {
- }
- ~collector()
- {
- prv_destroy(m_defer.load(std::memory_order_relaxed));
- }
- };
- private:
- std::atomic<unsigned int> m_current;
- std::atomic<bool> m_quiesce;
- VAR_T(node*) m_defer;
- collector m_collectors[2];
- public:
- proxy()
- : m_current(0),
- m_quiesce(false),
- m_defer(NULL)
- {
- }
- ~proxy()
- {
- prv_destroy(VAR(m_defer));
- }
- private:
- void prv_quiesce_begin()
- {
- // Try to begin the quiescence process.
- if (! m_quiesce.exchange(true, std::memory_order_acquire))
- {
- // advance the current collector and grab the old one.
- unsigned int old = m_current.load(std::memory_order_relaxed) & 0xFU;
- old = m_current.exchange((old + 1) & 1, std::memory_order_acq_rel);
- collector& c = m_collectors[old & 0xFU];
- // decode reference count.
- unsigned int refs = old & 0xFFFFFFF0U;
- // verify reference count and previous collector index.
- RL_ASSERT(! (refs & 0x10U) && (old & 0xFU) == (&c - m_collectors));
- // increment and generate an odd reference count.
- if (c.m_count.fetch_add(refs + 0x10U, std::memory_order_release) == -refs)
- {
- // odd reference count and drop-to-zero condition detected!
- prv_quiesce_complete(c);
- }
- }
- }
- void prv_quiesce_complete(collector& c)
- {
- // the collector `c' is now in a quiescent state! :^)
- std::atomic_thread_fence(std::memory_order_acquire);
- // maintain the back link and obtain "fresh" objects from
- // this collection.
- node* n = VAR(m_defer);
- VAR(m_defer) = c.m_defer.load(std::memory_order_relaxed);
- c.m_defer.store(0, std::memory_order_relaxed);
- // verify and reset the reference count.
- RL_ASSERT(c.m_count.load(std::memory_order_relaxed) == 0x10U);
- c.m_count.store(0, std::memory_order_relaxed);
- c.m_defer_count.store(0, std::memory_order_relaxed);
- // release the quiesce lock.
- m_quiesce.store(false, std::memory_order_release);
- // destroy nodes.
- prv_destroy(n);
- }
- public:
- collector& acquire()
- {
- // increment the master count _and_ obtain current collector.
- unsigned int current =
- m_current.fetch_add(0x20U, std::memory_order_acquire);
- // decode the collector index.
- return m_collectors[current & 0xFU];
- }
- void release(collector& c)
- {
- // decrement the collector.
- unsigned int count =
- c.m_count.fetch_sub(0x20U, std::memory_order_release);
- // check for the completion of the quiescence process.
- if ((count & 0xFFFFFFF0U) == 0x30U)
- {
- // odd reference count and drop-to-zero condition detected!
- prv_quiesce_complete(c);
- }
- }
- collector& sync(collector& c)
- {
- // check if the `c' is in the middle of a quiescence process.
- if (c.m_count.load(std::memory_order_relaxed) & 0x10U)
- {
- // drop `c' and get the next collector.
- release(c);
- return acquire();
- }
- return c;
- }
- void collect()
- {
- prv_quiesce_begin();
- }
- void collect(collector& c, node* n)
- {
- if (! n) return;
- // link node into the defer list.
- node* prev = c.m_defer.exchange(n, std::memory_order_relaxed);
- VAR(n->m_defer_next) = prev;
- // bump the defer count and begin quiescence process if over
- // the limit.
- unsigned int count =
- c.m_defer_count.fetch_add(1, std::memory_order_relaxed) + 1;
- if (count >= (T_defer_limit / 2))
- {
- prv_quiesce_begin();
- }
- }
- };
- // you're basic lock-free stack...
- // well, minus ABA counter and DWCAS of course! ;^)
- class stack
- {
- std::atomic<node*> m_head;
- public:
- stack()
- : m_head(NULL)
- {
- }
- public:
- void push(node* n)
- {
- node* head = m_head.load(std::memory_order_relaxed);
- do
- {
- n->m_next.store(head, std::memory_order_relaxed);
- }
- while (! m_head.compare_exchange_weak(
- head,
- n,
- std::memory_order_release));
- }
- node* flush()
- {
- return m_head.exchange(NULL, std::memory_order_acquire);
- }
- node* get_head()
- {
- return m_head.load(std::memory_order_acquire);
- }
- node* pop()
- {
- node* head = m_head.load(std::memory_order_acquire);
- node* xchg;
- do
- {
- if (! head) return NULL;
- xchg = head->m_next.load(std::memory_order_relaxed);
- }
- while (! m_head.compare_exchange_weak(
- head,
- xchg,
- std::memory_order_acquire));
- return head;
- }
- };
- #define ITERS 15
- #define DEFER 6
- #define WRITERS 2
- #define READERS 3
- #define REAPERS 1
- #define THREADS (WRITERS + READERS + REAPERS)
- struct proxy_test
- : rl::test_suite<proxy_test, THREADS>
- {
- typedef proxy<DEFER> proxy_type;
- proxy_type g_proxy;
- stack g_stack;
- unsigned g_writers; // for proper test termination only.
- void before()
- {
- g_writers = WRITERS;
- }
- void thread(unsigned int tidx)
- {
- rl::backoff b;
- if (tidx < READERS)
- {
- proxy_type::collector* c = &g_proxy.acquire();
- // readers.
- while (g_writers)
- {
- node* n = g_stack.get_head();
- while (n)
- {
- node* next = n->m_next.load(std::memory_order_relaxed);
- n = next;
- }
- c = &g_proxy.sync(*c);
- b.yield($);
- }
- g_proxy.release(*c);
- }
- else if (tidx < WRITERS + READERS)
- {
- // writers.
- unsigned count = 0;
- for (unsigned int i = 0; i < ITERS; ++i)
- {
- g_stack.push(new node);
- if (! (i % 2))
- {
- proxy_type::collector& c = g_proxy.acquire();
- g_proxy.collect(c, g_stack.pop());
- g_proxy.release(c);
- b.yield($);
- }
- }
- for (unsigned int i = count; i < ITERS; ++i)
- {
- proxy_type::collector& c = g_proxy.acquire();
- g_proxy.collect(c, g_stack.pop());
- g_proxy.release(c);
- }
- --g_writers;
- }
- else if (tidx < WRITERS + READERS + REAPERS)
- {
- // reapers.
- while (g_writers)
- {
- g_proxy.collect();
- b.yield($);
- }
- }
- }
- };
- int main()
- {
- {
- rl::test_params p;
- p.iteration_count = 10000000;
- p.execution_depth_limit = 10000;
- //p.search_type = rl::sched_bound;
- //p.search_type = rl::fair_full_search_scheduler_type;
- //p.search_type = rl::fair_context_bound_scheduler_type;
- rl::simulate<proxy_test>(p);
- }
- return 0;
- }
SHARE
TWEET
Chris M Thomasson
a guest
Jan 23rd, 2010
502
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy.

