-fix
[oweals/gnunet.git] / src / fragmentation / fragmentation.c
index d3483fc8d3f57a4f594a637829666cbc6459c727..4749f537819b8d10a9a11bcde50166b86d4535e3 100644 (file)
 #include "fragmentation.h"
 
 
+/**
+ * Absolute minimum delay we impose between sending and expecting ACK to arrive.
+ */
+#define MIN_ACK_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 1)
+
+
 /**
  * Fragmentation context.
  */
@@ -99,6 +105,16 @@ struct GNUNET_FRAGMENT_Context
    */
   unsigned int next_transmission;
 
+  /**
+   * How many rounds of transmission have we completed so far?
+   */
+  unsigned int num_rounds;
+
+  /**
+   * How many transmission have we completed in this round?
+   */
+  unsigned int num_transmissions;
+
   /**
    * GNUNET_YES if we called 'proc' and are now waiting for 'GNUNET_FRAGMENT_transmission_done'
    */
@@ -140,23 +156,22 @@ transmit_next (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GNUNET_assert (GNUNET_NO == fc->proc_busy);
   if (0 == fc->acks)
     return;                     /* all done */
-
   /* calculate delay */
   wrap = 0;
   while (0 == (fc->acks & (1LL << fc->next_transmission)))
   {
     fc->next_transmission = (fc->next_transmission + 1) % 64;
-    wrap |= (fc->next_transmission == 0);
+    wrap |= (0 == fc->next_transmission);
   }
   bit = fc->next_transmission;
   size = ntohs (fc->msg->size);
   if (bit == size / (fc->mtu - sizeof (struct FragmentHeader)))
     fsize =
-        size % (fc->mtu - sizeof (struct FragmentHeader)) +
+        (size % (fc->mtu - sizeof (struct FragmentHeader))) +
         sizeof (struct FragmentHeader);
   else
     fsize = fc->mtu;
-  if (fc->tracker != NULL)
+  if (NULL != fc->tracker)
     delay = GNUNET_BANDWIDTH_tracker_get_delay (fc->tracker, fsize);
   else
     delay = GNUNET_TIME_UNIT_ZERO;
@@ -167,6 +182,11 @@ transmit_next (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   }
   fc->next_transmission = (fc->next_transmission + 1) % 64;
   wrap |= (fc->next_transmission == 0);
+  while (0 == (fc->acks & (1LL << fc->next_transmission)))
+  {
+    fc->next_transmission = (fc->next_transmission + 1) % 64;
+    wrap |= (fc->next_transmission == 0);
+  }
 
   /* assemble fragmentation message */
   mbuf = (const char *) &fc[1];
@@ -176,16 +196,15 @@ transmit_next (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   fh->fragment_id = htonl (fc->fragment_id);
   fh->total_size = fc->msg->size;       /* already in big-endian */
   fh->offset = htons ((fc->mtu - sizeof (struct FragmentHeader)) * bit);
-  memcpy (&fh[1],
-          &mbuf[bit * (fc->mtu - sizeof (struct FragmentHeader))],
+  memcpy (&fh[1], &mbuf[bit * (fc->mtu - sizeof (struct FragmentHeader))],
           fsize - sizeof (struct FragmentHeader));
   if (NULL != fc->tracker)
     GNUNET_BANDWIDTH_tracker_consume (fc->tracker, fsize);
-  GNUNET_STATISTICS_update (fc->stats,
-                            _("# fragments transmitted"), 1, GNUNET_NO);
+  GNUNET_STATISTICS_update (fc->stats, _("# fragments transmitted"), 1,
+                            GNUNET_NO);
   if (0 != fc->last_round.abs_value)
-    GNUNET_STATISTICS_update (fc->stats,
-                              _("# fragments retransmitted"), 1, GNUNET_NO);
+    GNUNET_STATISTICS_update (fc->stats, _("# fragments retransmitted"), 1,
+                              GNUNET_NO);
 
   /* select next message to calculate delay */
   bit = fc->next_transmission;
@@ -201,15 +220,21 @@ transmit_next (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   if (wrap)
   {
     /* full round transmitted wait 2x delay for ACK before going again */
-    delay = GNUNET_TIME_relative_max (GNUNET_TIME_relative_multiply (delay, 2),
-                                      fc->delay);
+    fc->num_rounds++;
+    delay =
+        GNUNET_TIME_relative_max (GNUNET_TIME_relative_multiply (delay, 2),
+                                  GNUNET_TIME_relative_multiply (fc->delay,
+                                                                 fc->num_rounds));
     /* never use zero, need some time for ACK always */
-    delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS, delay);
-    fc->last_round = GNUNET_TIME_absolute_get ();
+    delay = GNUNET_TIME_relative_max (MIN_ACK_DELAY, delay);
     fc->wack = GNUNET_YES;
+    fc->last_round = GNUNET_TIME_absolute_get ();
+    GNUNET_STATISTICS_update (fc->stats, _("# fragments wrap arounds"), 1,
+                              GNUNET_NO);
   }
   fc->proc_busy = GNUNET_YES;
   fc->delay_until = GNUNET_TIME_relative_to_absolute (delay);
+  fc->num_transmissions++;
   fc->proc (fc->proc_cls, &fh->header);
 }
 
@@ -244,12 +269,11 @@ GNUNET_FRAGMENT_context_create (struct GNUNET_STATISTICS_Handle *stats,
   struct GNUNET_FRAGMENT_Context *fc;
   size_t size;
   uint64_t bits;
-
+  
   GNUNET_STATISTICS_update (stats, _("# messages fragmented"), 1, GNUNET_NO);
   GNUNET_assert (mtu >= 1024 + sizeof (struct FragmentHeader));
   size = ntohs (msg->size);
-  GNUNET_STATISTICS_update (stats,
-                            _("# total size of fragmented messages"),
+  GNUNET_STATISTICS_update (stats, _("# total size of fragmented messages"),
                             size, GNUNET_NO);
   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
   fc = GNUNET_malloc (sizeof (struct GNUNET_FRAGMENT_Context) + size);
@@ -260,8 +284,8 @@ GNUNET_FRAGMENT_context_create (struct GNUNET_STATISTICS_Handle *stats,
   fc->msg = (const struct GNUNET_MessageHeader *) &fc[1];
   fc->proc = proc;
   fc->proc_cls = proc_cls;
-  fc->fragment_id = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
-                                              UINT32_MAX);
+  fc->fragment_id =
+      GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX);
   memcpy (&fc[1], msg, size);
   bits =
       (size + mtu - sizeof (struct FragmentHeader) - 1) / (mtu -
@@ -325,22 +349,25 @@ GNUNET_FRAGMENT_process_ack (struct GNUNET_FRAGMENT_Context *fc,
   if (ntohl (fa->fragment_id) != fc->fragment_id)
     return GNUNET_SYSERR;       /* not our ACK */
   abits = GNUNET_ntohll (fa->bits);
-  if (GNUNET_YES == fc->wack)
+  if ( (GNUNET_YES == fc->wack) &&
+       (0 != fc->num_transmissions) )
   {
     /* normal ACK, can update running average of delay... */
     fc->wack = GNUNET_NO;
     ndelay = GNUNET_TIME_absolute_get_duration (fc->last_round);
-    fc->delay.rel_value = (ndelay.rel_value + 3 * fc->delay.rel_value) / 4;
+    fc->delay.rel_value =
+        (ndelay.rel_value / fc->num_transmissions + 3 * fc->delay.rel_value) / 4;
+    fc->num_transmissions = 0;
   }
   GNUNET_STATISTICS_update (fc->stats,
-                            _("# fragment acknowledgements received"),
-                            1, GNUNET_NO);
+                            _("# fragment acknowledgements received"), 1,
+                            GNUNET_NO);
   if (abits != (fc->acks & abits))
   {
     /* ID collission or message reordering, count! This should be rare! */
     GNUNET_STATISTICS_update (fc->stats,
-                              _("# bits removed from fragmentation ACKs"),
-                              1, GNUNET_NO);
+                              _("# bits removed from fragmentation ACKs"), 1,
+                              GNUNET_NO);
   }
   fc->acks = abits & fc->acks_mask;
   if (0 != fc->acks)
@@ -363,8 +390,8 @@ GNUNET_FRAGMENT_process_ack (struct GNUNET_FRAGMENT_Context *fc,
 
   /* all done */
   GNUNET_STATISTICS_update (fc->stats,
-                            _("# fragmentation transmissions completed"),
-                            1, GNUNET_NO);
+                            _("# fragmentation transmissions completed"), 1,
+                            GNUNET_NO);
   if (fc->task != GNUNET_SCHEDULER_NO_TASK)
   {
     GNUNET_SCHEDULER_cancel (fc->task);