The FastRandomContext class is documented as not thread-safe.
This PR adds a relevant note to the FeeFilterRounder::round() function declaration.
Close #19254
280 | @@ -281,11 +281,12 @@ class FeeFilterRounder
281 | explicit FeeFilterRounder(const CFeeRate& minIncrementalFee);
282 |
283 | /** Quantize a minimum fee for privacy purpose before broadcast **/
/** Quantize a minimum fee for privacy purpose before broadcast . Not thread-safe due to use of FastRandomContext **/
If this is not going to be changed, it's probably better to make it an explicit global.
Oh, I am not suggestion to never change this. This can be changed when net_processing is multi-threaded. Until then documentation and sanitizers are good enough to leave the code as is for now.
Isn't it as thread safe as a class A { int a = 0; public: int increment() { return ++a; } }, which we'd solve by saying static A my_a GUARDED_BY(cs_main); ? And both could be made parallelisable by saying static thread_local A my_a; -- it's just that GUARDED_BY doesn't work with block-scope statics?
Just adding // protected by cs_main at the declaration of filterRounder, or moving it to be a module-level static and adding a GUARDED_BY(cs_main) seems better to me?
My preference would be to just document it. Unless this is the only random context without a lock held in the net_processing loop.
As soon as the net_processing loop is run in parallel, our sanitizers should point out any unsafe code, so there should be no risk in leaving the code as-is.
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
No conflicts as of last run.
Concept ACK.
I don't have a strong opinion on taking either approach (mutex vs documentation-only)
286 | 287 | private: 288 | std::set<double> feeset; 289 | - FastRandomContext insecure_rand; 290 | + mutable Mutex m_random_context_mutex; 291 | + mutable FastRandomContext m_insecure_rand GUARDED_BY(m_random_context_mutex);
I think it'd make more sense to mark feeset as const, than marking the whole object as const, and the parts that change as mutable (given const X x;, then a = x.foo(); b = x.foo(); a == b ought to be true imo).
Adding a static std::set<double> make_feeset(const CFeeRate&) member function and using that to initialise feeset via the initializer list looks like it's enough?
4395 | @@ -4396,7 +4396,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
4396 | int64_t timeNow = GetTimeMicros();
4397 | if (timeNow > pto->m_tx_relay->nextSendTimeFeeFilter) {
4398 | static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
Can make default_feerate const too -- could make it constexpr even, so long as the constructors were also marked constexpr.
Co-authored-by: MarcoFalke <falke.marco@gmail.com>
Updated 877cca538b29bbb7420601e7972fb5635523f507 -> d842e6ac965b528f0d704f54aceb91eae84085fb (pr19268.01 -> pr19268.02):
self ACK d842e6ac965b528f0d704f54aceb91eae84085fb
ACK d842e6ac965b528f0d704f54aceb91eae84085fb: explicit is better than implicit
ACK d842e6a