- added benchmarking for updates
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses_mlp.c
index 61aa73397b3b140b7617a564db2b098873eef31b..512d5fb3959d9a7e9ff26ed64b0598d9496683fc 100644 (file)
@@ -806,14 +806,16 @@ mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHas
   return res;
 }
 
+
 /**
  * Solves the LP problem
  *
  * @param mlp the MLP Handle
+ * @param s_ctx context to return results
  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
  */
 static int
-mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
+mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *s_ctx)
 {
   int res;
   struct GNUNET_TIME_Relative duration;
@@ -867,13 +869,13 @@ lp_solv:
   duration = GNUNET_TIME_absolute_get_difference (start, end);
   mlp->lp_solved++;
   mlp->lp_total_duration =+ duration.rel_value;
+  s_ctx->lp_duration = duration;
 
   GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
-  GNUNET_STATISTICS_set (mlp->stats,"# LP execution time", duration.rel_value, GNUNET_NO);
-  GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average",
+  GNUNET_STATISTICS_set (mlp->stats,"# LP execution time (ms)", duration.rel_value, GNUNET_NO);
+  GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average (ms)",
                          mlp->lp_total_duration / mlp->lp_solved,  GNUNET_NO);
 
-
   /* Analyze problem status  */
   res = glp_get_status (mlp->prob);
   switch (res) {
@@ -903,10 +905,11 @@ lp_solv:
  * Solves the MLP problem
  *
  * @param mlp the MLP Handle
+ * @param s_ctx context to return results
  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
  */
 int
-mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
+mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *s_ctx)
 {
   int res;
   struct GNUNET_TIME_Relative duration;
@@ -943,10 +946,11 @@ mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
   duration = GNUNET_TIME_absolute_get_difference (start, end);
   mlp->mlp_solved++;
   mlp->mlp_total_duration =+ duration.rel_value;
+  s_ctx->mlp_duration = duration;
 
   GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
-  GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time", duration.rel_value, GNUNET_NO);
-  GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average",
+  GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time (ms)", duration.rel_value, GNUNET_NO);
+  GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average (ms)",
                          mlp->mlp_total_duration / mlp->mlp_solved,  GNUNET_NO);
 
   /* Analyze problem status  */
@@ -970,23 +974,24 @@ mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
   return GNUNET_OK;
 }
 
-int GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp);
+int GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *ctx);
+
 
 static void
 mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GAS_MLP_Handle *mlp = cls;
+  struct GAS_MLP_SolutionContext ctx;
 
   mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
 
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
 
-
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
 
   if (mlp->addr_in_problem != 0)
-    GAS_mlp_solve_problem(mlp);
+    GAS_mlp_solve_problem(mlp, &ctx);
 }
 
 
@@ -997,14 +1002,30 @@ mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
  */
 int
-GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp)
+GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *ctx)
 {
   int res;
-  mlp->last_execution = GNUNET_TIME_absolute_get ();
+  /* Check if solving is already running */
+  if (GNUNET_YES == mlp->semaphore)
+  {
+    if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
+    {
+      GNUNET_SCHEDULER_cancel(mlp->mlp_task);
+      mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
+    }
+    mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
+    return GNUNET_SYSERR;
+  }
+  mlp->semaphore = GNUNET_YES;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solving\n");
+  mlp->last_execution = GNUNET_TIME_absolute_get ();
 
+  ctx->lp_result = GNUNET_SYSERR;
+  ctx->mlp_result = GNUNET_SYSERR;
+  ctx->lp_duration = GNUNET_TIME_relative_get_forever();
+  ctx->mlp_duration = GNUNET_TIME_relative_get_forever();
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solve LP problem\n");
 #if WRITE_MLP
   char * name;
   static int i;
@@ -1014,7 +1035,14 @@ GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp)
   GNUNET_free (name);
 # endif
 
-  res = mlp_solve_lp_problem (mlp);
+  res = mlp_solve_lp_problem (mlp, ctx);
+  ctx->lp_result = res;
+  if (res != GNUNET_OK)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "LP Problem solving failed\n");
+    mlp->semaphore = GNUNET_NO;
+    return GNUNET_SYSERR;
+  }
 
 #if WRITE_MLP
   GNUNET_asprintf(&name, "problem_%i_lp_solution", i);
@@ -1022,31 +1050,24 @@ GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp)
   GNUNET_free (name);
 # endif
 
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solve MLP problem\n");
+  res = mlp_solve_mlp_problem (mlp, ctx);
+  ctx->mlp_result = res;
   if (res != GNUNET_OK)
   {
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "LP Problem solving failed\n");
-
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP Problem solving failed\n");
+    mlp->semaphore = GNUNET_NO;
     return GNUNET_SYSERR;
   }
-
-  res = mlp_solve_mlp_problem (mlp);
-
 #if WRITE_MLP
   GNUNET_asprintf(&name, "problem_%i_mlp_solution", i);
   glp_print_mip (mlp->prob, name);
   GNUNET_free (name);
 # endif
-  if (res != GNUNET_OK)
-  {
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP Problem solving failed\n");
-
-    return GNUNET_SYSERR;
-  }
 
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved %s (LP duration %llu / MLP duration %llu)\n",
+      (GNUNET_OK == res) ? "successfully" : "failed", ctx->lp_duration.rel_value, ctx->mlp_duration.rel_value);
   /* Process result */
   struct ATS_Peer *p = NULL;
   struct ATS_Address *a = NULL;
@@ -1082,6 +1103,7 @@ GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp)
     mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
   }
   mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
+  mlp->semaphore = GNUNET_NO;
   return res;
 }
 
@@ -1110,15 +1132,41 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
   unsigned int n_min;
   struct GNUNET_TIME_Relative i_exec;
   int c;
+  char * quota_out_str;
+  char * quota_in_str;
 
   /* Init GLPK environment */
-  GNUNET_assert (glp_init_env() == 0);
+  int res = glp_init_env();
+  switch (res) {
+    case 0:
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
+          "initialization successful");
+      break;
+    case 1:
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
+          "environment is already initialized");
+      break;
+    case 2:
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
+          "initialization failed (insufficient memory)");
+      GNUNET_free(mlp);
+      return NULL;
+      break;
+    case 3:
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
+          "initialization failed (unsupported programming model)");
+      GNUNET_free(mlp);
+      return NULL;
+      break;
+    default:
+      break;
+  }
 
   /* Create initial MLP problem */
   mlp->prob = glp_create_prob();
   GNUNET_assert (mlp->prob != NULL);
 
-  mlp->BIG_M = (double) (UINT32_MAX) /10;
+  mlp->BIG_M = (double) BIG_M_VALUE;
 
   /* Get diversity coefficient from configuration */
   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
@@ -1231,37 +1279,57 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
     if ((entry_in == NULL) || (entry_out == NULL))
       continue;
 
-    if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", entry_out, &quota_out))
+    if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_out, &quota_out_str))
+    {
+      if (0 == strcmp(quota_out_str, BIG_M_STRING) ||
+          (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str, &quota_out)))
+        quota_out = mlp->BIG_M;
+
+      GNUNET_free (quota_out_str);
+      quota_out_str = NULL;
+    }
+    else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
+    {
+      quota_out = mlp->BIG_M;
+    }
+    else
     {
       quota_out = mlp->BIG_M;
     }
-    if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", entry_in, &quota_in))
+
+    if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_in, &quota_in_str))
+    {
+      if (0 == strcmp(quota_in_str, BIG_M_STRING) ||
+          (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, &quota_in)))
+        quota_in = mlp->BIG_M;
+
+      GNUNET_free (quota_in_str);
+      quota_in_str = NULL;
+    }
+    else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
+    {
+      quota_in = mlp->BIG_M;
+    }
+    else
     {
       quota_in = mlp->BIG_M;
     }
+
     /* Check if defined quota could make problem unsolvable */
-    if ((n_min * b_min) > quota_out)
+    if (((n_min * b_min) > quota_out) && (GNUNET_ATS_NET_UNSPECIFIED != quotas[c]))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Inconsistent quota configuration value `%s': " \
           "outbound quota (%u Bps) too small for combination of minimum connections and minimum bandwidth per peer (%u * %u Bps = %u)\n", entry_out, quota_out, n_min, b_min, n_min * b_min);
-      unsigned int default_min = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
-      if ((quota_out / n_min) > default_min)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,  "Reducing minimum bandwidth per peer to %u Bps\n",
-            (quota_out / n_min));
-        b_min = (quota_out / n_min);
-      }
-      else
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,  "Reducing minimum bandwidth per peer to %u Bps and minimum connections to %u \n",
-            default_min, (quota_out / default_min));
-        b_min = default_min;
-        n_min = (quota_out / default_min);
-      }
+
+      GAS_mlp_done(mlp);
+      mlp = NULL;
+      return NULL;
     }
 
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found `%s' quota %llu and `%s' quota %llu\n",
                 entry_out, quota_out, entry_in, quota_in);
+    GNUNET_STATISTICS_update ((struct GNUNET_STATISTICS_Handle *) stats, entry_out, quota_out, GNUNET_NO);
+    GNUNET_STATISTICS_update ((struct GNUNET_STATISTICS_Handle *) stats, entry_in, quota_in, GNUNET_NO);
     mlp->quota_out[c] = quota_out;
     mlp->quota_in[c] = quota_in;
   }
@@ -1310,7 +1378,7 @@ GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
   mlp->b_min = b_min;
   mlp->n_min = n_min;
   mlp->m_q = GNUNET_ATS_QualityPropertiesCount;
-
+  mlp->semaphore = GNUNET_NO;
   return mlp;
 }
 
@@ -1320,12 +1388,15 @@ update_quality (struct GAS_MLP_Handle *mlp, struct ATS_Address * address)
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating quality metrics for peer `%s'\n",
       GNUNET_i2s (&address->peer));
 
+  GNUNET_assert (NULL != address);
+  GNUNET_assert (NULL != address->mlp_information);
+  GNUNET_assert (NULL != address->ats);
+
   struct MLP_information *mlpi = address->mlp_information;
   struct GNUNET_ATS_Information *ats = address->ats;
   GNUNET_assert (mlpi != NULL);
 
   int c;
-
   for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
   {
     int index = mlp_lookup_ats(address, mlp->q[c]);
@@ -1374,7 +1445,7 @@ update_quality (struct GAS_MLP_Handle *mlp, struct ATS_Address * address)
             c3 ++;
           }
         }
-        if (c3 > 0)
+        if ((c3 > 0) && (avg > 0))
           /* avg = 1 / ((q[0] + ... + q[l]) /c3) => c3 / avg*/
           mlpi->q_averaged[c] = (double) c3 / avg;
         else
@@ -1399,7 +1470,7 @@ update_quality (struct GAS_MLP_Handle *mlp, struct ATS_Address * address)
             c3 ++;
           }
         }
-        if (c3 > 0)
+        if ((c3 > 0) && (avg > 0))
           /* avg = 1 / ((q[0] + ... + q[l]) /c3) => c3 / avg*/
           mlpi->q_averaged[c] = (double) c3 / avg;
         else
@@ -1487,8 +1558,9 @@ GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
 {
   int new;
   struct MLP_information *mlpi;
+  struct GAS_MLP_SolutionContext ctx;
 
-  GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
+  GNUNET_STATISTICS_update (mlp->stats, "# MLP address updates", 1, GNUNET_NO);
 
   /* We add a new address */
   if (address->mlp_information == NULL)
@@ -1514,6 +1586,7 @@ GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
 
     address->mlp_information = mlpi;
     mlp->addr_in_problem ++;
+    GNUNET_STATISTICS_update (mlp->stats, "# addresses in MLP", 1, GNUNET_NO);
 
     /* Check for and add peer */
     struct ATS_Peer *peer = mlp_find_peer (mlp, &address->peer);
@@ -1540,6 +1613,7 @@ GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
       GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
       GNUNET_CONTAINER_DLL_insert (mlp->peer_head, mlp->peer_tail, peer);
       mlp->c_p ++;
+      GNUNET_STATISTICS_update (mlp->stats, "# peers in MLP", 1, GNUNET_NO);
     }
     else
     {
@@ -1549,7 +1623,6 @@ GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
 
       GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
     }
-
     update_quality (mlp, address);
   }
   else
@@ -1570,7 +1643,7 @@ GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
     mlp->presolver_required = GNUNET_YES;
   }
   if (mlp->auto_solve == GNUNET_YES)
-    GAS_mlp_solve_problem (mlp);
+    GAS_mlp_solve_problem (mlp, &ctx);
 }
 
 /**
@@ -1587,6 +1660,7 @@ void
 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
 {
   GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
+  struct GAS_MLP_SolutionContext ctx;
 
   /* Free resources */
   if (address->mlp_information != NULL)
@@ -1595,6 +1669,7 @@ GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
     address->mlp_information = NULL;
 
     mlp->addr_in_problem --;
+    GNUNET_STATISTICS_update (mlp->stats, "# addresses in MLP", -1, GNUNET_NO);
   }
 
   /* Remove from peer list */
@@ -1613,6 +1688,7 @@ GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
     GNUNET_CONTAINER_DLL_remove (mlp->peer_head, mlp->peer_tail, head);
     GNUNET_free (head);
     mlp->c_p --;
+    GNUNET_STATISTICS_update (mlp->stats, "# peers in MLP", -1, GNUNET_NO);
   }
 
   /* Update problem */
@@ -1626,7 +1702,7 @@ GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_Mult
     /* Recalculate */
     mlp->presolver_required = GNUNET_YES;
     if (mlp->auto_solve == GNUNET_YES)
-      GAS_mlp_solve_problem (mlp);
+      GAS_mlp_solve_problem (mlp, &ctx);
   }
 }
 
@@ -1705,7 +1781,7 @@ void
 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
 {
   struct ATS_Peer * peer;
-  struct ATS_Peer * tmp;
+  struct ATS_Address *addr;
 
   GNUNET_assert (mlp != NULL);
 
@@ -1719,10 +1795,16 @@ GAS_mlp_done (struct GAS_MLP_Handle *mlp)
   peer = mlp->peer_head;
   while (peer != NULL)
   {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up peer `%s'\n", GNUNET_i2s (&peer->id));
     GNUNET_CONTAINER_DLL_remove(mlp->peer_head, mlp->peer_tail, peer);
-    tmp = peer->next;
+    for (addr = peer->head; NULL != addr; addr = peer->head)
+    {
+      GNUNET_CONTAINER_DLL_remove(peer->head, peer->tail, addr);
+      GNUNET_free (addr->mlp_information);
+      addr->mlp_information = NULL;
+    }
     GNUNET_free (peer);
-    peer = tmp;
+    peer = mlp->peer_head;
   }
   mlp_delete_problem (mlp);