mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-09-17 02:04:18 +00:00
138 lines
4.7 KiB
C++
138 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include "NomadNetMemory.h"
|
|
|
|
namespace UI::LXMF::NomadNet {
|
|
|
|
// Single-slot callback-to-main-loop handoff. Reticulum callbacks only publish
|
|
// bytes and status here; the main loop is the sole consumer and LVGL owner.
|
|
class AsyncMailbox {
|
|
public:
|
|
static constexpr std::size_t MAX_BYTES = 64 * 1024;
|
|
static constexpr std::size_t MAX_WIRE_BYTES = MAX_BYTES + 64;
|
|
enum class Kind { NONE, LINK_ESTABLISHED, LINK_CLOSED, RESPONSE, FAILED, OVERSIZED };
|
|
struct Event {
|
|
Kind kind = Kind::NONE;
|
|
ExternalVector<uint8_t> data;
|
|
std::size_t transfer_size = 0;
|
|
};
|
|
|
|
void begin(const std::vector<uint8_t>& link_token) {
|
|
Guard guard(_lock);
|
|
if (_link_token == link_token &&
|
|
(_event.kind == Kind::LINK_ESTABLISHED || _event.kind == Kind::LINK_CLOSED)) return;
|
|
_link_token = link_token;
|
|
_request_token.clear();
|
|
_event = Event{};
|
|
}
|
|
|
|
void expect_request(const std::vector<uint8_t>& request_token) {
|
|
Guard guard(_lock);
|
|
if (_request_token == request_token &&
|
|
(_event.kind == Kind::RESPONSE || _event.kind == Kind::FAILED ||
|
|
_event.kind == Kind::OVERSIZED)) return;
|
|
_request_token = request_token;
|
|
_event = Event{};
|
|
}
|
|
|
|
bool publish_link(const std::vector<uint8_t>& token, bool established) {
|
|
Guard guard(_lock);
|
|
if (token.empty()) return false;
|
|
if (_link_token.empty()) _link_token = token;
|
|
if (token != _link_token) return false;
|
|
// A remote may close immediately after delivering a response. Preserve
|
|
// terminal request events until the main loop consumes them instead of
|
|
// replacing the page/error with a generic LINK_CLOSED event.
|
|
if (!established && (_event.kind == Kind::RESPONSE ||
|
|
_event.kind == Kind::FAILED ||
|
|
_event.kind == Kind::OVERSIZED)) return false;
|
|
_event.kind = established ? Kind::LINK_ESTABLISHED : Kind::LINK_CLOSED;
|
|
_event.data.clear();
|
|
_event.transfer_size = 0;
|
|
return true;
|
|
}
|
|
|
|
bool publish_response(const std::vector<uint8_t>& token, const uint8_t* data,
|
|
std::size_t size, std::size_t transfer_size) {
|
|
Guard guard(_lock);
|
|
if (token.empty()) return false;
|
|
if (_request_token.empty()) _request_token = token;
|
|
if (token != _request_token) return false;
|
|
if (transfer_size > MAX_WIRE_BYTES || size > MAX_WIRE_BYTES || (!data && size != 0)) {
|
|
set_oversized(transfer_size);
|
|
return true;
|
|
}
|
|
_event.kind = Kind::RESPONSE;
|
|
_event.transfer_size = transfer_size;
|
|
_event.data.clear();
|
|
if (size != 0) _event.data.assign(data, data + size);
|
|
return true;
|
|
}
|
|
|
|
bool publish_failed(const std::vector<uint8_t>& token) {
|
|
Guard guard(_lock);
|
|
if (token.empty()) return false;
|
|
if (_request_token.empty()) _request_token = token;
|
|
if (token != _request_token) return false;
|
|
if (_event.kind == Kind::OVERSIZED || _event.kind == Kind::RESPONSE) return false;
|
|
_event.kind = Kind::FAILED;
|
|
_event.data.clear();
|
|
_event.transfer_size = 0;
|
|
return true;
|
|
}
|
|
|
|
bool publish_progress(const std::vector<uint8_t>& token, std::size_t transfer_size) {
|
|
Guard guard(_lock);
|
|
if (token.empty()) return false;
|
|
if (_request_token.empty()) _request_token = token;
|
|
if (token != _request_token) return false;
|
|
if (transfer_size <= MAX_WIRE_BYTES) return true;
|
|
set_oversized(transfer_size);
|
|
return true;
|
|
}
|
|
|
|
bool take(Event& event) {
|
|
Guard guard(_lock);
|
|
if (_event.kind == Kind::NONE) return false;
|
|
event = std::move(_event);
|
|
_event = Event{};
|
|
return true;
|
|
}
|
|
|
|
void clear() {
|
|
Guard guard(_lock);
|
|
_link_token.clear();
|
|
_request_token.clear();
|
|
_event = Event{};
|
|
}
|
|
|
|
private:
|
|
class Guard {
|
|
public:
|
|
explicit Guard(std::atomic_flag& lock) : _lock(lock) {
|
|
while (_lock.test_and_set(std::memory_order_acquire)) {}
|
|
}
|
|
~Guard() { _lock.clear(std::memory_order_release); }
|
|
private:
|
|
std::atomic_flag& _lock;
|
|
};
|
|
|
|
void set_oversized(std::size_t transfer_size) {
|
|
_event.kind = Kind::OVERSIZED;
|
|
_event.data.clear();
|
|
_event.transfer_size = transfer_size;
|
|
}
|
|
|
|
std::atomic_flag _lock = ATOMIC_FLAG_INIT;
|
|
std::vector<uint8_t> _link_token;
|
|
std::vector<uint8_t> _request_token;
|
|
Event _event;
|
|
};
|
|
|
|
} // namespace UI::LXMF::NomadNet
|