// Simple Atomic Stack
// For Academic and Experimental things...
// Beginner, Moderate?
// In Good Ol' Relacy! Nice as always.
//_______________________________________________
//#define RL_DEBUGBREAK_ON_ASSERT
//#define RL_MSVC_OUTPUT
//#define RL_FORCE_SEQ_CST
//#define RL_GC
#include <relacy/relacy_std.hpp>
#include <iostream>
// Simple macro based redirection of the verbose std membars.
#define CT_MB_ACQ std::memory_order_acquire
#define CT_MB_REL std::memory_order_release
#define CT_MB_RLX std::memory_order_relaxed
#define CT_MB_ACQ_REL std::memory_order_acq_rel
#define CT_MB_SEQ_CST std::memory_order_seq_cst
#define CT_MB(mp_0) std::atomic_thread_fence(mp_0)
// Some global vars directing the show...
#define THREADS 3
#define ITERS 2
// Experimental Stack
struct ct_ecstack
{
#define WAITNEXT ((node*)0xDEADBEEF)
struct node
{
std::atomic<node*> m_next;
VAR_T(unsigned int) m_data;
node(unsigned int data) : m_next(nullptr), m_data(data) {}
void process()
{
// simulate some work?
rl::yield(1 + rl::rand(2), $); // ;^)
}
node* next_wait()
{
node* next = nullptr;
while ((next = m_next.load(CT_MB_RLX)) == WAITNEXT)
{
// Humm, we can actually do other work right here...
// Hand off...
rl::yield(1, $);
}
return next;
}
};
std::atomic<node*> m_head;
ct_ecstack() : m_head(nullptr) {}
void push(node* n)
{
n->m_next.store(WAITNEXT, CT_MB_RLX);
node* head = m_head.exchange(n, CT_MB_REL); // release
n->m_next.store(head, CT_MB_RLX);
}
node* flush_try()
{
return m_head.exchange(nullptr, CT_MB_ACQ); // acquire
}
};
// Relacy Stack Test...
struct ct_ecstack_test
: rl::test_suite<ct_ecstack_test, THREADS>
{
ct_ecstack g_ecstack;
void process()
{
ct_ecstack::node* n = g_ecstack.flush_try(); // flush all
while (n)
{
// Process n first, acts like a backoff for the next wait
// Hand off some other nodes? Future note...
n->process();
// Wait for the next pointer, or hand off?
ct_ecstack::node* next = n->next_wait();
// Destroy
delete n;
// Loop on...
n = next;
}
}
void thread(unsigned int tidx)
{
for (unsigned int i = 0; i < ITERS; ++i)
{
g_ecstack.push(new ct_ecstack::node(tidx));
g_ecstack.push(new ct_ecstack::node(tidx));
g_ecstack.push(new ct_ecstack::node(tidx));
process();
g_ecstack.push(new ct_ecstack::node(tidx));
}
process();
}
};
// Test away... Or fly? Humm...
int main()
{
{
rl::test_params p;
p.iteration_count = 10000000;
//p.execution_depth_limit = 33333;
//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<ct_ecstack_test>(p);
}
return 0;
}
Any thoughts? This should be fairly basic in nature.