Files
pyxis/lib/tdeck_ui/UI/LXMF/NomadNetCacheFlow.h
T
Torlando 32a09938d8 fix(nomadnet): address Greploop round 1 on the cache transient-stall guard
Three findings on the transient-stall bail, all valid:

- Reload invalidation: a bail recorded BYPASS, but NomadNetCacheFlow::service()
  accepts only MISS as a successful invalidation, so a bail during an admitted
  reload reported 'Page cache invalidation failed' instead of falling through
  to a live fetch. The bail now records MISS.

- Open-resource leak: the bail cleared read_open_/write_open_ (and abandoned an
  open directory enumeration) without calling endRead()/abortWrite()/endList(),
  leaking SD handles. The bail now releases each in-flight resource via the
  seam's own teardown (bounded best-effort; a still-transient close is
  accepted rather than re-pinning the op).

- Tick-vs-time: the not-ready UNAVAILABLE path returns immediately (no bus
  wait), so a pure 500-tick budget could expire during a legitimate SD mount
  window and disable caching for the whole session. The bail is now gated on
  BOTH the tick floor AND a 10s wall-time window (service() takes a monotonic
  ms clock; production passes millis(), 0 is a safe default for tests).

Regression tests: reload-invalidation bail -> NEED_LIVE (flow), flat-clock does
not bail, healable transient keeps authority, list-open (RECOVERY_END) stall
bails and releases the handle (cache).
2026-09-15 06:27:14 +00:00

47 lines
1.3 KiB
C++

// Copyright (c) 2026 Pyxis contributors
// SPDX-License-Identifier: MIT
#ifndef UI_LXMF_NOMADNET_CACHE_FLOW_H
#define UI_LXMF_NOMADNET_CACHE_FLOW_H
#include "NomadNetCache.h"
namespace UI { namespace LXMF { namespace NomadNet {
enum class CacheFlowState : std::uint8_t {
IDLE,
LOOKUP,
INVALIDATE,
NEED_LIVE,
READY,
FAILED,
CANCELLED
};
class NomadNetCacheFlow {
public:
explicit NomadNetCacheFlow(NomadNetCache& cache) : cache_(cache) {}
CacheFlowState begin(const CacheKey&, std::uint64_t now, bool reload);
void service(std::uint64_t now_ms = 0);
bool acceptLive(const std::vector<std::uint8_t>&, const CacheEligibility&,
std::uint64_t now);
void cancel();
CacheFlowState state() const { return state_; }
bool pageReady() const { return state_ == CacheFlowState::READY && !page_.empty(); }
bool takePage(ExternalVector<std::uint8_t>&);
const std::string& status() const { return status_; }
private:
NomadNetCache& cache_;
CacheKey key_;
CacheFlowState state_ = CacheFlowState::IDLE;
ExternalVector<std::uint8_t> page_;
std::string status_;
std::uint64_t now_ = 0;
bool lookup_admitted_ = false;
bool invalidation_admitted_ = false;
};
}}} // namespace UI::LXMF::NomadNet
#endif