Replace once_a_time_seconds; send proofs faster

This replaces the horrible, horrible, badly misused templated
once_a_time_seconds and once_a_time_milliseconds with a `periodic_task`
that works the same way but takes parameters as constructor arguments
instead of template parameters.

It also makes various small improvements:

- uses std::chrono::steady_clock instead of ifdef'ing platform dependent
  timer code.
- takes a std::chrono duration rather than a template integer and
  scaling parameter.
- timers can be reset to trigger on the next invocation, and this is
  thread-safe.
- timer intervals can be changed at run-time.

This all then gets used to reset the proof timer immediately upon
receiving a ping (initially or after expiring) from storage server and
lokinet so that we send proofs out faster.
This commit is contained in:
Jason Rhinelander 2019-12-20 14:21:24 -04:00
parent 6e436c29b7
commit f3fdcb1fbc
8 changed files with 94 additions and 89 deletions

View File

@ -32,9 +32,8 @@
#include <list>
#include <numeric>
#include <boost/timer/timer.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/random_generator.hpp>
#include <chrono>
#include <atomic>
#include "misc_os_dependent.h"
#include "syncobj.h"
@ -230,57 +229,38 @@ namespace math_helper
}
}
template<uint64_t scale, int default_interval, bool start_immediate = true>
class once_a_time
{
uint64_t get_time() const
// Periodic timer that gatekeeps calling of a job to a minimum interval after the previous job
// finished. Only the reset() call is thread-safe; everything else should be confined to the
// owning thread.
class periodic_task {
public:
explicit periodic_task(std::chrono::microseconds interval, bool start_immediate = true)
: m_interval{interval}, m_last_worked_time{std::chrono::steady_clock::now()}, m_trigger_now{start_immediate}
{}
template <class functor_t>
void do_call(functor_t functr)
{
#ifdef _WIN32
FILETIME fileTime;
GetSystemTimeAsFileTime(&fileTime);
unsigned __int64 present = 0;
present |= fileTime.dwHighDateTime;
present = present << 32;
present |= fileTime.dwLowDateTime;
present /= 10; // mic-sec
return present;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000 + tv.tv_usec;
#endif
if (m_trigger_now || std::chrono::steady_clock::now() - m_last_worked_time > m_interval)
{
functr();
m_last_worked_time = std::chrono::steady_clock::now();
m_trigger_now = false;
}
}
public:
once_a_time():m_interval(default_interval * scale)
{
m_last_worked_time = 0;
if(!start_immediate)
m_last_worked_time = get_time();
}
// Makes the next task attempt run the job, regardless of the time since the last job. Atomic.
void reset() { m_trigger_now = true; }
// Returns the current interval
std::chrono::microseconds interval() const { return m_interval; }
// Changes the current interval
void interval(std::chrono::microseconds us) { m_interval = us; }
template<class functor_t>
bool do_call(functor_t functr)
{
uint64_t current_time = get_time();
if(current_time - m_last_worked_time > m_interval)
{
bool res = functr();
m_last_worked_time = get_time();
return res;
}
return true;
}
private:
uint64_t m_last_worked_time;
uint64_t m_interval;
};
template<int default_interval, bool start_immediate = true>
class once_a_time_seconds: public once_a_time<1000000, default_interval, start_immediate> {};
template<int default_interval, bool start_immediate = true>
class once_a_time_milliseconds: public once_a_time<1000, default_interval, start_immediate> {};
private:
std::chrono::microseconds m_interval;
std::chrono::steady_clock::time_point m_last_worked_time;
std::atomic<bool> m_trigger_now;
};
}
}

View File

@ -1952,6 +1952,10 @@ namespace cryptonote
}
return true;
}
void core::reset_proof_interval()
{
m_check_uptime_proof_interval.reset();
}
//-----------------------------------------------------------------------------------------------
void core::do_uptime_proof_call()
{
@ -1969,14 +1973,14 @@ namespace cryptonote
next_proof_time += UPTIME_PROOF_FREQUENCY_IN_SECONDS - UPTIME_PROOF_TIMER_SECONDS/2;
if ((uint64_t) std::time(nullptr) < next_proof_time)
return true;
return;
if (!check_external_ping(m_last_storage_server_ping, STORAGE_SERVER_PING_LIFETIME, "the storage server"))
{
MGINFO_RED(
"Failed to submit uptime proof: have not heard from the storage server recently. Make sure that it "
"is running! It is required to run alongside the Loki daemon");
return true;
return;
}
uint8_t hf_version = get_blockchain_storage().get_current_hard_fork_version();
if (!check_external_ping(m_last_lokinet_ping, LOKINET_PING_LIFETIME, "Lokinet"))
@ -1986,7 +1990,7 @@ namespace cryptonote
MGINFO_RED(
"Failed to submit uptime proof: have not heard from lokinet recently. Make sure that it "
"is running! It is required to run alongside the Loki daemon");
return true;
return;
}
else
{
@ -1997,14 +2001,12 @@ namespace cryptonote
}
submit_uptime_proof();
return true;
});
}
else
{
// reset the interval so that we're ready when we register, OR if we get deregistered this primes us up for re-registration in the same session
m_check_uptime_proof_interval = {};
m_check_uptime_proof_interval.reset();
}
}
//-----------------------------------------------------------------------------------------------

View File

@ -32,6 +32,7 @@
#include <ctime>
#include <future>
#include <chrono>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
@ -56,6 +57,8 @@ DISABLE_VS_WARNINGS(4355)
#include "common/loki_integration_test_hooks.h"
namespace cryptonote
{
using namespace std::literals;
struct test_options {
std::vector<std::pair<uint8_t, uint64_t>> hard_forks;
size_t long_term_block_weight_window;
@ -893,6 +896,12 @@ namespace cryptonote
*/
bool submit_uptime_proof();
/** Called to signal that a significant service node application ping has arrived (either the
* first, or the first after a long time). This triggers a check and attempt to send an uptime
* proof soon (i.e. at the next idle loop).
*/
void reset_proof_interval();
/*
* @brief get the blockchain pruning seed
*
@ -1098,16 +1107,16 @@ namespace cryptonote
cryptonote_protocol_stub m_protocol_stub; //!< cryptonote protocol stub instance
epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled
epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space
epee::math_helper::once_a_time_seconds<UPTIME_PROOF_TIMER_SECONDS, true> m_check_uptime_proof_interval; //!< interval for checking our own uptime proof
epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate
epee::math_helper::once_a_time_seconds<60*60*5, true> m_blockchain_pruning_interval; //!< interval for incremental blockchain pruning
epee::math_helper::once_a_time_seconds<60*2, false> m_service_node_vote_relayer;
epee::math_helper::once_a_time_seconds<60*60, false> m_sn_proof_cleanup_interval;
epee::math_helper::periodic_task m_store_blockchain_interval{12h, false}; //!< interval for manual storing of Blockchain, if enabled
epee::math_helper::periodic_task m_fork_moaner{2h}; //!< interval for checking HardFork status
epee::math_helper::periodic_task m_txpool_auto_relayer{2min, false}; //!< interval for checking re-relaying txpool transactions
epee::math_helper::periodic_task m_check_updates_interval{12h}; //!< interval for checking for new versions
epee::math_helper::periodic_task m_check_disk_space_interval{10min}; //!< interval for checking for disk space
epee::math_helper::periodic_task m_check_uptime_proof_interval{std::chrono::seconds{UPTIME_PROOF_TIMER_SECONDS}}; //!< interval for checking our own uptime proof
epee::math_helper::periodic_task m_block_rate_interval{90s, false}; //!< interval for checking block rate
epee::math_helper::periodic_task m_blockchain_pruning_interval{5h}; //!< interval for incremental blockchain pruning
epee::math_helper::periodic_task m_service_node_vote_relayer{2min, false};
epee::math_helper::periodic_task m_sn_proof_cleanup_interval{1h, false};
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?

View File

@ -33,6 +33,7 @@
#include <boost/program_options.hpp>
#include <boost/logic/tribool_fwd.hpp>
#include <atomic>
#include "cryptonote_basic/blobdatatype.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/verification_context.h"
#include "cryptonote_basic/difficulty.h"
@ -43,6 +44,7 @@
namespace cryptonote
{
using namespace std::literals;
struct i_miner_handler
{
@ -149,9 +151,9 @@ namespace cryptonote
i_miner_handler* m_phandler;
Blockchain* m_pbc;
account_public_address m_mine_address;
epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval;
epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval;
epee::math_helper::once_a_time_seconds<1> m_autodetect_interval;
epee::math_helper::periodic_task m_update_block_template_interval{5s};
epee::math_helper::periodic_task m_update_merge_hr_interval{2s};
epee::math_helper::periodic_task m_autodetect_interval{1s};
std::vector<blobdata> m_extra_messages;
miner_config m_config;
std::string m_config_folder_path;

View File

@ -55,6 +55,8 @@ namespace cryptonote
/* */
/************************************************************************/
using namespace std::literals;
//! tuple of <deregister, transaction fee, receive time> for organization
typedef std::pair<std::tuple<bool, double, std::time_t>, crypto::hash> tx_by_fee_and_receive_time_entry;
@ -713,7 +715,7 @@ namespace cryptonote
//TODO: this time should be a named constant somewhere, not hard-coded
//! interval on which to check for stale/"stuck" transactions
epee::math_helper::once_a_time_seconds<30> m_remove_stuck_tx_interval;
epee::math_helper::periodic_task m_remove_stuck_tx_interval{30s};
//TODO: look into doing this better
//!< container for transactions organized by fee per size and receive time

View File

@ -57,6 +57,7 @@ DISABLE_VS_WARNINGS(4355)
namespace cryptonote
{
using namespace std::literals;
template<class t_core>
class t_cryptonote_protocol_handler: public i_cryptonote_protocol
@ -180,9 +181,9 @@ namespace cryptonote
std::atomic<bool> m_no_sync;
boost::mutex m_sync_lock;
block_queue m_block_queue;
epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker;
epee::math_helper::once_a_time_milliseconds<100> m_standby_checker;
epee::math_helper::once_a_time_seconds<101> m_sync_search_checker;
epee::math_helper::periodic_task m_idle_peer_kicker{30s};
epee::math_helper::periodic_task m_standby_checker{100ms};
epee::math_helper::periodic_task m_sync_search_checker{101s};
std::atomic<unsigned int> m_max_out_peers;
tools::PerformanceTimer m_sync_timer, m_add_timer;
uint64_t m_last_add_end_time;

View File

@ -61,6 +61,7 @@ DISABLE_VS_WARNINGS(4355)
namespace nodetool
{
using namespace std::literals;
struct proxy
{
proxy()
@ -440,11 +441,11 @@ namespace nodetool
t_payload_net_handler& m_payload_handler;
peerlist_storage m_peerlist_storage;
epee::math_helper::once_a_time_seconds<P2P_DEFAULT_HANDSHAKE_INTERVAL> m_peer_handshake_idle_maker_interval;
epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval;
epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval;
epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval;
epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval;
epee::math_helper::periodic_task m_peer_handshake_idle_maker_interval{std::chrono::seconds{P2P_DEFAULT_HANDSHAKE_INTERVAL}};
epee::math_helper::periodic_task m_connections_maker_interval{1s};
epee::math_helper::periodic_task m_peerlist_store_interval{30min};
epee::math_helper::periodic_task m_gray_peerlist_housekeeping_interval{1min};
epee::math_helper::periodic_task m_incoming_connections_interval{1h};
uint64_t m_last_stat_request_time;
std::list<epee::net_utils::network_address> m_priority_peers;

View File

@ -3035,6 +3035,8 @@ namespace cryptonote
struct version_printer { const std::array<int, 3> &v; };
std::ostream &operator<<(std::ostream &o, const version_printer &vp) { return o << vp.v[0] << '.' << vp.v[1] << '.' << vp.v[2]; }
// Handles a ping. Returns true if the ping was significant (i.e. first ping after startup, or
// after the ping had expired).
template <typename Response>
bool handle_ping(const std::array<int, 3> &cur_version, const std::array<int, 3> &required, const char *name, std::atomic<std::time_t> &update, time_t lifetime, Response &res)
{
@ -3044,15 +3046,16 @@ namespace cryptonote
res.status = status.str();
MERROR(res.status);
} else {
auto now = std::time(nullptr);
if (update + lifetime < now) // Print loudly for the first ping after startup/expiry
MGINFO_GREEN("Received ping from " << name << " " << version_printer{cur_version});
else
MDEBUG("Accepted ping from " << name << " " << version_printer{cur_version});
update = now;
res.status = "OK";
auto now = std::time(nullptr);
auto old = update.exchange(now);
if (old + lifetime < now) { // Print loudly for the first ping after startup/expiry
MGINFO_GREEN("Received ping from " << name << " " << version_printer{cur_version});
return true;
}
MDEBUG("Accepted ping from " << name << " " << version_printer{cur_version});
}
return true;
return false;
}
}
@ -3062,8 +3065,10 @@ namespace cryptonote
epee::json_rpc::error&,
const connection_context*)
{
return handle_ping({req.version_major, req.version_minor, req.version_patch}, service_nodes::MIN_STORAGE_SERVER_VERSION,
"Storage Server", m_core.m_last_storage_server_ping, STORAGE_SERVER_PING_LIFETIME, res);
if (handle_ping({req.version_major, req.version_minor, req.version_patch}, service_nodes::MIN_STORAGE_SERVER_VERSION,
"Storage Server", m_core.m_last_storage_server_ping, STORAGE_SERVER_PING_LIFETIME, res))
m_core.reset_proof_interval();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_lokinet_ping(const COMMAND_RPC_LOKINET_PING::request& req,
@ -3071,7 +3076,10 @@ namespace cryptonote
epee::json_rpc::error&,
const connection_context*)
{
return handle_ping(req.version, service_nodes::MIN_LOKINET_VERSION, "Lokinet", m_core.m_last_lokinet_ping, LOKINET_PING_LIFETIME, res);
if (handle_ping(req.version, service_nodes::MIN_LOKINET_VERSION,
"Lokinet", m_core.m_last_lokinet_ping, LOKINET_PING_LIFETIME, res))
m_core.reset_proof_interval();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_staking_requirement(const COMMAND_RPC_GET_STAKING_REQUIREMENT::request& req, COMMAND_RPC_GET_STAKING_REQUIREMENT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)