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)
{