320 lines
12 KiB
C++
320 lines
12 KiB
C++
#pragma once
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
namespace YSG {
|
|
static constexpr std::uint8_t triple_buffer_invalid_index = 255;
|
|
/// 三缓冲操作结果。
|
|
enum class Triple_Buffer_Result : std::uint8_t {
|
|
/// 操作成功。
|
|
Success,
|
|
/// 目标物理缓冲正在被占用。
|
|
Busy,
|
|
/// 角色映射在操作期间发生变化。
|
|
State_Changed,
|
|
/// 交换条件拒绝,不代表错误。
|
|
Condition_Failed
|
|
};
|
|
/// 三缓冲获取模式。
|
|
enum class Triple_Buffer_Mode : std::uint8_t {
|
|
/// 失败立即返回。
|
|
Try,
|
|
/// 等待缓冲释放。
|
|
Wait
|
|
};
|
|
/// 三个逻辑角色到三个物理缓冲的当前映射。
|
|
struct Triple_Buffer_View {
|
|
std::array<std::uint8_t, 3> index{};
|
|
/// 返回指定角色当前对应的物理缓冲索引。
|
|
std::uint8_t operator[](std::uint8_t role) const {
|
|
return index[role];
|
|
}
|
|
};
|
|
/// 一次物理缓冲占用租约。
|
|
struct Triple_Buffer_Lease {
|
|
std::uint8_t index = triple_buffer_invalid_index;
|
|
/// 判断租约是否持有有效物理缓冲。
|
|
explicit operator bool() const {
|
|
return index != triple_buffer_invalid_index;
|
|
}
|
|
};
|
|
struct Triple_Buffer_Mark_Record {
|
|
Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed;
|
|
Triple_Buffer_Lease lease{};
|
|
std::uint8_t busy_index = triple_buffer_invalid_index;
|
|
};
|
|
struct Triple_Buffer_Swap_Record {
|
|
Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed;
|
|
Triple_Buffer_View old_view{};
|
|
Triple_Buffer_View new_view{};
|
|
std::uint8_t busy_index = triple_buffer_invalid_index;
|
|
};
|
|
struct alignas(64) Triple_Buffer_Busy {
|
|
std::atomic_bool busy{false};
|
|
};
|
|
struct Triple_Buffer_Stats {
|
|
std::atomic<std::uint64_t> mark_success{0};
|
|
std::atomic<std::uint64_t> mark_busy{0};
|
|
std::atomic<std::uint64_t> mark_state_changed{0};
|
|
std::atomic<std::uint64_t> swap_success{0};
|
|
std::atomic<std::uint64_t> swap_busy{0};
|
|
std::atomic<std::uint64_t> swap_state_changed{0};
|
|
std::atomic<std::uint64_t> swap_condition_failed{0};
|
|
std::atomic<std::uint64_t> wait_count{0};
|
|
};
|
|
class Triple_Role_Buffer_Control {
|
|
public:
|
|
/// 设置性能统计写入目标。
|
|
void set_stats(Triple_Buffer_Stats* value) {
|
|
stats = value;
|
|
}
|
|
/// 重置角色映射和全部 busy 标记。
|
|
void reset() {
|
|
slot_state.store(0, std::memory_order_release);
|
|
for (auto& item : buffer_busy) {
|
|
item.busy.store(false, std::memory_order_release);
|
|
item.busy.notify_all();
|
|
}
|
|
slot_state.notify_all();
|
|
}
|
|
Triple_Buffer_View read_view() const {
|
|
return decode_order(slot_state.load(std::memory_order_acquire));
|
|
}
|
|
/// 尝试占用指定物理缓冲。
|
|
Triple_Buffer_Mark_Record try_mark_use_index(std::uint8_t index) {
|
|
if (!try_mark_index(index)) {
|
|
record_mark(Triple_Buffer_Result::Busy);
|
|
return {Triple_Buffer_Result::Busy, {}, index};
|
|
}
|
|
record_mark(Triple_Buffer_Result::Success);
|
|
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
|
|
}
|
|
/// 等待并占用指定物理缓冲。
|
|
Triple_Buffer_Lease wait_mark_use_index(std::uint8_t index) {
|
|
for (;;) {
|
|
if (try_mark_index(index)) {
|
|
record_mark(Triple_Buffer_Result::Success);
|
|
return {index};
|
|
}
|
|
record_mark(Triple_Buffer_Result::Busy);
|
|
record_wait();
|
|
wait_index_free(index);
|
|
}
|
|
}
|
|
/// 尝试占用指定角色当前对应的物理缓冲。
|
|
Triple_Buffer_Mark_Record try_mark_use_role(std::uint8_t role) {
|
|
auto order = slot_state.load(std::memory_order_acquire);
|
|
auto view = decode_order(order);
|
|
auto index = view[role];
|
|
if (!try_mark_index(index)) {
|
|
record_mark(Triple_Buffer_Result::Busy);
|
|
return {Triple_Buffer_Result::Busy, {}, index};
|
|
}
|
|
auto cur_order = slot_state.load(std::memory_order_acquire);
|
|
if (cur_order != order) {
|
|
unmark_index(index);
|
|
record_mark(Triple_Buffer_Result::State_Changed);
|
|
return {Triple_Buffer_Result::State_Changed, {}, triple_buffer_invalid_index};
|
|
}
|
|
record_mark(Triple_Buffer_Result::Success);
|
|
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
|
|
}
|
|
/// 等待并占用指定角色当前对应的物理缓冲。
|
|
Triple_Buffer_Lease wait_mark_use_role(std::uint8_t role) {
|
|
for (;;) {
|
|
auto record = try_mark_use_role(role);
|
|
if (record.result == Triple_Buffer_Result::Success) {
|
|
return record.lease;
|
|
}
|
|
if (record.result == Triple_Buffer_Result::Busy) {
|
|
record_wait();
|
|
wait_index_free(record.busy_index);
|
|
}
|
|
}
|
|
}
|
|
/// 释放租约持有的物理缓冲。
|
|
void unmark_use(Triple_Buffer_Lease lease) {
|
|
unmark_index(lease.index);
|
|
}
|
|
/// 尝试交换两个角色的物理缓冲映射。
|
|
template <typename Can_Swap>
|
|
Triple_Buffer_Swap_Record try_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap) {
|
|
std::uint8_t busy_index = triple_buffer_invalid_index;
|
|
return try_swap_role_once(left_role, right_role, can_swap, busy_index);
|
|
}
|
|
/// 等待必要缓冲后交换两个角色的物理缓冲映射。
|
|
template <typename Can_Swap>
|
|
Triple_Buffer_Swap_Record wait_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap) {
|
|
for (;;) {
|
|
std::uint8_t busy_index = triple_buffer_invalid_index;
|
|
auto record = try_swap_role_once(left_role, right_role, can_swap, busy_index);
|
|
if (record.result == Triple_Buffer_Result::Success || record.result == Triple_Buffer_Result::Condition_Failed) {
|
|
return record;
|
|
}
|
|
if (record.result == Triple_Buffer_Result::Busy) {
|
|
record_wait();
|
|
wait_index_free(busy_index);
|
|
}
|
|
else {
|
|
record_wait();
|
|
slot_state.wait(encode_order(record.old_view), std::memory_order_acquire);
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
std::atomic<std::uint8_t> slot_state{0};
|
|
std::array<Triple_Buffer_Busy, 3> buffer_busy{};
|
|
Triple_Buffer_Stats* stats{};
|
|
void record_mark(Triple_Buffer_Result result) {
|
|
if (!stats)
|
|
return;
|
|
switch (result) {
|
|
case Triple_Buffer_Result::Success:
|
|
stats->mark_success.fetch_add(1, std::memory_order_relaxed);
|
|
break;
|
|
case Triple_Buffer_Result::Busy:
|
|
stats->mark_busy.fetch_add(1, std::memory_order_relaxed);
|
|
break;
|
|
case Triple_Buffer_Result::State_Changed:
|
|
stats->mark_state_changed.fetch_add(1, std::memory_order_relaxed);
|
|
break;
|
|
case Triple_Buffer_Result::Condition_Failed:
|
|
break;
|
|
}
|
|
}
|
|
void record_swap(Triple_Buffer_Result result) {
|
|
if (!stats)
|
|
return;
|
|
switch (result) {
|
|
case Triple_Buffer_Result::Success:
|
|
stats->swap_success.fetch_add(1, std::memory_order_relaxed);
|
|
break;
|
|
case Triple_Buffer_Result::Busy:
|
|
stats->swap_busy.fetch_add(1, std::memory_order_relaxed);
|
|
break;
|
|
case Triple_Buffer_Result::State_Changed:
|
|
stats->swap_state_changed.fetch_add(1, std::memory_order_relaxed);
|
|
break;
|
|
case Triple_Buffer_Result::Condition_Failed:
|
|
stats->swap_condition_failed.fetch_add(1, std::memory_order_relaxed);
|
|
break;
|
|
}
|
|
}
|
|
void record_wait() {
|
|
if (stats)
|
|
stats->wait_count.fetch_add(1, std::memory_order_relaxed);
|
|
}
|
|
static Triple_Buffer_View decode_order(std::uint8_t order) {
|
|
switch (order) {
|
|
case 0:
|
|
return {{0, 1, 2}};
|
|
case 1:
|
|
return {{0, 2, 1}};
|
|
case 2:
|
|
return {{1, 0, 2}};
|
|
case 3:
|
|
return {{1, 2, 0}};
|
|
case 4:
|
|
return {{2, 0, 1}};
|
|
case 5:
|
|
return {{2, 1, 0}};
|
|
}
|
|
return {{0, 1, 2}};
|
|
}
|
|
static std::uint8_t encode_order(const Triple_Buffer_View& view) {
|
|
if (view.index[0] == 0) {
|
|
return view.index[1] == 1 ? 0 : 1;
|
|
}
|
|
if (view.index[0] == 1) {
|
|
return view.index[1] == 0 ? 2 : 3;
|
|
}
|
|
return view.index[1] == 0 ? 4 : 5;
|
|
}
|
|
static std::uint8_t swap_order(std::uint8_t order, std::uint8_t left_role, std::uint8_t right_role) {
|
|
auto view = decode_order(order);
|
|
auto temp = view.index[left_role];
|
|
view.index[left_role] = view.index[right_role];
|
|
view.index[right_role] = temp;
|
|
return encode_order(view);
|
|
}
|
|
bool try_mark_index(std::uint8_t index) {
|
|
bool expected = false;
|
|
return buffer_busy[index].busy.compare_exchange_strong(expected, true, std::memory_order_acq_rel, std::memory_order_acquire);
|
|
}
|
|
void unmark_index(std::uint8_t index) {
|
|
buffer_busy[index].busy.store(false, std::memory_order_release);
|
|
buffer_busy[index].busy.notify_all();
|
|
}
|
|
void wait_index_free(std::uint8_t index) {
|
|
while (buffer_busy[index].busy.load(std::memory_order_acquire)) {
|
|
buffer_busy[index].busy.wait(true, std::memory_order_acquire);
|
|
}
|
|
}
|
|
bool try_mark_two_indices(std::uint8_t left_index, std::uint8_t right_index, std::uint8_t& busy_index) {
|
|
if (left_index == right_index) {
|
|
if (!try_mark_index(left_index)) {
|
|
busy_index = left_index;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
auto first = left_index < right_index ? left_index : right_index;
|
|
auto second = left_index < right_index ? right_index : left_index;
|
|
if (!try_mark_index(first)) {
|
|
busy_index = first;
|
|
return false;
|
|
}
|
|
if (!try_mark_index(second)) {
|
|
unmark_index(first);
|
|
busy_index = second;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
void unmark_two_indices(std::uint8_t left_index, std::uint8_t right_index) {
|
|
if (left_index == right_index) {
|
|
unmark_index(left_index);
|
|
return;
|
|
}
|
|
unmark_index(left_index);
|
|
unmark_index(right_index);
|
|
}
|
|
template <typename Can_Swap>
|
|
Triple_Buffer_Swap_Record try_swap_role_once(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, std::uint8_t& busy_index) {
|
|
auto old_order = slot_state.load(std::memory_order_acquire);
|
|
auto old_view = decode_order(old_order);
|
|
auto left_index = old_view[left_role];
|
|
auto right_index = old_view[right_role];
|
|
if (!try_mark_two_indices(left_index, right_index, busy_index)) {
|
|
record_swap(Triple_Buffer_Result::Busy);
|
|
return {Triple_Buffer_Result::Busy, old_view, old_view, busy_index};
|
|
}
|
|
auto cur_order = slot_state.load(std::memory_order_acquire);
|
|
if (cur_order != old_order) {
|
|
auto cur_view = decode_order(cur_order);
|
|
unmark_two_indices(left_index, right_index);
|
|
record_swap(Triple_Buffer_Result::State_Changed);
|
|
return {Triple_Buffer_Result::State_Changed, old_view, cur_view, triple_buffer_invalid_index};
|
|
}
|
|
if (!can_swap(old_view)) {
|
|
unmark_two_indices(left_index, right_index);
|
|
record_swap(Triple_Buffer_Result::Condition_Failed);
|
|
return {Triple_Buffer_Result::Condition_Failed, old_view, old_view, triple_buffer_invalid_index};
|
|
}
|
|
auto new_order = swap_order(old_order, left_role, right_role);
|
|
auto new_view = decode_order(new_order);
|
|
auto expected = old_order;
|
|
if (!slot_state.compare_exchange_strong(expected, new_order, std::memory_order_acq_rel, std::memory_order_acquire)) {
|
|
auto cur_view = decode_order(expected);
|
|
unmark_two_indices(left_index, right_index);
|
|
record_swap(Triple_Buffer_Result::State_Changed);
|
|
return {Triple_Buffer_Result::State_Changed, old_view, cur_view, triple_buffer_invalid_index};
|
|
}
|
|
unmark_two_indices(left_index, right_index);
|
|
slot_state.notify_all();
|
|
record_swap(Triple_Buffer_Result::Success);
|
|
return {Triple_Buffer_Result::Success, old_view, new_view, triple_buffer_invalid_index};
|
|
}
|
|
};
|
|
} // namespace YSG
|