From 5a69ff400eaccd9917c10b4a784e8e388cfe6356 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Mon, 13 Apr 2020 18:45:12 +0100 Subject: [PATCH 1/5] Epoch upgrade on background instead of worker Also fixes the RPC test not being multithreaded --- nano/node/json_handler.cpp | 2 +- nano/node/node.cpp | 1 + nano/rpc_test/rpc.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 5018ad6e99..e2c1236cac 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2142,7 +2142,7 @@ void nano::json_handler::epoch_upgrade () if (nano::pub_key (prv) == node.ledger.epoch_signer (node.ledger.epoch_link (epoch))) { auto node_l (node.shared ()); - node.worker.push_task ([node_l, prv, epoch, count_limit, threads]() { + node.background ([node_l, prv, epoch, count_limit, threads]() { node_l->epoch_upgrader (prv, epoch, count_limit, threads); }); response_l.put ("started", "1"); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 36f30824af..25d7774a4b 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1348,6 +1348,7 @@ bool nano::node::init_error () const void nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch epoch_a, uint64_t count_limit, uint64_t threads) { + debug_assert (nano::thread_role::get () != nano::thread_role::name::worker); auto upgrader_process = [](nano::node & node_a, std::atomic & counter, std::shared_ptr epoch, uint64_t difficulty, nano::public_key const & signer_a, nano::root const & root_a, nano::account const & account_a) { epoch->block_work_set (node_a.work_generate_blocking (nano::work_version::work_1, root_a, difficulty).value_or (0)); bool valid_signature (!nano::validate_message (signer_a, epoch->hash (), epoch->block_signature ())); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 05c1eade66..f9e2b39770 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -8089,6 +8089,7 @@ TEST (rpc, epoch_upgrade_multithreaded) rpc.start (); boost::property_tree::ptree request; request.put ("action", "epoch_upgrade"); + request.put ("threads", 2); request.put ("epoch", 1); request.put ("key", epoch_signer.prv.data.to_string ()); test_response response (request, rpc.config.port, system.io_ctx); From 24e29296bb2aec8119b11821196bd6555842967e Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Mon, 13 Apr 2020 20:14:09 +0100 Subject: [PATCH 2/5] Epoch upgrade as an async task; fix limited count in multithreaded mode; improve tests --- nano/lib/threading.cpp | 3 + nano/lib/threading.hpp | 3 +- nano/node/json_handler.cpp | 13 +-- nano/node/node.cpp | 34 ++++++-- nano/node/node.hpp | 4 +- nano/rpc_test/rpc.cpp | 8 ++ nano/slow_test/node.cpp | 173 ++++++++++++++++++++----------------- 7 files changed, 145 insertions(+), 93 deletions(-) diff --git a/nano/lib/threading.cpp b/nano/lib/threading.cpp index 7a5482ab07..0b0319bd3b 100644 --- a/nano/lib/threading.cpp +++ b/nano/lib/threading.cpp @@ -78,6 +78,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role) case nano::thread_role::name::state_block_signature_verification: thread_role_name_string = "State block sig"; break; + case nano::thread_role::name::epoch_upgrader: + thread_role_name_string = "Epoch upgrader"; + break; } /* diff --git a/nano/lib/threading.hpp b/nano/lib/threading.hpp index 1097a8670b..f6ab4e187d 100644 --- a/nano/lib/threading.hpp +++ b/nano/lib/threading.hpp @@ -34,7 +34,8 @@ namespace thread_role confirmation_height_processing, worker, request_aggregator, - state_block_signature_verification + state_block_signature_verification, + epoch_upgrader }; /* * Get/Set the identifier for the current thread diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index e2c1236cac..33528a0bff 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2141,11 +2141,14 @@ void nano::json_handler::epoch_upgrade () { if (nano::pub_key (prv) == node.ledger.epoch_signer (node.ledger.epoch_link (epoch))) { - auto node_l (node.shared ()); - node.background ([node_l, prv, epoch, count_limit, threads]() { - node_l->epoch_upgrader (prv, epoch, count_limit, threads); - }); - response_l.put ("started", "1"); + if (!node.epoch_upgrader (prv, epoch, count_limit, threads)) + { + response_l.put ("started", "1"); + } + else + { + response_l.put ("started", "0"); + } } else { diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 25d7774a4b..b69074d5a2 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -717,6 +717,11 @@ void nano::node::stop () wallets.stop (); stats.stop (); worker.stop (); + auto epoch_upgrade = epoch_upgrading.lock (); + if (epoch_upgrade->valid ()) + { + epoch_upgrade->wait (); + } // work pool is not stopped on purpose due to testing setup } } @@ -1346,9 +1351,24 @@ bool nano::node::init_error () const return store.init_error () || wallets_store.init_error (); } -void nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch epoch_a, uint64_t count_limit, uint64_t threads) +bool nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch epoch_a, uint64_t count_limit, uint64_t threads) +{ + bool error = stopped.load (); + if (!error) + { + auto epoch_upgrade = epoch_upgrading.lock (); + error = epoch_upgrade->valid () && epoch_upgrade->wait_for (std::chrono::seconds (0)) == std::future_status::timeout; + if (!error) + { + epoch_upgrade.get () = std::async (std::launch::async, std::bind (&nano::node::epoch_upgrader_impl, this, prv_a, epoch_a, count_limit, threads)); + } + } + return error; +} + +void nano::node::epoch_upgrader_impl (nano::private_key const & prv_a, nano::epoch epoch_a, uint64_t count_limit, uint64_t threads) { - debug_assert (nano::thread_role::get () != nano::thread_role::name::worker); + nano::thread_role::set (nano::thread_role::name::epoch_upgrader); auto upgrader_process = [](nano::node & node_a, std::atomic & counter, std::shared_ptr epoch, uint64_t difficulty, nano::public_key const & signer_a, nano::root const & root_a, nano::account const & account_a) { epoch->block_work_set (node_a.work_generate_blocking (nano::work_version::work_1, root_a, difficulty).value_or (0)); bool valid_signature (!nano::validate_message (signer_a, epoch->hash (), epoch->block_signature ())); @@ -1414,7 +1434,7 @@ void nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch ep { auto transaction (store.tx_begin_read ()); // Collect accounts to upgrade - for (auto i (store.latest_begin (transaction)), n (store.latest_end ()); i != n; ++i) + for (auto i (store.latest_begin (transaction)), n (store.latest_end ()); i != n && accounts_list.size () < count_limit; ++i) { nano::account const & account (i->first); nano::account_info const & info (i->second); @@ -1430,13 +1450,15 @@ void nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch ep Repeat until accounts with previous epoch exist in latest table */ std::atomic upgraded_accounts (0); uint64_t workers (0); - for (auto i (accounts_list.get ().begin ()), n (accounts_list.get ().end ()); i != n && upgraded_accounts < upgrade_batch_size && upgraded_accounts < count_limit && !stopped; ++i) + uint64_t attempts (0); + for (auto i (accounts_list.get ().begin ()), n (accounts_list.get ().end ()); i != n && attempts < upgrade_batch_size && attempts < count_limit && !stopped; ++i) { auto transaction (store.tx_begin_read ()); nano::account_info info; nano::account const & account (i->account); if (!store.account_get (transaction, account, info) && info.epoch () < epoch_a) { + ++attempts; auto difficulty (nano::work_threshold (nano::work_version::work_1, nano::block_details (epoch_a, false, false, true))); nano::root const & root (info.head); std::shared_ptr epoch = builder.state () @@ -1502,8 +1524,9 @@ void nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch ep { std::atomic upgraded_pending (0); uint64_t workers (0); + uint64_t attempts (0); auto transaction (store.tx_begin_read ()); - for (auto i (store.pending_begin (transaction, nano::pending_key (1, 0))), n (store.pending_end ()); i != n && upgraded_pending < upgrade_batch_size && upgraded_pending < count_limit && !stopped;) + for (auto i (store.pending_begin (transaction, nano::pending_key (1, 0))), n (store.pending_end ()); i != n && attempts < upgrade_batch_size && attempts < count_limit && !stopped;) { bool to_next_account (false); nano::pending_key const & key (i->first); @@ -1512,6 +1535,7 @@ void nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch ep nano::pending_info const & info (i->second); if (info.epoch < epoch_a) { + ++attempts; release_assert (nano::epochs::is_sequential (info.epoch, epoch_a)); auto difficulty (nano::work_threshold (nano::work_version::work_1, nano::block_details (epoch_a, false, false, true))); nano::root const & root (key.account); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 7cc92ae36b..838ec6b117 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -145,7 +145,7 @@ class node final : public std::enable_shared_from_this void ongoing_online_weight_calculation_queue (); bool online () const; bool init_error () const; - void epoch_upgrader (nano::private_key const &, nano::epoch, uint64_t, uint64_t); + bool epoch_upgrader (nano::private_key const &, nano::epoch, uint64_t, uint64_t); nano::worker worker; nano::write_database_queue write_database_queue; boost::asio::io_context & io_ctx; @@ -206,6 +206,8 @@ class node final : public std::enable_shared_from_this private: void long_inactivity_cleanup (); + void epoch_upgrader_impl (nano::private_key const &, nano::epoch, uint64_t, uint64_t); + nano::locked> epoch_upgrading; }; std::unique_ptr collect_container_info (node & node, const std::string & name); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index f9e2b39770..2bd7117e6e 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -7977,6 +7977,14 @@ TEST (rpc, epoch_upgrade) } ASSERT_EQ (200, response.status); ASSERT_EQ ("1", response.json.get ("started")); + test_response response_fail (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response_fail.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response_fail.status); + ASSERT_EQ ("0", response_fail.json.get ("started")); system.deadline_set (5s); bool done (false); while (!done) diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index f6918561e8..e380116563 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -1182,106 +1182,117 @@ TEST (signature_checker, mass_boundary_checks) // Possible to manually add work peers TEST (node, mass_epoch_upgrader) { - unsigned threads = 20; - size_t total_accounts = 2500; + auto perform_test = [](size_t const batch_size) { + unsigned threads = 5; + size_t total_accounts = 2500; #ifndef NDEBUG - total_accounts /= 5; + total_accounts /= 5; #endif - struct info - { - nano::keypair key; - nano::block_hash pending_hash; - }; + struct info + { + nano::keypair key; + nano::block_hash pending_hash; + }; - std::vector opened (total_accounts / 2); - std::vector unopened (total_accounts / 2); + std::vector opened (total_accounts / 2); + std::vector unopened (total_accounts / 2); - nano::system system; - nano::node_config node_config (nano::get_available_port (), system.logging); - node_config.work_threads = 4; - //node_config.work_peers = { { "192.168.1.101", 7000 } }; - auto & node = *system.add_node (node_config); + nano::system system; + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.work_threads = 4; + //node_config.work_peers = { { "192.168.1.101", 7000 } }; + auto & node = *system.add_node (node_config); - auto balance = node.balance (nano::test_genesis_key.pub); - auto latest = node.latest (nano::test_genesis_key.pub); - nano::uint128_t amount = 1; + auto balance = node.balance (nano::test_genesis_key.pub); + auto latest = node.latest (nano::test_genesis_key.pub); + nano::uint128_t amount = 1; - // Send to all accounts - std::array *, 2> all{ &opened, &unopened }; - for (auto & accounts : all) - { - for (auto & info : *accounts) + // Send to all accounts + std::array *, 2> all{ &opened, &unopened }; + for (auto & accounts : all) + { + for (auto & info : *accounts) + { + balance -= amount; + nano::state_block_builder builder; + std::error_code ec; + auto block = builder + .account (nano::test_genesis_key.pub) + .previous (latest) + .balance (balance) + .link (info.key.pub) + .representative (nano::test_genesis_key.pub) + .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) + .work (*node.work_generate_blocking (latest, nano::work_threshold (nano::work_version::work_1, nano::block_details (nano::epoch::epoch_0, false, false, false)))) + .build (ec); + ASSERT_FALSE (ec); + ASSERT_NE (nullptr, block); + ASSERT_EQ (nano::process_result::progress, node.process (*block).code); + latest = block->hash (); + info.pending_hash = block->hash (); + } + } + ASSERT_EQ (1 + total_accounts, node.ledger.cache.block_count); + ASSERT_EQ (1, node.ledger.cache.account_count); + + // Receive for half of accounts + for (auto const & info : opened) { - balance -= amount; nano::state_block_builder builder; std::error_code ec; auto block = builder - .account (nano::test_genesis_key.pub) - .previous (latest) - .balance (balance) - .link (info.key.pub) - .representative (nano::test_genesis_key.pub) - .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) - .work (*node.work_generate_blocking (latest, nano::work_threshold (nano::work_version::work_1, nano::block_details (nano::epoch::epoch_0, false, false, false)))) + .account (info.key.pub) + .previous (0) + .balance (amount) + .link (info.pending_hash) + .representative (info.key.pub) + .sign (info.key.prv, info.key.pub) + .work (*node.work_generate_blocking (info.key.pub, nano::work_threshold (nano::work_version::work_1, nano::block_details (nano::epoch::epoch_0, false, false, false)))) .build (ec); ASSERT_FALSE (ec); ASSERT_NE (nullptr, block); ASSERT_EQ (nano::process_result::progress, node.process (*block).code); - latest = block->hash (); - info.pending_hash = block->hash (); } - } - ASSERT_EQ (1 + total_accounts, node.ledger.cache.block_count); - ASSERT_EQ (1, node.ledger.cache.account_count); + ASSERT_EQ (1 + total_accounts + opened.size (), node.ledger.cache.block_count); + ASSERT_EQ (1 + opened.size (), node.ledger.cache.account_count); - // Receive for half of accounts - for (auto const & info : opened) - { - nano::state_block_builder builder; - std::error_code ec; - auto block = builder - .account (info.key.pub) - .previous (0) - .balance (amount) - .link (info.pending_hash) - .representative (info.key.pub) - .sign (info.key.prv, info.key.pub) - .work (*node.work_generate_blocking (info.key.pub, nano::work_threshold (nano::work_version::work_1, nano::block_details (nano::epoch::epoch_0, false, false, false)))) - .build (ec); - ASSERT_FALSE (ec); - ASSERT_NE (nullptr, block); - ASSERT_EQ (nano::process_result::progress, node.process (*block).code); - } - ASSERT_EQ (1 + total_accounts + opened.size (), node.ledger.cache.block_count); - ASSERT_EQ (1 + opened.size (), node.ledger.cache.account_count); - - nano::keypair epoch_signer (nano::test_genesis_key); + nano::keypair epoch_signer (nano::test_genesis_key); - auto block_count_before = node.ledger.cache.block_count.load (); - std::cout << "Mass upgrading " << 1 + total_accounts << " accounts" << std::endl; - auto future = std::async ( - std::launch::async, [node_l = node.shared (), signer = epoch_signer.prv.as_private_key (), epoch = nano::epoch::epoch_1, total = 1 + total_accounts, threads] { - node_l->epoch_upgrader (signer, epoch, total, threads); - }); - auto expected_blocks = block_count_before + total_accounts + 1; - system.deadline_set (300s); - while (node.ledger.cache.block_count != expected_blocks) - { - ASSERT_NO_ERROR (system.poll ()); - std::this_thread::sleep_for (1s); - std::cout << node.ledger.cache.block_count - block_count_before << " / " << expected_blocks - block_count_before << std::endl; - } - ASSERT_EQ (expected_blocks, node.ledger.cache.block_count); - // Check upgrade - { - auto transaction (node.store.tx_begin_read ()); - ASSERT_EQ (expected_blocks, node.store.block_count (transaction).sum ()); - for (auto i (node.store.latest_begin (transaction)); i != node.store.latest_end (); ++i) + auto const block_count_before = node.ledger.cache.block_count.load (); + auto const total_to_upgrade = 1 + total_accounts; + std::cout << "Mass upgrading " << total_to_upgrade << " accounts" << std::endl; + while (node.ledger.cache.block_count != block_count_before + total_to_upgrade) { - nano::account_info info (i->second); - ASSERT_EQ (info.epoch (), nano::epoch::epoch_1); + auto const pre_upgrade = node.ledger.cache.block_count.load (); + auto upgrade_count = std::min (batch_size, block_count_before + total_to_upgrade - pre_upgrade); + std::thread ([node_l = node.shared (), signer = epoch_signer.prv.as_private_key (), epoch = nano::epoch::epoch_1, upgrade_count, threads] { + node_l->epoch_upgrader (signer, epoch, upgrade_count, threads); + }) + .detach (); + system.deadline_set (60s); + while (node.ledger.cache.block_count != pre_upgrade + upgrade_count) + { + ASSERT_NO_ERROR (system.poll ()); + std::this_thread::sleep_for (200ms); + std::cout << node.ledger.cache.block_count - block_count_before << " / " << total_to_upgrade << std::endl; + } } - } + auto expected_blocks = block_count_before + total_accounts + 1; + ASSERT_EQ (expected_blocks, node.ledger.cache.block_count); + // Check upgrade + { + auto transaction (node.store.tx_begin_read ()); + ASSERT_EQ (expected_blocks, node.store.block_count (transaction).sum ()); + for (auto i (node.store.latest_begin (transaction)); i != node.store.latest_end (); ++i) + { + nano::account_info info (i->second); + ASSERT_EQ (info.epoch (), nano::epoch::epoch_1); + } + } + }; + // Test with a limited number of upgrades and an unlimited + perform_test (42); + perform_test (std::numeric_limits::max ()); } From bf76b1f644d102c3b7a39067ec273e0471f2acf5 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Mon, 13 Apr 2020 22:05:41 +0100 Subject: [PATCH 3/5] Use node.epoch_upgrader directly in the slow test --- nano/lib/locks.hpp | 5 +++++ nano/node/node.cpp | 2 +- nano/slow_test/node.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/nano/lib/locks.hpp b/nano/lib/locks.hpp index 5d29bad5d2..f0b34bdb6d 100644 --- a/nano/lib/locks.hpp +++ b/nano/lib/locks.hpp @@ -114,6 +114,11 @@ class locked return owner->obj; } + T & operator* () const + { + return get (); + } + locked * owner{ nullptr }; }; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index b69074d5a2..33f6a5d11d 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1360,7 +1360,7 @@ bool nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch ep error = epoch_upgrade->valid () && epoch_upgrade->wait_for (std::chrono::seconds (0)) == std::future_status::timeout; if (!error) { - epoch_upgrade.get () = std::async (std::launch::async, std::bind (&nano::node::epoch_upgrader_impl, this, prv_a, epoch_a, count_limit, threads)); + *epoch_upgrade = std::async (std::launch::async, std::bind (&nano::node::epoch_upgrader_impl, this, prv_a, epoch_a, count_limit, threads)); } } return error; diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index e380116563..da8ce932dc 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -1267,10 +1267,9 @@ TEST (node, mass_epoch_upgrader) { auto const pre_upgrade = node.ledger.cache.block_count.load (); auto upgrade_count = std::min (batch_size, block_count_before + total_to_upgrade - pre_upgrade); - std::thread ([node_l = node.shared (), signer = epoch_signer.prv.as_private_key (), epoch = nano::epoch::epoch_1, upgrade_count, threads] { - node_l->epoch_upgrader (signer, epoch, upgrade_count, threads); - }) - .detach (); + ASSERT_FALSE (node.epoch_upgrader (epoch_signer.prv.as_private_key (), nano::epoch::epoch_1, upgrade_count, threads)); + // Already ongoing - should fail + ASSERT_TRUE (node.epoch_upgrader (epoch_signer.prv.as_private_key (), nano::epoch::epoch_1, upgrade_count, threads)); system.deadline_set (60s); while (node.ledger.cache.block_count != pre_upgrade + upgrade_count) { @@ -1278,6 +1277,7 @@ TEST (node, mass_epoch_upgrader) std::this_thread::sleep_for (200ms); std::cout << node.ledger.cache.block_count - block_count_before << " / " << total_to_upgrade << std::endl; } + std::this_thread::sleep_for (50ms); } auto expected_blocks = block_count_before + total_accounts + 1; ASSERT_EQ (expected_blocks, node.ledger.cache.block_count); From 1c7a7a13f9fa598727d83ddca5944313e5b9c712 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Tue, 14 Apr 2020 07:09:46 +0100 Subject: [PATCH 4/5] std min fix --- nano/slow_test/node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index da8ce932dc..09187c10a5 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -1266,7 +1266,7 @@ TEST (node, mass_epoch_upgrader) while (node.ledger.cache.block_count != block_count_before + total_to_upgrade) { auto const pre_upgrade = node.ledger.cache.block_count.load (); - auto upgrade_count = std::min (batch_size, block_count_before + total_to_upgrade - pre_upgrade); + auto upgrade_count = std::min (batch_size, block_count_before + total_to_upgrade - pre_upgrade); ASSERT_FALSE (node.epoch_upgrader (epoch_signer.prv.as_private_key (), nano::epoch::epoch_1, upgrade_count, threads)); // Already ongoing - should fail ASSERT_TRUE (node.epoch_upgrader (epoch_signer.prv.as_private_key (), nano::epoch::epoch_1, upgrade_count, threads)); From f39e724b6ad73b1e57c57ab8fa53826c10e793aa Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Tue, 14 Apr 2020 09:20:14 +0100 Subject: [PATCH 5/5] No need for std::bind (Wes review) --- nano/node/node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 33f6a5d11d..a76fcbefa8 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1360,7 +1360,7 @@ bool nano::node::epoch_upgrader (nano::private_key const & prv_a, nano::epoch ep error = epoch_upgrade->valid () && epoch_upgrade->wait_for (std::chrono::seconds (0)) == std::future_status::timeout; if (!error) { - *epoch_upgrade = std::async (std::launch::async, std::bind (&nano::node::epoch_upgrader_impl, this, prv_a, epoch_a, count_limit, threads)); + *epoch_upgrade = std::async (std::launch::async, &nano::node::epoch_upgrader_impl, this, prv_a, epoch_a, count_limit, threads); } } return error;