Add mockable clock type #25101

pull MarcoFalke wants to merge 1 commits into bitcoin:master from MarcoFalke:2205-mock-clock-😃 changing 4 files +33 −17
  1. MarcoFalke commented at 11:10 AM on May 10, 2022: member

    This will be used primarily by the addr time refactor (https://github.com/bitcoin/bitcoin/pull/24697) to make addr relay time type safe. However, it can also be used in other places, and can be reviewed independently, so I split it up.

  2. MarcoFalke added the label Refactoring on May 10, 2022
  3. MarcoFalke commented at 11:12 AM on May 10, 2022: member

    Naming discussion welcome. Previous naming thread at #24697 (review)

  4. DrahtBot commented at 7:18 PM on May 10, 2022: member

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    No conflicts as of last run.

  5. in src/util/time.h:64 in fa66cf13a5 outdated
      57 | @@ -43,7 +58,10 @@ inline double CountSecondsDouble(SecondsDouble t) { return t.count(); }
      58 |  
      59 |  /**
      60 |   * DEPRECATED
      61 | - * Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
      62 | + * Use either:
      63 | + * - std::chrono::sytem_clock::now()  or Now<std::chrono::system_clock>() for system time
      64 | + * - std::chrono::steady_clock::now() or Now<std::chrono::steady_clock>() for steady time
      65 | + * - NodeClock::now()                 or Now<NodeClock>()                 for mockable system time
    


    ajtowns commented at 10:17 PM on May 10, 2022:

    Doesn't Now<>() want a timepoint type, rather than a clock? Should be Now<NodeSeconds>() or Now<SteadyMilliseconds>(), no?


    MarcoFalke commented at 6:04 PM on May 11, 2022:

    Good catch, reworked doxygen string

  6. in src/util/time.h:93 in fa66cf13a5 outdated
      87 | @@ -70,7 +88,10 @@ std::chrono::seconds GetMockTime();
      88 |  
      89 |  /** Return system time (or mocked time, if set) */
      90 |  template <typename T>
      91 | -T GetTime();
      92 | +T GetTime()
      93 | +{
      94 | +    return std::chrono::duration_cast<T>(NodeClock::now().time_since_epoch());
    


    ajtowns commented at 10:19 PM on May 10, 2022:

    return Now<std::chrono::time_point<NodeClock, T>>().time_since_epoch() ?


    MarcoFalke commented at 6:04 PM on May 11, 2022:

    Yes, makes sense to use the casting feature of Now.

  7. MarcoFalke force-pushed on May 11, 2022
  8. MarcoFalke force-pushed on May 11, 2022
  9. in src/util/time.h:44 in fa201cc1b8 outdated
      42 | - * This helper is used to convert durations before passing them over an
      43 | + * This helper is used to convert durations/time_points before passing them over an
      44 |   * interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
      45 |   */
      46 | +template <typename Clock>
      47 | +constexpr int64_t count_seconds(std::chrono::time_point<Clock, std::chrono::seconds> t)
    


    ajtowns commented at 6:26 PM on May 11, 2022:

    Should this be called seconds_since_epoch (or the camelcase equivalent) perhaps? Looking back through #24697 it doesn't seem like it's just a temporary that will go away, so might be worth clearly distinguishing something that's working with a timepoint vs a duration.

    At least as far as #24697 is concerned, this is effectively implementing the behaviour of NodeClock::to_time_t so it's a bit funny that you explicitly deleted it above then reimplemented it here :)


    MarcoFalke commented at 6:41 PM on May 11, 2022:

    Is time_t using more than 32-bits on all platforms? Also rounding/truncation is implementation defined. So it seems best to avoid.

    Also, I expect count_milliseconds to be added in the future, which to_time_t can't handle either.

    seconds_since_epoch sounds good, will change.


    MarcoFalke commented at 7:23 PM on May 11, 2022:

    Actually I think it is fine to silently do a cast inside the function. Similar to how it is fine to do a silent cast in Now.

    So I'll be pushing a version of this that takes the duration as a template parameter.

  10. MarcoFalke force-pushed on May 11, 2022
  11. in src/util/time.h:46 in fa0f3ffd31 outdated
      44 |   * interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
      45 |   */
      46 | +template <typename Duration, typename Timepoint>
      47 | +constexpr int64_t TicksSinceEpoch(Timepoint t)
      48 | +{
      49 | +    return std::chrono::time_point_cast<Duration>(t).time_since_epoch().count();
    


    ajtowns commented at 8:09 PM on May 11, 2022:

    Add a static_assert(std::is_integral_v<typename Duration::rep>) or have the return type be typename Duration::rep ?


    MarcoFalke commented at 5:45 AM on May 12, 2022:

    Good catch. Used auto return type

  12. MarcoFalke force-pushed on May 12, 2022
  13. MarcoFalke force-pushed on May 12, 2022
  14. MarcoFalke commented at 8:29 AM on May 12, 2022: member

    Rebased for changes in src/bitcoin-cli.cpp.

  15. in src/util/time.h:63 in fab086650e outdated
      57 | @@ -48,7 +58,11 @@ inline double CountSecondsDouble(SecondsDouble t) { return t.count(); }
      58 |  
      59 |  /**
      60 |   * DEPRECATED
      61 | - * Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
      62 | + * Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
      63 | + * ClockType is
      64 | + * - std::chrono::sytem_clock  for system time
    


    jonatack commented at 4:07 PM on May 16, 2022:

    s/sytem/system/

    Between system_clock, steady_clock, and NodeClock, which would you see used for the CNodeStats?


    ajtowns commented at 6:58 PM on May 16, 2022:

    I think NodeClock for everything except for really short waits, like ThreadMessageHandler (which is already steady_clock). system_clock only for implementing NodeClock and logging?


    MarcoFalke commented at 8:42 AM on May 17, 2022:

    thx, fixed typo

  16. jonatack commented at 4:09 PM on May 16, 2022: member

    Concept ACK / Edit: light code review, debug build, and tested running bitcoind locally on this branch

  17. in src/util/time.h:18 in fab086650e outdated
      13 | @@ -14,6 +14,16 @@
      14 |  
      15 |  using namespace std::chrono_literals;
      16 |  
      17 | +/** Mockable clock in the context of tests, otherwise the system clock */
      18 | +struct NodeClock : std::chrono::system_clock {
    


    ajtowns commented at 5:26 PM on May 16, 2022:

    Newline before { ? Isn't it better to put in public explicitly?


    jonatack commented at 7:05 PM on May 16, 2022:

    Public in structs might be unneeded, as access is public by default in structs / private by default in classes, and because https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c8-use-class-rather-than-struct-if-any-member-is-non-public


    ajtowns commented at 7:28 PM on May 16, 2022:

    Yeah, it's definitely unneeded, just seems clearer for people to have it there explicitly? Seems like we usually have an explicit public when inheriting a struct from some other struct.


    jonatack commented at 7:34 PM on May 16, 2022:

    Yes, good point re inheriting.


    MarcoFalke commented at 8:43 AM on May 17, 2022:

    Thx, added public inheritance

  18. ajtowns commented at 5:27 PM on May 16, 2022: member

    Looks good to me

  19. MarcoFalke force-pushed on May 17, 2022
  20. in src/util/time.h:29 in fa207814f1 outdated
      24 | +};
      25 | +using NodeSeconds = std::chrono::time_point<NodeClock, std::chrono::seconds>;
      26 | +
      27 |  using SteadySeconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::seconds>;
      28 |  using SteadyMilliseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::milliseconds>;
      29 |  using SteadyMicroseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::microseconds>;
    


    jonatack commented at 3:51 PM on May 17, 2022:

    Note: SteadyMicroseconds is currently unused and untested.


    MarcoFalke commented at 3:57 PM on May 17, 2022:

    Not sure how to test it or how to make it used. It was added in case someone wants to use it, but I am happy to remove it and let someone add it later if it gets used.


    MarcoFalke commented at 5:38 PM on May 18, 2022:

    side note: It can be tested in the // Check that steady time and system time changes after a sleep test case.

  21. in src/util/time.h:23 in fa207814f1 outdated
      18 | +struct NodeClock : public std::chrono::system_clock {
      19 | +    using time_point = std::chrono::time_point<NodeClock>;
      20 | +    /** Return current system time or mocked time, if set */
      21 | +    static time_point now() noexcept;
      22 | +    static std::time_t to_time_t(const time_point&) = delete; // unused
      23 | +    static time_point from_time_t(std::time_t) = delete;      // unused
    



    MarcoFalke commented at 3:58 PM on May 17, 2022:

    I am not touching de-/con-structors, so I guess no?


    jonatack commented at 6:15 PM on May 17, 2022:

    Right, the intention here is to delete the to_time_t and from_time_t class members inherited from std::chrono::system_clock, so IIUC, we don't care here about copy constructor deletion implicitly suppressing move constructor and move assignment operators.

    Verified these are deleted in the same way they are declared per https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t and https://en.cppreference.com/w/cpp/chrono/system_clock/from_time_t.


    ajtowns commented at 9:47 PM on May 17, 2022:

    You don't normally instantiate instances of clock types, all the methods on them are static / class methods, so constructors/destructors/copying don't matter?


    MarcoFalke commented at 5:03 PM on May 18, 2022:

    right. They don't matter and as I mentioned before, I am not touching them at all, so I fail to understand what the issue is.

    See also:

    (not meaningful, but compiles fine)

    diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
    index eeecbe5ea9..c6fd1ec548 100644
    --- a/src/test/util_tests.cpp
    +++ b/src/test/util_tests.cpp
    @@ -1483,6 +1483,17 @@ BOOST_AUTO_TEST_CASE(gettime)
     
     BOOST_AUTO_TEST_CASE(util_time_GetTime)
     {
    +{
    +auto c1{NodeClock{}};
    +auto c3{c1};
    +auto c2{std::move(c1)};
    +}
    +{
    +auto c1{std::chrono::system_clock{}};
    +auto c3{c1};
    +auto c2{std::move(c1)};
    +}
    +
         SetMockTime(111);
         // Check that mock time does not change after a sleep
         for (const auto& num_sleep : {0ms, 1ms}) {
    

    jonatack commented at 5:39 PM on May 18, 2022:

    I think the last three comments agree, but I'm not sure :) I did want to ask to make sure and check my assumptions. Thanks for the replies.


    jonatack commented at 8:28 AM on May 20, 2022:

    Continuing with that test code snippet, verified these don't compile as expected.

    test/util_tests.cpp:1487:25: error: attempt to use a deleted function
        auto c1{NodeClock{}.to_time_t(std::chrono::system_clock::now())};
                            ^
    ./util/time.h:22:24: note: 'to_time_t' has been explicitly marked deleted here
        static std::time_t to_time_t(const time_point&) = delete; // unused
    
    test/util_tests.cpp:1487:25: error: attempt to use a deleted function
        auto c1{NodeClock{}.from_time_t(std::chrono::system_clock{})};
                            ^
    ./util/time.h:23:23: note: 'from_time_t' has been explicitly marked deleted here
        static time_point from_time_t(std::time_t) = delete;      // unused
    
  22. jonatack commented at 3:55 PM on May 17, 2022: member

    ACK fa207814f126feeb47b4a1271142dc86f5ce678d

    Am running mainnet and signet nodes with this branch today.

  23. DrahtBot added the label Needs rebase on May 18, 2022
  24. Add mockable clock type and TicksSinceEpoch helper fa305fd92c
  25. MarcoFalke force-pushed on May 18, 2022
  26. MarcoFalke commented at 5:04 PM on May 18, 2022: member

    Rebased. Should be trivial to re-review

  27. jonatack commented at 5:32 PM on May 18, 2022: member

    ACK fa305fd92c0a5a91831be3ccec0a5ef962a5fbcb per git range-diff 7b3343f fa20781 fa305fd

  28. DrahtBot removed the label Needs rebase on May 18, 2022
  29. ajtowns commented at 3:53 AM on May 20, 2022: member

    ACK fa305fd92c0a5a91831be3ccec0a5ef962a5fbcb

  30. fanquake merged this on May 20, 2022
  31. fanquake closed this on May 20, 2022

  32. MarcoFalke deleted the branch on May 20, 2022
  33. sidhujag referenced this in commit d148a072ea on May 28, 2022
  34. DrahtBot locked this on May 20, 2023

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-13 18:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me