|
|
- #pragma once
-
- #include "gp/system/process_data.hpp"
-
- #include <atomic>
- #include <new>
-
- namespace gp {
- namespace system {
- struct task_queue{
- struct node{
- alignas(gp_config::limits::hardware_constructive_interference_size)
- std::atomic_bool is_locked;
- gp::system::process_data* value;
- alignas(gp_config::limits::hardware_constructive_interference_size)
- std::atomic<struct node*> next;
-
- node() noexcept
- {
- is_locked = false;
- value = nullptr;
- next = nullptr;
- }
-
- node(node&& v) noexcept
- {
- 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<struct node*>;
-
- task_queue() noexcept
- : start{nullptr}
- , end{nullptr}
- {}
-
- node_ptr_rep start;
- node_ptr_rep end;
-
- // NODES ARE ACQUIRED ON POP
- node_ptr try_pop() noexcept {
- 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) noexcept {
- 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;
- }
- }
- }
- };
- }
- }
|