#pragma once #include "gp/system/process_data.hpp" #include #include namespace gp { struct topic_list{ struct node{ alignas(gp_config::limits::hardware_constructive_interference_size) std::atomic_bool is_locked; gp::process_data* value; alignas(gp_config::limits::hardware_constructive_interference_size) std::atomic next; node() { is_locked = false; value = nullptr; next = nullptr; } node(node&& v) { v.try_acquire(); is_locked = false; value = gp::move(v.value); next = v.next.load(); } bool try_acquire() noexcept { bool expected = false; return !(is_locked.compare_exchange_strong(expected, true)); } void release() noexcept { is_locked.store(false); } }; using node_ptr = struct node*; using node_ptr_rep = std::atomic; topic_list() : start{nullptr} , end{nullptr} {} node_ptr_rep start; node_ptr_rep end; // NODES ARE ACQUIRED ON POP node_ptr try_pop() { auto ptr = start.load(); if(!ptr) return nullptr; if(ptr->try_acquire()) { auto replace = ptr->next.load(); auto expected = ptr; if(end.load() == ptr) { replace = nullptr; } if(start.compare_exchange_strong(expected, replace)) { end.store(nullptr); return ptr; } else { return nullptr; } } else { return nullptr; } } // ONLY PUSH ACQUIRED NODES, // RELEASE WHEN NO LONGER IN USE bool try_push(node_ptr node) { node->next.store(nullptr); auto ed = end.load(); if(ed) { if(ed->try_acquire()) { auto old_ed = ed; node->next.store(ed); if(end.compare_exchange_strong(ed, node)) { node->release(); old_ed->release(); return true; } else { node->release(); old_ed->release(); return false; } } else return false; } else { if(end.compare_exchange_strong(ed, node)) { start.store(node); node->release(); return true; } else { return false; } } } }; }