Browse Source

Merge branch 'xfer_time_coarse'

Nick Mathewson 6 years ago
parent
commit
69a320ce5d

+ 4 - 0
changes/bug24613

@@ -0,0 +1,4 @@
+  o Minor features (performance, 32-bit):
+    - Improve performance on 32-bit systems by avoiding 64-bit division
+      to calculate the current timestamp in milliseconds for channel
+      padding computations. Implements ticket 24613.

+ 91 - 0
src/common/compat_time.c

@@ -351,6 +351,20 @@ monotime_coarse_to_stamp(const monotime_coarse_t *t)
   return (uint32_t)(t->abstime_ >> monotime_shift);
 }
 
+int
+monotime_is_zero(const monotime_t *val)
+{
+  return val->abstime_ == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+  const uint64_t nsec = msec * ONE_MILLION;
+  const uint64_t ticks = (nsec * mach_time_info.denom) / mach_time_info.numer;
+  out->abstime_ = val->abstime_ + ticks;
+}
+
 /* end of "__APPLE__" */
 #elif defined(HAVE_CLOCK_GETTIME)
 
@@ -441,6 +455,25 @@ monotime_coarse_to_stamp(const monotime_coarse_t *t)
   return (sec * STAMP_TICKS_PER_SECOND) + (nsec >> 20);
 }
 
+int
+monotime_is_zero(const monotime_t *val)
+{
+  return val->ts_.tv_sec == 0 && val->ts_.tv_nsec == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+  const uint32_t sec = msec / 1000;
+  const uint32_t msec_remainder = msec % 1000;
+  out->ts_.tv_sec = val->ts_.tv_sec + sec;
+  out->ts_.tv_nsec = val->ts_.tv_nsec + (msec_remainder * ONE_MILLION);
+  if (out->ts_.tv_nsec > ONE_BILLION) {
+    out->ts_.tv_nsec -= ONE_BILLION;
+    out->ts_.tv_sec += 1;
+  }
+}
+
 /* end of "HAVE_CLOCK_GETTIME" */
 #elif defined (_WIN32)
 
@@ -581,6 +614,32 @@ monotime_coarse_to_stamp(const monotime_coarse_t *t)
   return (uint32_t) t->tick_count_;
 }
 
+int
+monotime_is_zero(const monotime_t *val)
+{
+  return val->pcount_ == 0;
+}
+
+int
+monotime_coarse_is_zero(const monotime_coarse_t *val)
+{
+  return val->tick_count_ == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+  const uint64_t nsec = msec * ONE_MILLION;
+  const uint64_t ticks = (nsec * nsec_per_tick_denom) / nsec_per_tick_numer;
+  out->pcount_ = val->pcount_ + ticks;
+}
+
+void
+monotime_coarse_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+  out->tick_count_ = val->tick_count_ + msec;
+}
+
 /* end of "_WIN32" */
 #elif defined(MONOTIME_USING_GETTIMEOFDAY)
 
@@ -628,6 +687,25 @@ monotime_coarse_to_stamp(const monotime_coarse_t *t)
   return (sec * STAMP_TICKS_PER_SECOND) | (nsec >> 10);
 }
 
+int
+monotime_is_zero(const monotime_t *val)
+{
+  return val->tv_.tv_sec == 0 && val->tv_.tv_usec == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+  const uint32_t sec = msec / 1000;
+  const uint32_t msec_remainder = msec % 1000;
+  out->tv_.tv_sec = val->tv_.tv_sec + sec;
+  out->tv_.tv_usec = val->tv_.tv_nsec + (msec_remainder * 1000);
+  if (out->tv_.tv_usec > ONE_MILLION) {
+    out->tv_.tv_usec -= ONE_MILLION;
+    out->tv_.tv_sec += 1;
+  }
+}
+
 /* end of "MONOTIME_USING_GETTIMEOFDAY" */
 #else
 #error "No way to implement monotonic timers."
@@ -650,6 +728,19 @@ monotime_init(void)
   }
 }
 
+void
+monotime_zero(monotime_t *out)
+{
+  memset(out, 0, sizeof(*out));
+}
+#ifdef MONOTIME_COARSE_TYPE_IS_DIFFERENT
+void
+monotime_coarse_zero(monotime_coarse_t *out)
+{
+  memset(out, 0, sizeof(*out));
+}
+#endif
+
 int64_t
 monotime_diff_usec(const monotime_t *start,
                    const monotime_t *end)

+ 22 - 0
src/common/compat_time.h

@@ -105,6 +105,21 @@ uint64_t monotime_absolute_usec(void);
  */
 uint64_t monotime_absolute_msec(void);
 
+/**
+ * Set <b>out</b> to zero.
+ */
+void monotime_zero(monotime_t *out);
+/**
+ * Return true iff <b>out</b> is zero
+ */
+int monotime_is_zero(const monotime_t *out);
+
+/**
+ * Set <b>out</b> to N milliseconds after <b>val</b>.
+ */
+/* XXXX We should add a more generic function here if we ever need to */
+void monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec);
+
 #if defined(MONOTIME_COARSE_FN_IS_DIFFERENT)
 /**
  * Set <b>out</b> to the current coarse time.
@@ -144,10 +159,17 @@ int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start,
     const monotime_coarse_t *end);
 int64_t monotime_coarse_diff_msec(const monotime_coarse_t *start,
     const monotime_coarse_t *end);
+void monotime_coarse_zero(monotime_coarse_t *out);
+int monotime_coarse_is_zero(const monotime_coarse_t *val);
+void monotime_coarse_add_msec(monotime_coarse_t *out,
+                              const monotime_coarse_t *val, uint32_t msec);
 #else /* !(defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)) */
 #define monotime_coarse_diff_nsec monotime_diff_nsec
 #define monotime_coarse_diff_usec monotime_diff_usec
 #define monotime_coarse_diff_msec monotime_diff_msec
+#define monotime_coarse_zero monotime_zero
+#define monotime_coarse_is_zero monotime_is_zero
+#define monotime_coarse_add_msec monotime_add_msec
 #endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
 
 void tor_gettimeofday(struct timeval *timeval);

+ 6 - 6
src/or/channel.c

@@ -3241,12 +3241,12 @@ channel_timestamp_active(channel_t *chan)
   time_t now = time(NULL);
 
   tor_assert(chan);
-  chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
+  monotime_coarse_get(&chan->timestamp_xfer);
 
   chan->timestamp_active = now;
 
   /* Clear any potential netflow padding timer. We're active */
-  chan->next_padding_time_ms = 0;
+  monotime_coarse_zero(&chan->next_padding_time);
 }
 
 /**
@@ -3311,13 +3311,13 @@ channel_timestamp_recv(channel_t *chan)
 {
   time_t now = time(NULL);
   tor_assert(chan);
-  chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
+  monotime_coarse_get(&chan->timestamp_xfer);
 
   chan->timestamp_active = now;
   chan->timestamp_recv = now;
 
   /* Clear any potential netflow padding timer. We're active */
-  chan->next_padding_time_ms = 0;
+  monotime_coarse_zero(&chan->next_padding_time);
 }
 
 /**
@@ -3332,13 +3332,13 @@ channel_timestamp_xmit(channel_t *chan)
   time_t now = time(NULL);
   tor_assert(chan);
 
-  chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
+  monotime_coarse_get(&chan->timestamp_xfer);
 
   chan->timestamp_active = now;
   chan->timestamp_xmit = now;
 
   /* Clear any potential netflow padding timer. We're active */
-  chan->next_padding_time_ms = 0;
+  monotime_coarse_zero(&chan->next_padding_time);
 }
 
 /***************************************************************

+ 5 - 6
src/or/channel.h

@@ -88,10 +88,9 @@ struct channel_s {
    * Used to decide what channels to pad, and when. */
   channel_usage_info_t channel_usage;
 
-  /** When should we send a cell for netflow padding, in absolute
-   *  milliseconds since monotime system start. 0 means no padding
-   *  is scheduled. */
-  uint64_t next_padding_time_ms;
+  /** When should we send a cell for netflow padding? 0 means no padding is
+   *  scheduled. */
+  monotime_coarse_t next_padding_time;
 
   /** The callback pointer for the padding callbacks */
   tor_timer_t *padding_timer;
@@ -158,7 +157,7 @@ struct channel_s {
   time_t timestamp_active; /* Any activity */
 
   /**
-   * This is a high-resolution monotonic timestamp that marks when we
+   * This is a monotonic timestamp that marks when we
    * believe the channel has actually sent or received data to/from
    * the wire. Right now, it is used to determine when we should send
    * a padding cell for channelpadding.
@@ -167,7 +166,7 @@ struct channel_s {
    * accurately reflect actual network data transfer? Or might this be
    * very wrong wrt when bytes actually go on the wire?
    */
-  uint64_t timestamp_xfer_ms;
+  monotime_coarse_t timestamp_xfer;
 
   /* Methods implemented by the lower layer */
 

+ 29 - 25
src/or/channelpadding.c

@@ -23,7 +23,8 @@
 #include <event2/event.h>
 #include "rendservice.h"
 
-STATIC int channelpadding_get_netflow_inactive_timeout_ms(const channel_t *);
+STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms(
+                                                           const channel_t *);
 STATIC int channelpadding_send_disable_command(channel_t *);
 STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *);
 
@@ -165,7 +166,7 @@ channelpadding_new_consensus_params(networkstatus_t *ns)
  * Returns the next timeout period (in milliseconds) after which we should
  * send a padding packet, or 0 if padding is disabled.
  */
-STATIC int
+STATIC int32_t
 channelpadding_get_netflow_inactive_timeout_ms(const channel_t *chan)
 {
   int low_timeout = consensus_nf_ito_low;
@@ -377,15 +378,16 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan)
 
   chan->pending_padding_callback = 0;
 
-  if (!chan->next_padding_time_ms ||
+  if (monotime_coarse_is_zero(&chan->next_padding_time) ||
       chan->has_queued_writes(chan)) {
     /* We must have been active before the timer fired */
-    chan->next_padding_time_ms = 0;
+    monotime_coarse_zero(&chan->next_padding_time);
     return;
   }
 
   {
-    uint64_t now = monotime_coarse_absolute_msec();
+    monotime_coarse_t now;
+    monotime_coarse_get(&now);
 
     log_fn(LOG_INFO,LD_OR,
         "Sending netflow keepalive on "U64_FORMAT" to %s (%s) after "
@@ -393,12 +395,13 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan)
         U64_PRINTF_ARG(chan->global_identifier),
         safe_str_client(chan->get_remote_descr(chan, 0)),
         safe_str_client(hex_str(chan->identity_digest, DIGEST_LEN)),
-        U64_PRINTF_ARG(now - chan->timestamp_xfer_ms),
-        U64_PRINTF_ARG(now - chan->next_padding_time_ms));
+        I64_PRINTF_ARG(monotime_coarse_diff_msec(&chan->timestamp_xfer,&now)),
+        I64_PRINTF_ARG(
+                   monotime_coarse_diff_msec(&chan->next_padding_time,&now)));
   }
 
   /* Clear the timer */
-  chan->next_padding_time_ms = 0;
+  monotime_coarse_zero(&chan->next_padding_time);
 
   /* Send the padding cell. This will cause the channel to get a
    * fresh timestamp_active */
@@ -500,38 +503,42 @@ channelpadding_schedule_padding(channel_t *chan, int in_ms)
 STATIC int64_t
 channelpadding_compute_time_until_pad_for_netflow(channel_t *chan)
 {
-  uint64_t long_now = monotime_coarse_absolute_msec();
+  monotime_coarse_t now;
+  monotime_coarse_get(&now);
 
-  if (!chan->next_padding_time_ms) {
+  if (monotime_coarse_is_zero(&chan->next_padding_time)) {
     /* If the below line or crypto_rand_int() shows up on a profile,
      * we can avoid getting a timeout until we're at least nf_ito_lo
      * from a timeout window. That will prevent us from setting timers
      * on connections that were active up to 1.5 seconds ago.
      * Idle connections should only call this once every 5.5s on average
      * though, so that might be a micro-optimization for little gain. */
-    int64_t padding_timeout =
+    int32_t padding_timeout =
         channelpadding_get_netflow_inactive_timeout_ms(chan);
 
     if (!padding_timeout)
       return CHANNELPADDING_TIME_DISABLED;
 
-    chan->next_padding_time_ms = padding_timeout
-                                 + chan->timestamp_xfer_ms;
+    monotime_coarse_add_msec(&chan->next_padding_time,
+                             &chan->timestamp_xfer,
+                             padding_timeout);
   }
 
+  const int64_t ms_till_pad =
+    monotime_coarse_diff_msec(&now, &chan->next_padding_time);
+
   /* If the next padding time is beyond the maximum possible consensus value,
    * then this indicates a clock jump, so just send padding now. This is
    * better than using monotonic time because we want to avoid the situation
    * where we wait around forever for monotonic time to move forward after
    * a clock jump far into the past.
    */
-  if (chan->next_padding_time_ms > long_now +
-      DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) {
+  if (ms_till_pad > DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) {
     tor_fragile_assert();
     log_warn(LD_BUG,
         "Channel padding timeout scheduled "I64_FORMAT"ms in the future. "
         "Did the monotonic clock just jump?",
-        I64_PRINTF_ARG(chan->next_padding_time_ms - long_now));
+        I64_PRINTF_ARG(ms_till_pad));
     return 0; /* Clock jumped: Send padding now */
   }
 
@@ -540,11 +547,8 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan)
      from now which we should send padding, so we can schedule a callback
      then.
    */
-  if (long_now +
-      (TOR_HOUSEKEEPING_CALLBACK_MSEC + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)
-      >= chan->next_padding_time_ms) {
-    int64_t ms_until_pad_for_netflow = chan->next_padding_time_ms -
-                                       long_now;
+  if (ms_till_pad < (TOR_HOUSEKEEPING_CALLBACK_MSEC +
+                       TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)) {
     /* If the padding time is in the past, that means that libevent delayed
      * calling the once-per-second callback due to other work taking too long.
      * See https://bugs.torproject.org/22212 and
@@ -554,16 +558,16 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan)
      * and allowed a router to emit a netflow frame, just so we don't forget
      * about it entirely.. */
 #define NETFLOW_MISSED_WINDOW (150000 - DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH)
-    if (ms_until_pad_for_netflow < 0) {
-      int severity = (ms_until_pad_for_netflow < -NETFLOW_MISSED_WINDOW)
+    if (ms_till_pad < 0) {
+      int severity = (ms_till_pad < -NETFLOW_MISSED_WINDOW)
                       ? LOG_NOTICE : LOG_INFO;
       log_fn(severity, LD_OR,
               "Channel padding timeout scheduled "I64_FORMAT"ms in the past. ",
-              I64_PRINTF_ARG(-ms_until_pad_for_netflow));
+             I64_PRINTF_ARG(-ms_till_pad));
       return 0; /* Clock jumped: Send padding now */
     }
 
-    return ms_until_pad_for_netflow;
+    return ms_till_pad;
   }
   return CHANNELPADDING_TIME_LATER;
 }

+ 3 - 3
src/test/test_channel.c

@@ -703,7 +703,7 @@ test_channel_inbound_cell(void *arg)
   old_count = test_chan_fixed_cells_recved;
   channel_process_cell(chan, cell);
   tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count);
-  tt_u64_op(chan->timestamp_xfer_ms, OP_EQ, 0);
+  tt_assert(monotime_coarse_is_zero(&chan->timestamp_xfer));
   tt_u64_op(chan->timestamp_active, OP_EQ, 0);
   tt_u64_op(chan->timestamp_recv, OP_EQ, 0);
 
@@ -716,10 +716,10 @@ test_channel_inbound_cell(void *arg)
   channel_process_cell(chan, cell);
   tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1);
   /* We should have a series of timestamp set. */
-  tt_u64_op(chan->timestamp_xfer_ms, OP_NE, 0);
+  tt_assert(!monotime_coarse_is_zero(&chan->timestamp_xfer));
   tt_u64_op(chan->timestamp_active, OP_NE, 0);
   tt_u64_op(chan->timestamp_recv, OP_NE, 0);
-  tt_u64_op(chan->next_padding_time_ms, OP_EQ, 0);
+  tt_assert(monotime_is_zero(&chan->next_padding_time));
   tt_u64_op(chan->n_cells_recved, OP_EQ, 1);
   tt_u64_op(chan->n_bytes_recved, OP_EQ, get_cell_network_size(0));
 

+ 71 - 41
src/test/test_channelpadding.c

@@ -279,7 +279,6 @@ test_channelpadding_timers(void *arg)
 {
   channelpadding_decision_t decision;
   channel_t *chans[CHANNELS_TO_TEST];
-  int64_t new_time;
   (void)arg;
 
   tor_libevent_postfork();
@@ -289,8 +288,9 @@ test_channelpadding_timers(void *arg)
 
   monotime_init();
   monotime_enable_test_mocking();
-  monotime_set_mock_time_nsec(1);
-  monotime_coarse_set_mock_time_nsec(1);
+  uint64_t nsec_mock = 1;
+  monotime_set_mock_time_nsec(nsec_mock);
+  monotime_coarse_set_mock_time_nsec(nsec_mock);
 
   timers_initialize();
   channelpadding_new_consensus_params(NULL);
@@ -304,11 +304,14 @@ test_channelpadding_timers(void *arg)
     tried_to_write_cell = 0;
     int i = 0;
 
+    monotime_coarse_t now;
+    monotime_coarse_get(&now);
+
     /* This loop fills our timerslot array with timers of increasing time
      * until they fire */
     for (; i < CHANNELPADDING_MAX_TIMERS; i++) {
-      chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec()
-                                        + 10 + i*4;
+      monotime_coarse_add_msec(&chans[i]->next_padding_time,
+                        &now, 10 + i*4);
       decision = channelpadding_decide_to_pad_channel(chans[i]);
       tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
       tt_assert(chans[i]->pending_padding_callback);
@@ -318,7 +321,8 @@ test_channelpadding_timers(void *arg)
     /* This loop should add timers to the first position in the timerslot
      * array, since its timeout is before all other timers. */
     for (; i < CHANNELS_TO_TEST/3; i++) {
-      chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 1;
+      monotime_coarse_add_msec(&chans[i]->next_padding_time,
+                        &now, 1);
       decision = channelpadding_decide_to_pad_channel(chans[i]);
       tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
       tt_assert(chans[i]->pending_padding_callback);
@@ -329,8 +333,8 @@ test_channelpadding_timers(void *arg)
      * pseudorandom pattern.  It ensures that the lists can grow with multiple
      * timers in them. */
     for (; i < CHANNELS_TO_TEST/2; i++) {
-      chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 10 +
-          i*3 % CHANNELPADDING_MAX_TIMERS;
+      monotime_coarse_add_msec(&chans[i]->next_padding_time,
+                        &now, 10 + i*3 % CHANNELPADDING_MAX_TIMERS);
       decision = channelpadding_decide_to_pad_channel(chans[i]);
       tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
       tt_assert(chans[i]->pending_padding_callback);
@@ -340,8 +344,8 @@ test_channelpadding_timers(void *arg)
     /* This loop should add timers to the last position in the timerslot
      * array, since its timeout is after all other timers. */
     for (; i < CHANNELS_TO_TEST; i++) {
-      chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 500 +
-          i % CHANNELPADDING_MAX_TIMERS;
+      monotime_coarse_add_msec(&chans[i]->next_padding_time,
+                               &now, 500 + i % CHANNELPADDING_MAX_TIMERS);
       decision = channelpadding_decide_to_pad_channel(chans[i]);
       tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
       tt_assert(chans[i]->pending_padding_callback);
@@ -349,9 +353,9 @@ test_channelpadding_timers(void *arg)
     }
 
     // Wait for the timers and then kill the event loop.
-    new_time = (monotime_coarse_absolute_msec()+1001)*NSEC_PER_MSEC;
-    monotime_coarse_set_mock_time_nsec(new_time);
-    monotime_set_mock_time_nsec(new_time);
+    nsec_mock += 1001 * NSEC_PER_MSEC;
+    monotime_coarse_set_mock_time_nsec(nsec_mock);
+    monotime_set_mock_time_nsec(nsec_mock);
     timers_run_pending();
 
     tt_int_op(tried_to_write_cell, OP_EQ, CHANNELS_TO_TEST);
@@ -388,6 +392,7 @@ test_channelpadding_killonehop(void *arg)
   monotime_enable_test_mocking();
   monotime_set_mock_time_nsec(1);
   monotime_coarse_set_mock_time_nsec(1);
+  new_time = 1;
 
   timers_initialize();
   setup_mock_consensus();
@@ -401,9 +406,12 @@ test_channelpadding_killonehop(void *arg)
   smartlist_clear(current_md_consensus->net_params);
   channelpadding_new_consensus_params(current_md_consensus);
 
+  monotime_coarse_t now;
+  monotime_coarse_get(&now);
+
   tried_to_write_cell = 0;
   get_options_mutable()->Tor2webMode = 1;
-  client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(client_relay3);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_assert(client_relay3->pending_padding_callback);
@@ -413,9 +421,10 @@ test_channelpadding_killonehop(void *arg)
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!client_relay3->pending_padding_callback);
@@ -427,7 +436,7 @@ test_channelpadding_killonehop(void *arg)
 
   // Before the client tries to pad, the relay will still pad:
   tried_to_write_cell = 0;
-  relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100);
   get_options_mutable()->ORPort_set = 1;
   get_options_mutable()->Tor2webMode = 0;
   decision = channelpadding_decide_to_pad_channel(relay3_client);
@@ -435,9 +444,10 @@ test_channelpadding_killonehop(void *arg)
   tt_assert(relay3_client->pending_padding_callback);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!client_relay3->pending_padding_callback);
@@ -474,7 +484,8 @@ test_channelpadding_killonehop(void *arg)
   get_options_mutable()->ORPort_set = 0;
   get_options_mutable()->HiddenServiceSingleHopMode = 1;
   get_options_mutable()->HiddenServiceNonAnonymousMode = 1;
-  client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+
+  monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(client_relay3);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_assert(client_relay3->pending_padding_callback);
@@ -484,9 +495,10 @@ test_channelpadding_killonehop(void *arg)
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101 * NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!client_relay3->pending_padding_callback);
@@ -498,7 +510,7 @@ test_channelpadding_killonehop(void *arg)
 
   // Before the client tries to pad, the relay will still pad:
   tried_to_write_cell = 0;
-  relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100);
   get_options_mutable()->ORPort_set = 1;
   get_options_mutable()->HiddenServiceSingleHopMode = 0;
   get_options_mutable()->HiddenServiceNonAnonymousMode = 0;
@@ -507,9 +519,10 @@ test_channelpadding_killonehop(void *arg)
   tt_assert(relay3_client->pending_padding_callback);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101 * NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!client_relay3->pending_padding_callback);
@@ -573,6 +586,9 @@ test_channelpadding_consensus(void *arg)
   monotime_enable_test_mocking();
   monotime_set_mock_time_nsec(1);
   monotime_coarse_set_mock_time_nsec(1);
+  new_time = 1;
+  monotime_coarse_t now;
+  monotime_coarse_get(&now);
   timers_initialize();
 
   if (!connection_array)
@@ -586,7 +602,7 @@ test_channelpadding_consensus(void *arg)
 
   /* Test 1: Padding can be completely disabled via consensus */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_assert(chan->pending_padding_callback);
@@ -596,9 +612,10 @@ test_channelpadding_consensus(void *arg)
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!chan->pending_padding_callback);
@@ -628,7 +645,7 @@ test_channelpadding_consensus(void *arg)
   tt_i64_op(val, OP_EQ, 0);
   val = channelpadding_compute_time_until_pad_for_netflow(chan);
   tt_i64_op(val, OP_EQ, -2);
-  tt_assert(!chan->next_padding_time_ms);
+  tt_assert(monotime_coarse_is_zero(&chan->next_padding_time));
 
   smartlist_clear(current_md_consensus->net_params);
 
@@ -641,7 +658,7 @@ test_channelpadding_consensus(void *arg)
   channelpadding_new_consensus_params(current_md_consensus);
 
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_assert(chan->pending_padding_callback);
@@ -653,9 +670,10 @@ test_channelpadding_consensus(void *arg)
   tt_i64_op(val, OP_LE, 200);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+201)*NSEC_PER_MSEC;
+  new_time += 201*NSEC_PER_MSEC;
   monotime_set_mock_time_nsec(new_time);
   monotime_coarse_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!chan->pending_padding_callback);
@@ -944,6 +962,9 @@ test_channelpadding_decide_to_pad_channel(void *arg)
   monotime_enable_test_mocking();
   monotime_set_mock_time_nsec(1);
   monotime_coarse_set_mock_time_nsec(1);
+  new_time = 1;
+  monotime_coarse_t now;
+  monotime_coarse_get(&now);
   timers_initialize();
   setup_full_capture_of_logs(LOG_WARN);
   channelpadding_new_consensus_params(NULL);
@@ -960,7 +981,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
 
   /* Test case #2a: > 1.1s until timeout */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1200;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 1200);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER);
   tt_assert(!chan->pending_padding_callback);
@@ -968,23 +989,27 @@ test_channelpadding_decide_to_pad_channel(void *arg)
 
   /* Test case #2b: >= 1.0s until timeout */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1000;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 1000);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_assert(chan->pending_padding_callback);
   tt_int_op(tried_to_write_cell, OP_EQ, 0);
 
+  // Set up a timer for the <0 case below.
+  monotime_coarse_t now_minus_100s;
+  monotime_coarse_add_msec(&now_minus_100s, &now, 900);
   // Wait for the timer from case #2b
-  new_time = (monotime_coarse_absolute_msec() + 1000)*NSEC_PER_MSEC;
+  new_time += 1000*NSEC_PER_MSEC;
   monotime_set_mock_time_nsec(new_time);
   monotime_coarse_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!chan->pending_padding_callback);
 
   /* Test case #2c: > 0.1s until timeout */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_assert(chan->pending_padding_callback);
@@ -995,16 +1020,17 @@ test_channelpadding_decide_to_pad_channel(void *arg)
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
   tt_assert(!chan->pending_padding_callback);
 
   /* Test case #2e: 0s until timeout */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec();
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 0);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT);
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
@@ -1012,7 +1038,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
 
   /* Test case #2f: <0s until timeout */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() - 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now_minus_100s, 0);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT);
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
@@ -1020,7 +1046,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
 
   /* Test case #3: Channel that sends a packet while timeout is scheduled */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1031,9 +1057,10 @@ test_channelpadding_decide_to_pad_channel(void *arg)
 
   // We don't expect any timer callbacks here. Make a dummy one to be sure.
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
 
   tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1041,7 +1068,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
 
   /* Test case #4: Channel that closes while a timeout is scheduled */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1051,9 +1078,10 @@ test_channelpadding_decide_to_pad_channel(void *arg)
   chan->state = CHANNEL_STATE_MAINT;
 
   // We don't expect any timer callbacks here. Make a dummy one to be sure.
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
 
   tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1062,16 +1090,17 @@ test_channelpadding_decide_to_pad_channel(void *arg)
 
   /* Test case #5: Make sure previous test case didn't break everything */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_assert(chan->pending_padding_callback);
   tt_int_op(tried_to_write_cell, OP_EQ, 0);
 
   // Wait for the timer
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time += 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
 
   tt_int_op(tried_to_write_cell, OP_EQ, 1);
@@ -1090,7 +1119,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
    * It must be last.
    */
   tried_to_write_cell = 0;
-  chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+  monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
   decision = channelpadding_decide_to_pad_channel(chan);
   tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
   tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1100,9 +1129,10 @@ test_channelpadding_decide_to_pad_channel(void *arg)
   free_fake_channeltls((channel_tls_t*)chan);
 
   // We don't expect any timer callbacks here. Make a dummy one to be sure.
-  new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+  new_time = 101*NSEC_PER_MSEC;
   monotime_coarse_set_mock_time_nsec(new_time);
   monotime_set_mock_time_nsec(new_time);
+  monotime_coarse_get(&now);
   timers_run_pending();
 
   tt_int_op(tried_to_write_cell, OP_EQ, 0);

+ 61 - 0
src/test/test_util.c

@@ -5925,6 +5925,65 @@ test_util_monotonic_time_ratchet(void *arg)
   ;
 }
 
+static void
+test_util_monotonic_time_zero(void *arg)
+{
+  (void) arg;
+  monotime_t t1;
+  monotime_coarse_t ct1;
+  monotime_init();
+  /* Check 1: The current time is not zero. */
+  monotime_get(&t1);
+  monotime_coarse_get(&ct1);
+  tt_assert(!monotime_is_zero(&t1));
+  tt_assert(!monotime_coarse_is_zero(&ct1));
+
+  /* Check 2: The _zero() makes the time zero. */
+  monotime_zero(&t1);
+  monotime_coarse_zero(&ct1);
+  tt_assert(monotime_is_zero(&t1));
+  tt_assert(monotime_coarse_is_zero(&ct1));
+ done:
+  ;
+}
+
+static void
+test_util_monotonic_time_add_msec(void *arg)
+{
+  (void) arg;
+  monotime_t t1, t2;
+  monotime_coarse_t ct1, ct2;
+  monotime_init();
+
+  monotime_get(&t1);
+  monotime_coarse_get(&ct1);
+
+  /* adding zero does nothing */
+  monotime_add_msec(&t2, &t1, 0);
+  monotime_coarse_add_msec(&ct2, &ct1, 0);
+  tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 0);
+  tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 0);
+
+  /* Add 1337 msec; see if the diff function agree */
+  monotime_add_msec(&t2, &t1, 1337);
+  monotime_coarse_add_msec(&ct2, &ct1, 1337);
+  tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337);
+  tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337);
+
+  /* Add 1337 msec twice more; make sure that any second rollover issues
+   * worked. */
+  monotime_add_msec(&t2, &t2, 1337);
+  monotime_coarse_add_msec(&ct2, &ct2, 1337);
+  monotime_add_msec(&t2, &t2, 1337);
+  monotime_coarse_add_msec(&ct2, &ct2, 1337);
+  tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337*3);
+  tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337*3);
+
+  
+ done:
+  ;
+}
+
 static void
 test_util_htonll(void *arg)
 {
@@ -6158,6 +6217,8 @@ struct testcase_t util_tests[] = {
   UTIL_TEST(calloc_check, 0),
   UTIL_TEST(monotonic_time, 0),
   UTIL_TEST(monotonic_time_ratchet, TT_FORK),
+  UTIL_TEST(monotonic_time_zero, 0),
+  UTIL_TEST(monotonic_time_add_msec, 0),
   UTIL_TEST(htonll, 0),
   UTIL_TEST(get_unquoted_path, 0),
   END_OF_TESTCASES