diff --git a/Makefile.am b/Makefile.am index b1b5ccb7..9014f4f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,7 @@ src_libbitcoin_node_la_SOURCES = \ src/protocols/protocol_header_out_31800.cpp \ src/protocols/protocol_header_out_70012.cpp \ src/protocols/protocol_observer.cpp \ + src/protocols/protocol_performer.cpp \ src/protocols/protocol_transaction_in.cpp \ src/protocols/protocol_transaction_out.cpp \ src/sessions/session.cpp \ @@ -155,6 +156,7 @@ include_bitcoin_node_protocols_HEADERS = \ include/bitcoin/node/protocols/protocol_header_out_31800.hpp \ include/bitcoin/node/protocols/protocol_header_out_70012.hpp \ include/bitcoin/node/protocols/protocol_observer.hpp \ + include/bitcoin/node/protocols/protocol_performer.hpp \ include/bitcoin/node/protocols/protocol_transaction_in.hpp \ include/bitcoin/node/protocols/protocol_transaction_out.hpp \ include/bitcoin/node/protocols/protocols.hpp diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 858df754..a838cf6c 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -290,6 +290,7 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/protocols/protocol_header_out_31800.cpp" "../../src/protocols/protocol_header_out_70012.cpp" "../../src/protocols/protocol_observer.cpp" + "../../src/protocols/protocol_performer.cpp" "../../src/protocols/protocol_transaction_in.cpp" "../../src/protocols/protocol_transaction_out.cpp" "../../src/sessions/session.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj index 0c750f9f..478a6a6c 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj @@ -94,6 +94,7 @@ + @@ -128,6 +129,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters index 8525813e..c53ac915 100644 --- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters @@ -111,6 +111,9 @@ src\protocols + + src\protocols + src\protocols @@ -209,6 +212,9 @@ include\bitcoin\node\protocols + + include\bitcoin\node\protocols + include\bitcoin\node\protocols diff --git a/include/bitcoin/node.hpp b/include/bitcoin/node.hpp index 805f4d15..7294700a 100644 --- a/include/bitcoin/node.hpp +++ b/include/bitcoin/node.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp index a13c9cb0..9b092b0c 100644 --- a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp @@ -23,34 +23,25 @@ #include #include #include -#include +#include namespace libbitcoin { namespace node { -/// This does NOT inhereit from protocol_block_in. +/// This does NOT inherit from protocol_block_in. class BCN_API protocol_block_in_31800 - : public node::protocol, - protected network::tracker + : public protocol_performer { public: typedef std::shared_ptr ptr; - using type_id = network::messages::inventory::type_id; - using map_ptr = chaser_check::map_ptr; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) template protocol_block_in_31800(Session& session, const channel_ptr& channel, bool performance) NOEXCEPT - : node::protocol(session, channel), - network::tracker(session.log), - drop_stalled_(performance && - to_bool(session.config().node.sample_period_seconds)), - use_deviation_(session.config().node.allowed_deviation > 0.0), + : protocol_performer(session, channel, performance), block_type_(session.config().network.witness_node() ? type_id::witness_block : type_id::block), - performance_timer_(std::make_shared(session.log, - channel->strand(), session.config().node.sample_period())), map_(std::make_shared()) { } @@ -61,27 +52,29 @@ class BCN_API protocol_block_in_31800 void stopping(const code& ec) NOEXCEPT override; protected: - /// Performance polling. - virtual void start_performance() NOEXCEPT; - virtual void pause_performance() NOEXCEPT; - virtual void stop_performance() NOEXCEPT; + /// Determine if block passes check validation. + virtual code validate(const system::chain::block& block, + const system::chain::context& ctx) const NOEXCEPT; /// Get published download identifiers. virtual void handle_event(const code& ec, chaser::chase event_, chaser::link value) NOEXCEPT; virtual void do_get_downloads(chaser::count_t count) NOEXCEPT; + + /// Manage work splitting. + virtual bool is_idle() const NOEXCEPT; virtual void do_split(chaser::channel_t channel) NOEXCEPT; - void do_pause(chaser::channel_t channel) NOEXCEPT; - void do_resume(chaser::channel_t channel) NOEXCEPT; + virtual void do_pause(chaser::channel_t channel) NOEXCEPT; + virtual void do_resume(chaser::channel_t channel) NOEXCEPT; - /// Accept incoming block message. + /// Check incoming block message. virtual bool handle_receive_block(const code& ec, const network::messages::block::cptr& message) NOEXCEPT; private: - void handle_performance_timer(const code& ec) NOEXCEPT; - void handle_send_performance(const code& ec) NOEXCEPT; - void do_handle_performance(const code& ec) NOEXCEPT; + using map_ptr = chaser_check::map_ptr; + using type_id = network::messages::inventory::type_id; + static constexpr size_t minimum_for_stall_divide = 2; void send_get_data(const map_ptr& map) NOEXCEPT; network::messages::get_data create_get_data( @@ -92,17 +85,10 @@ class BCN_API protocol_block_in_31800 void handle_put_hashes(const code& ec) NOEXCEPT; void handle_get_hashes(const code& ec, const map_ptr& map) NOEXCEPT; - void send_performance(uint64_t rate) NOEXCEPT; - - // These are thread safe. - const bool drop_stalled_; - const bool use_deviation_; + // This is thread safe. const network::messages::inventory::type_id block_type_; - // These are protected by strand. - uint64_t bytes_{ zero }; - network::steady_clock::time_point start_{}; - network::deadline::ptr performance_timer_; + // This is protected by strand. map_ptr map_; }; diff --git a/include/bitcoin/node/protocols/protocol_performer.hpp b/include/bitcoin/node/protocols/protocol_performer.hpp new file mode 100644 index 00000000..92380056 --- /dev/null +++ b/include/bitcoin/node/protocols/protocol_performer.hpp @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_NODE_PROTOCOL_PERFORMER_HPP +#define LIBBITCOIN_NODE_PROTOCOL_PERFORMER_HPP + +#include +#include +#include + +namespace libbitcoin { +namespace node { + +/// Abstract base protocol for performance standard deviation measurement. +class BCN_API protocol_performer + : public node::protocol, + protected network::tracker +{ +public: + virtual void start_performance() NOEXCEPT; + virtual void pause_performance() NOEXCEPT; + virtual void stop_performance() NOEXCEPT; + virtual void count(size_t bytes) NOEXCEPT; + +protected: + template + protocol_performer(Session& session, const channel_ptr& channel, + bool enable) NOEXCEPT + : node::protocol(session, channel), + network::tracker(session.log), + use_deviation_(session.config().node.allowed_deviation > 0.0), + drop_stall_(enable && + to_bool(session.config().node.sample_period_seconds)), + performance_timer_(std::make_shared(session.log, + channel->strand(), session.config().node.sample_period())) + { + } + + virtual bool is_idle() const NOEXCEPT = 0; + +private: + void handle_performance_timer(const code& ec) NOEXCEPT; + void handle_send_performance(const code& ec) NOEXCEPT; + void do_handle_performance(const code& ec) NOEXCEPT; + + void send_performance(uint64_t rate) NOEXCEPT; + + // These are thread safe. + const bool use_deviation_; + const bool drop_stall_; + + // These are protected by strand. + uint64_t bytes_{ zero }; + network::steady_clock::time_point start_{}; + network::deadline::ptr performance_timer_; +}; + +} // namespace node +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/node/protocols/protocols.hpp b/include/bitcoin/node/protocols/protocols.hpp index e3d3d29f..9dc1ae69 100644 --- a/include/bitcoin/node/protocols/protocols.hpp +++ b/include/bitcoin/node/protocols/protocols.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/include/bitcoin/node/sessions/session_outbound.hpp b/include/bitcoin/node/sessions/session_outbound.hpp index 7e3aedc4..715dd74c 100644 --- a/include/bitcoin/node/sessions/session_outbound.hpp +++ b/include/bitcoin/node/sessions/session_outbound.hpp @@ -37,7 +37,6 @@ class BCN_API session_outbound session_outbound(full_node& node, uint64_t identifier) NOEXCEPT; void start(network::result_handler&& handler) NOEXCEPT override; - virtual void performance(uint64_t channel, uint64_t speed, network::result_handler&& handler) NOEXCEPT; @@ -45,10 +44,11 @@ class BCN_API session_outbound virtual void handle_event(const code& ec, chaser::chase event_, chaser::link value) NOEXCEPT; virtual void split(chaser::channel_t channel) NOEXCEPT; + virtual void do_performance(uint64_t channel, uint64_t speed, + const network::result_handler& handler) NOEXCEPT; private: - void do_performance(uint64_t channel, uint64_t speed, - const network::result_handler& handler) NOEXCEPT; + static constexpr size_t minimum_for_standard_deviation = 3; // This is thread safe. const float allowed_deviation_; diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index d674100a..dbcf5e6c 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -18,10 +18,10 @@ */ #include +#include #include #include #include -#include #include #include #include @@ -38,126 +38,7 @@ using namespace network; using namespace network::messages; using namespace std::placeholders; -// Shared pointers required for lifetime in handler parameters. BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) - -// performance polling -// ---------------------------------------------------------------------------- - -void protocol_block_in_31800::start_performance() NOEXCEPT -{ - if (stopped()) - return; - - if (drop_stalled_) - { - bytes_ = zero; - start_ = steady_clock::now(); - performance_timer_->start(BIND(handle_performance_timer, _1)); - } -} - -void protocol_block_in_31800::handle_performance_timer(const code& ec) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (ec == network::error::operation_canceled) - return; - - if (stopped()) - return; - - if (ec) - { - LOGF("Performance timer failure, " << ec.message()); - stop(ec); - return; - } - - if (map_->empty()) - { - // Channel is exhausted, performance no longer relevant. - pause_performance(); - return; - } - - const auto delta = duration_cast(steady_clock::now() - start_); - const auto unsigned_delta = sign_cast(delta.count()); - const auto non_zero_period = system::greater(unsigned_delta, one); - const auto rate = floored_divide(bytes_, non_zero_period); - send_performance(rate); -} - -void protocol_block_in_31800::pause_performance() NOEXCEPT -{ - send_performance(max_uint64); -} - -void protocol_block_in_31800::stop_performance() NOEXCEPT -{ - send_performance(zero); -} - -void protocol_block_in_31800::send_performance(uint64_t rate) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (drop_stalled_) - { - // Must come first as this takes priority as per configuration. - // Shared performance manager detects slow and stalled channels. - if (use_deviation_) - { - performance_timer_->stop(); - performance(identifier(), rate, BIND(handle_send_performance, _1)); - return; - } - - // Internal performance manager detects only stalled channel (not slow). - const auto ec = is_zero(rate) ? error::stalled_channel : - (rate == max_uint64 ? error::exhausted_channel : error::success); - performance_timer_->stop(); - do_handle_performance(ec); - } -} - -void protocol_block_in_31800::handle_send_performance(const code& ec) NOEXCEPT -{ - POST(do_handle_performance, ec); -} - -void protocol_block_in_31800::do_handle_performance(const code& ec) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (stopped()) - return; - - // Caused only by performance(max) - had no outstanding work. - // Timer stopped until chaser::download event restarts it. - if (ec == error::exhausted_channel) - return; - - // Caused only by performance(zero|xxx) - had outstanding work. - if (ec == error::stalled_channel || ec == error::slow_channel) - { - LOGP("Performance [" << authority() << "] " << ec.message()); - stop(ec); - return; - } - - if (ec) - { - LOGF("Performance failure [" << authority() << "] " << ec.message()); - stop(ec); - return; - } - - // Restart performance timing cycle. - start_performance(); -} // start/stop // ---------------------------------------------------------------------------- @@ -198,51 +79,69 @@ void protocol_block_in_31800::stopping(const code& ec) NOEXCEPT // handle events (download, split) // ---------------------------------------------------------------------------- +bool protocol_block_in_31800::is_idle() const NOEXCEPT +{ + return map_->empty(); +} + void protocol_block_in_31800::handle_event(const code&, chaser::chase event_, chaser::link value) NOEXCEPT { - constexpr auto minimum_for_stall_divide = 2_size; - if (stopped()) return; - if (event_ == chaser::chase::download) + switch (event_) { - // There are count blocks to download at/above the given header. - if (is_current()) + case chaser::chase::download: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_get_downloads, std::get(value)); + // There are count blocks to download at/above given header. + // But don't download blocks until candidate chain is current. + if (is_current()) + { + BC_ASSERT(std::holds_alternative(value)); + POST(do_get_downloads, std::get(value)); + } + + break; } - } - else if (event_ == chaser::chase::split) - { - BC_ASSERT(std::holds_alternative(value)); - const auto channel = std::get(value); + case chaser::chase::split: + { + BC_ASSERT(std::holds_alternative(value)); + const auto channel = std::get(value); + + // It was determined to be the slowest channel with work. + // If value identifies this channel, split work and stop. + if (channel == identifier()) + { + POST(do_split, channel); + } - // If value identifies this channel, split work and stop. - if (channel == identifier()) + break; + } + case chaser::chase::stall: { - POST(do_split, channel); + // If this channel has divisible work, split it and stop. + // There are no channels reporting work, either stalled or done. + // This is initiated by any channel notifying chase::starved. + if (map_->size() >= minimum_for_stall_divide) + { + POST(do_split, chaser::count_t{}); + } + + break; } - } - else if (event_ == chaser::chase::stall) - { - // If this channel has divisible work, split it and stop. - if (map_->size() >= minimum_for_stall_divide) + case chaser::chase::pause: { - POST(do_split, chaser::count_t{}); + // Pause local timers due to channel pause (e.g. snapshot pending). + POST(do_pause, chaser::channel_t{}); + break; + } + case chaser::chase::resume: + { + // Resume local timers due to channel resume (e.g. snapshot done). + POST(do_resume, chaser::channel_t{}); + break; } - } - else if (event_ == chaser::chase::pause) - { - // Pause local timers due to channel pause. - POST(do_pause, chaser::channel_t{}); - } - else if (event_ == chaser::chase::resume) - { - // Resume local timers due to channel resume. - POST(do_resume, chaser::channel_t{}); } } @@ -253,7 +152,7 @@ void protocol_block_in_31800::do_pause(chaser::channel_t) NOEXCEPT void protocol_block_in_31800::do_resume(chaser::channel_t) NOEXCEPT { - if (!map_->empty()) + if (!is_idle()) start_performance(); } @@ -264,7 +163,7 @@ void protocol_block_in_31800::do_get_downloads(chaser::count_t) NOEXCEPT if (stopped()) return; - if (map_->empty()) + if (is_idle()) { // Assume performance was stopped due to exhaustion. start_performance(); @@ -315,9 +214,10 @@ void protocol_block_in_31800::send_get_data(const map_ptr& map) NOEXCEPT if (map->empty()) return; - if (map_->empty()) + if (is_idle()) { - SEND(create_get_data((map_ = map)), handle_send, _1); + const auto message = create_get_data((map_ = map)); + SEND(message, handle_send, _1); return; } @@ -342,9 +242,16 @@ get_data protocol_block_in_31800::create_get_data( return getter; } -// accept block +// check block // ---------------------------------------------------------------------------- +code protocol_block_in_31800::validate(const chain::block& block, + const chain::context& ctx) const NOEXCEPT +{ + code ec{}; + return ec = block.check() ? ec : block.check(ctx); +} + bool protocol_block_in_31800::handle_receive_block(const code& ec, const block::cptr& message) NOEXCEPT { @@ -372,11 +279,10 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, // Could check for parent invalidation and propagate here, but blocks are // not checked in order, so there would remain no guarantee. - code error{}; const auto& link = it->link; const auto& ctx = it->context; - const auto height = possible_narrow_cast(ctx.height); - if (((error = block.check())) || ((error = block.check(ctx)))) + + if (const auto error = validate(block, ctx)) { query.set_block_unconfirmable(link); notify(error::success, chaser::chase::unchecked, link); @@ -405,11 +311,12 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, LOGP("Downloaded block [" << encode_hash(hash) << ":" << ctx.height << "] from [" << authority() << "]."); + const auto height = possible_narrow_cast(ctx.height); notify(error::success, chaser::chase::checked, height); - bytes_ += message->cached_size; - map_->erase(it); + count(message->cached_size); - if (map_->empty()) + map_->erase(it); + if (is_idle()) { LOGP("Getting more block hashes for [" << authority() << "]."); get_hashes(BIND(handle_get_hashes, _1, _2)); @@ -464,8 +371,6 @@ void protocol_block_in_31800::handle_get_hashes(const code& ec, POST(send_get_data, map); } -BC_POP_WARNING() -BC_POP_WARNING() BC_POP_WARNING() } // namespace node diff --git a/src/protocols/protocol_performer.cpp b/src/protocols/protocol_performer.cpp new file mode 100644 index 00000000..4426ddf1 --- /dev/null +++ b/src/protocols/protocol_performer.cpp @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include + +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace node { + +#define CLASS protocol_performer + +using namespace system; +using namespace network; +using namespace std::placeholders; + +void protocol_performer::start_performance() NOEXCEPT +{ + if (stopped()) + return; + + if (drop_stall_) + { + bytes_ = zero; + start_ = steady_clock::now(); + performance_timer_->start(BIND(handle_performance_timer, _1)); + } +} + +void protocol_performer::handle_performance_timer(const code& ec) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (ec == network::error::operation_canceled) + return; + + if (stopped()) + return; + + if (ec) + { + LOGF("Performance timer failure, " << ec.message()); + stop(ec); + return; + } + + if (is_idle()) + { + // Channel is exhausted, performance no longer relevant. + pause_performance(); + return; + } + + const auto delta = duration_cast(steady_clock::now() - start_); + const auto unsigned_delta = sign_cast(delta.count()); + const auto non_zero_period = greater(unsigned_delta, one); + const auto rate = floored_divide(bytes_, non_zero_period); + send_performance(rate); +} + +void protocol_performer::pause_performance() NOEXCEPT +{ + BC_ASSERT(stranded()); + send_performance(max_uint64); +} + +void protocol_performer::stop_performance() NOEXCEPT +{ + BC_ASSERT(stranded()); + send_performance(zero); +} + +void protocol_performer::send_performance(uint64_t rate) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (drop_stall_) + { + // Must come first as this takes priority as per configuration. + // Shared performance manager detects slow and stalled channels. + if (use_deviation_) + { + performance_timer_->stop(); + node::protocol::performance(identifier(), rate, + BIND(handle_send_performance, _1)); + return; + } + + // Protocol performance manager detects only stalled channels. + const auto ec = is_zero(rate) ? error::stalled_channel : + (rate == max_uint64 ? error::exhausted_channel : + error::success); + + performance_timer_->stop(); + do_handle_performance(ec); + } +} + +void protocol_performer::handle_send_performance(const code& ec) NOEXCEPT +{ + POST(do_handle_performance, ec); +} + +void protocol_performer::do_handle_performance(const code& ec) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (stopped()) + return; + + // Caused only by performance(max) - had no outstanding work. + // Timer stopped until chaser::download event restarts it. + if (ec == error::exhausted_channel) + return; + + // Caused only by performance(zero|xxx) - had outstanding work. + if (ec == error::stalled_channel || ec == error::slow_channel) + { + LOGP("Channel dropped [" << authority() << "] " << ec.message()); + stop(ec); + return; + } + + if (ec) + { + LOGF("Performance failure [" << authority() << "] " << ec.message()); + stop(ec); + return; + } + + // Restart performance timing cycle. + start_performance(); +} + +void protocol_performer::count(size_t bytes) NOEXCEPT +{ + BC_ASSERT(stranded()); + bytes_ = ceilinged_add(bytes_, possible_wide_cast(bytes)); +} + +} // namespace node +} // namespace libbitcoin diff --git a/src/sessions/session_outbound.cpp b/src/sessions/session_outbound.cpp index 08e3903e..dca5d2af 100644 --- a/src/sessions/session_outbound.cpp +++ b/src/sessions/session_outbound.cpp @@ -60,7 +60,7 @@ session_outbound::session_outbound(full_node& node, { } -// start +// split // ---------------------------------------------------------------------------- void session_outbound::start(result_handler&& handler) NOEXCEPT @@ -71,9 +71,6 @@ void session_outbound::start(result_handler&& handler) NOEXCEPT network::session_outbound::start(std::move(handler)); } -// split -// ---------------------------------------------------------------------------- - // Event subscriber operates on the network strand (session). void session_outbound::handle_event(const code&, chaser::chase event_, chaser::link value) NOEXCEPT @@ -83,6 +80,7 @@ void session_outbound::handle_event(const code&, if (stopped()) return; + // When a channel becomes starved notify other(s) to split work. if (event_ == chaser::chase::starved) { BC_ASSERT(std::holds_alternative(value)); @@ -129,9 +127,6 @@ void session_outbound::do_performance(uint64_t channel, uint64_t speed, { BC_ASSERT(stranded()); - // Three elements are required to measure deviation, don't drop to two. - constexpr auto minimum_for_standard_deviation = 3_size; - if (speed == max_uint64) { speeds_.erase(channel); @@ -147,8 +142,12 @@ void session_outbound::do_performance(uint64_t channel, uint64_t speed, return; } + // Floating point conversion. + BC_PUSH_WARNING(NO_STATIC_CAST) speeds_[channel] = static_cast(speed); + BC_POP_WARNING() + // Three elements are required to measure deviation, don't drop below. const auto count = speeds_.size(); if (count <= minimum_for_standard_deviation) {