diff --git a/src/policy/mempool_fees.h b/src/policy/mempool_fees.h index 8f5a2268842912..929edd31cd70e0 100644 --- a/src/policy/mempool_fees.h +++ b/src/policy/mempool_fees.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -21,22 +22,35 @@ class CTxMemPool; // mempool condition might likely change. static const unsigned int MAX_CONF_TARGET{3}; -// Cache mempool-based feerate estimates to avoid repeatedly running the -// expensive block-building algorithm. +/** + * CachedMempoolEstimates holds a cache of recent mempool-based fee estimates. + * Running the block-building algorithm multiple times is undesriable due to + * locking. + */ struct CachedMempoolEstimates { + private: + // shared_mutex allows for multiple concurrent reads, but only a single update + mutable std::shared_mutex cache_mutex; static constexpr std::chrono::seconds cache_life{30}; std::map estimates; std::chrono::steady_clock::time_point last_updated; bool isStale() const { + std::shared_lock lock(cache_mutex); return (last_updated + cache_life) < std::chrono::steady_clock::now(); } public: + + CachedMempoolEstimates() : last_updated(std::chrono::steady_clock::now() - cache_life - std::chrono::seconds(1)) {} + CachedMempoolEstimates(const CachedMempoolEstimates&) = delete; + CachedMempoolEstimates& operator=(const CachedMempoolEstimates&) = delete; + std::optional get(uint64_t number_of_blocks) const { + std::shared_lock lock(cache_mutex); if (isStale()) return std::nullopt; LogPrint(BCLog::MEMPOOL, "CachedMempoolEstimates : cache is not stale, using cached value\n"); @@ -47,8 +61,9 @@ struct CachedMempoolEstimates { return std::nullopt; } - void update(std::map& newEstimates) + void update(const std::map& newEstimates) { + std::unique_lock lock(cache_mutex); // Overwrite the entire map with the new data to avoid old // estimates remaining. estimates = newEstimates;