-avoid side-effect in assertion
[oweals/gnunet.git] / src / testbed / testbed_api_testbed.c
index a7134d24ebd6db568bf44f571e2c140acb7df605..8e3a1073efa5054d78c40a9006ed4ec0c895628e 100644 (file)
 #define DEFAULT_SETUP_TIMEOUT 300
 
 /**
- * DLL of operations
+ * Testbed Run Handle
+ */
+struct RunContext;
+
+/**
+ * Context information for the operation we start
  */
-struct DLLOperation
+struct RunContextOperation
 {
   /**
    * The testbed operation handle
@@ -69,15 +74,6 @@ struct DLLOperation
    */
   void *cls;
 
-  /**
-   * The next pointer for DLL
-   */
-  struct DLLOperation *next;
-
-  /**
-   * The prev pointer for DLL
-   */
-  struct DLLOperation *prev;
 };
 
 
@@ -125,6 +121,28 @@ enum State
 };
 
 
+/**
+ * Context for host compability checks
+ */
+struct CompatibilityCheckContext
+{
+  /**
+   * The run context
+   */
+  struct RunContext *rc;
+
+  /**
+   * Handle for the compability check
+   */
+  struct GNUNET_TESTBED_HostHabitableCheckHandle *h;
+
+  /**
+   * Index of the host in the run context's hosts array
+   */
+  unsigned int index;
+};
+
+
 /**
  * Testbed Run Handle
  */
@@ -178,14 +196,9 @@ struct RunContext
   void *test_master_cls;
 
   /**
-   * The head element of DLL operations
+   * A hashmap for operations started by us
    */
-  struct DLLOperation *dll_op_head;
-
-  /**
-   * The tail element of DLL operations
-   */
-  struct DLLOperation *dll_op_tail;
+  struct GNUNET_CONTAINER_MultiHashMap32 *rcop_map;
 
   /**
    * An array of hosts loaded from the hostkeys file
@@ -193,9 +206,9 @@ struct RunContext
   struct GNUNET_TESTBED_Host **hosts;
 
   /**
-   * The handle for whether a host is habitable or not
+   * Array of compatibility check contexts
    */
-  struct GNUNET_TESTBED_HostHabitableCheckHandle **hc_handles;
+  struct CompatibilityCheckContext *hclist;
 
   /**
    * Array of peers which we create
@@ -308,6 +321,121 @@ struct RunContext
 };
 
 
+/**
+ * Return a 32-bit key from a pointer
+ *
+ * @param rcop the pointer
+ * @return 32-bit key
+ */
+static uint32_t
+rcop_key (void *rcop)
+{  
+  return * ((uint32_t *) &rcop);
+}
+
+
+/**
+ * Context information used for finding a pointer in the rcop_map
+ */
+struct SearchContext
+{
+  /**
+   * The operation pointer to look for
+   */
+  struct GNUNET_TESTBED_Operation *query;
+
+  /**
+   * The Run context operation which has the operation being queried
+   */
+  struct RunContextOperation *result;
+};
+
+
+/**
+ * Iterator for searching over the elements matching a given query
+ *
+ * @param cls the SearchContext
+ * @param key the 32-bit key
+ * @param value the RunContextOperation element
+ * @return GNUNET_YES to continue iteration; GNUNET_NO to cancel it
+ */
+static int
+search_iterator (void *cls, uint32_t key, void *value)
+{
+  struct RunContextOperation *rcop = value;
+  struct SearchContext *sc = cls;
+
+  GNUNET_assert (NULL != rcop);
+  if (sc->query == rcop->op)
+  {
+    GNUNET_assert (NULL == sc->result);
+    sc->result = rcop;
+    return GNUNET_NO;
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Initiate a search for the given operation in the rcop_map
+ *
+ * @param rc the RunContext whose rcop_map will be searched for the given
+ *          operation
+ * @param op the given operation to search for
+ * @return the matching RunContextOperation if found; NULL if not
+ */
+static struct RunContextOperation *
+search_rcop (struct RunContext *rc, struct GNUNET_TESTBED_Operation *op)
+{
+  struct SearchContext sc;
+  
+  sc.query = op;
+  sc.result = NULL;
+  if (GNUNET_SYSERR == 
+      GNUNET_CONTAINER_multihashmap32_get_multiple (rc->rcop_map,
+                                                    rcop_key (op),
+                                                    &search_iterator,
+                                                    &sc))
+  {
+    GNUNET_assert (NULL != sc.result);
+    return sc.result;
+  }
+  return NULL;
+}
+
+
+/**
+ * Insert an RunContextOperation into the rcop_map of the given RunContext
+ *
+ * @param rc the RunContext into whose map is to be used for insertion
+ * @param rcop the RunContextOperation to insert
+ */
+static void
+insert_rcop (struct RunContext *rc, struct RunContextOperation *rcop)
+{
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multihashmap32_put (rc->rcop_map,
+                                                      rcop_key (rcop->op), rcop,
+                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+}
+
+
+/**
+ * Remove a RunContextOperation from the rcop_map of the given RunContext
+ *
+ * @param rc the RunContext from whose map the given RunContextOperaton has to
+ *          be removed
+ * @param rcop the RunContextOperation
+ */
+static void
+remove_rcop (struct RunContext *rc, struct RunContextOperation *rcop)
+{
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap32_remove (rc->rcop_map,
+                                                         rcop_key (rcop->op),
+                                                         rcop));
+}
+
 /**
  * Assuming all peers have been destroyed cleanup run handle
  *
@@ -323,9 +451,10 @@ cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == rc->register_hosts_task);
   GNUNET_assert (NULL == rc->reg_handle);
   GNUNET_assert (NULL == rc->peers);
-  GNUNET_assert (NULL == rc->hc_handles);
+  GNUNET_assert (NULL == rc->hclist);
   GNUNET_assert (RC_PEERS_SHUTDOWN == rc->state);
-  GNUNET_assert (NULL == rc->dll_op_head);
+  GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (rc->rcop_map));
+  GNUNET_CONTAINER_multihashmap32_destroy (rc->rcop_map);
   if (NULL != rc->c)
     GNUNET_TESTBED_controller_disconnect (rc->c);
   if (NULL != rc->cproc)
@@ -342,19 +471,51 @@ cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GNUNET_free (rc);
 }
 
+
+/**
+ * Iterator for cleaning up elements from rcop_map 
+ *
+ * @param cls the RunContext
+ * @param key the 32-bit key
+ * @param value the RunContextOperation element
+ * @return always GNUNET_YES
+ */
+static int
+rcop_cleanup_iterator (void *cls, uint32_t key, void *value)
+{
+  struct RunContext *rc = cls;
+  struct RunContextOperation *rcop = value;
+
+  GNUNET_assert (rc == rcop->rc);
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Frees memory, closes pending operations, cancels actives tasks of the given
+ * RunContext 
+ *
+ * @param rc the RunContext
+ */
 static void
 cleanup (struct RunContext *rc)
 {
-  struct DLLOperation *dll_op;
+  struct CompatibilityCheckContext *hc;
   unsigned int nhost;
 
-  if (NULL != rc->hc_handles)
+  if (NULL != rc->hclist)
   {
     for (nhost = 0; nhost < rc->num_hosts; nhost++)
-      if (NULL != rc->hc_handles[nhost])
-        GNUNET_TESTBED_is_host_habitable_cancel (rc->hc_handles[nhost]);
-    GNUNET_free (rc->hc_handles);
-    rc->hc_handles = NULL;
+    {
+      hc = &rc->hclist[nhost];
+      if (NULL != hc->h)
+        GNUNET_TESTBED_is_host_habitable_cancel (hc->h);
+    }
+    GNUNET_free (rc->hclist);
+    rc->hclist = NULL;
   }
   /* Stop register hosts task if it is running */
   if (GNUNET_SCHEDULER_NO_TASK != rc->register_hosts_task)
@@ -383,12 +544,10 @@ cleanup (struct RunContext *rc)
     rc->topology_operation = NULL;
   }
   /* cancel any exiting operations */
-  while (NULL != (dll_op = rc->dll_op_head))
-  {
-    GNUNET_TESTBED_operation_done (dll_op->op);
-    GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-    GNUNET_free (dll_op);
-  }
+  GNUNET_assert (GNUNET_SYSERR != 
+                 GNUNET_CONTAINER_multihashmap32_iterate (rc->rcop_map,
+                                                          &rcop_cleanup_iterator,
+                                                          rc));
 }
 
 
@@ -402,7 +561,7 @@ static void
 shutdown_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
 
   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != rc->shutdown_run_task);
   rc->shutdown_run_task = GNUNET_SCHEDULER_NO_TASK;
@@ -413,13 +572,13 @@ shutdown_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   {
     if (NULL != rc->peers)
     {
-      dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-      dll_op->op = GNUNET_TESTBED_shutdown_peers (rc->c, dll_op, NULL, NULL);
-      GNUNET_assert (NULL != dll_op->op);
+      rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+      rcop->rc = rc;
+      rcop->op = GNUNET_TESTBED_shutdown_peers (rc->c, rcop, NULL, NULL);
+      GNUNET_assert (NULL != rcop->op);
       DEBUG ("Shutting down peers\n");
       rc->pstart_time = GNUNET_TIME_absolute_get ();
-      GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail,
-                                        dll_op);
+      insert_rcop (rc, rcop);
       return;
     }
   }
@@ -446,6 +605,12 @@ shutdown_now (struct RunContext *rc)
 }
 
 
+/**
+ * Task run upon any interrupt.  Common ones are SIGINT & SIGTERM.
+ *
+ * @param cls the RunContext which has to be acted upon
+ * @param tc the scheduler task context
+ */
 static void
 interrupt (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
@@ -483,18 +648,19 @@ static void
 start_peers_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
   unsigned int peer;
 
   DEBUG ("Starting Peers\n");
   rc->pstart_time = GNUNET_TIME_absolute_get ();
   for (peer = 0; peer < rc->num_peers; peer++)
   {
-    dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-    dll_op->op = GNUNET_TESTBED_peer_start (NULL, rc->peers[peer], NULL, NULL);
-    GNUNET_assert (NULL != dll_op->op);
-    dll_op->cls = rc->peers[peer];
-    GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op);
+    rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+    rcop->rc = rc;
+    rcop->op  = GNUNET_TESTBED_peer_start (NULL, rc->peers[peer], NULL, NULL);
+    GNUNET_assert (NULL != rcop->op);
+    rcop->cls = rc->peers[peer];
+    insert_rcop (rc, rcop);
   }
   rc->peer_count = 0;
 }
@@ -512,15 +678,14 @@ start_peers_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 static void
 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
 {
-  struct DLLOperation *dll_op = cls;
+  struct RunContextOperation *rcop = cls;
   struct RunContext *rc;
 
-  GNUNET_assert (NULL != dll_op);
-  rc = dll_op->rc;
-  GNUNET_assert (NULL != rc);
-  GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-  GNUNET_TESTBED_operation_done (dll_op->op);
-  GNUNET_free (dll_op);
+  GNUNET_assert (NULL != rcop);
+  GNUNET_assert (NULL != (rc = rcop->rc));
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
   if (NULL == peer)
   {
     if (NULL != emsg)
@@ -588,7 +753,7 @@ topology_completion_callback (void *cls, unsigned int nsuccess,
 static void
 create_peers (struct RunContext *rc)
 {
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
   unsigned int peer;
 
   DEBUG ("Creating peers\n");
@@ -599,16 +764,16 @@ create_peers (struct RunContext *rc)
   rc->peer_count = 0;
   for (peer = 0; peer < rc->num_peers; peer++)
   {
-    dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-    dll_op->rc = rc;
-    dll_op->op =
+    rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+    rcop->rc = rc;
+    rcop->op =
         GNUNET_TESTBED_peer_create (rc->c,
                                     (0 ==
                                      rc->num_hosts) ? rc->h : rc->hosts[peer %
                                                                         rc->num_hosts],
-                                    rc->cfg, peer_create_cb, dll_op);    
-    GNUNET_assert (NULL != dll_op->op);
-    GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail, dll_op);
+                                    rc->cfg, &peer_create_cb, rcop);
+    GNUNET_assert (NULL != rcop->op);
+    insert_rcop (rc, rcop);
   }
 }
 
@@ -624,14 +789,14 @@ static void
 event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
 
   if (RC_INIT == rc->state)
   {
     switch (event->type)
     {
     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
-      dll_op = event->op_cls;
+      rcop = event->op_cls;
       if (NULL != event->details.operation_finished.emsg)
       {
         LOG (GNUNET_ERROR_TYPE_ERROR, _("Linking controllers failed. Exiting"));
@@ -639,10 +804,10 @@ event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
       }
       else
         rc->reg_hosts++;
-      GNUNET_assert (event->op == dll_op->op);
-      GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-      GNUNET_TESTBED_operation_done (dll_op->op);
-      GNUNET_free (dll_op);
+      GNUNET_assert (event->op == rcop->op);
+      remove_rcop (rc, rcop);
+      GNUNET_TESTBED_operation_done (rcop->op);
+      GNUNET_free (rcop);
       if (rc->reg_hosts == rc->num_hosts)
       {
         rc->state = RC_LINKED;
@@ -655,17 +820,13 @@ event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
       return;
     }
   }
-  for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next)
-  {
-    if ((GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type) &&
-        (event->op == dll_op->op))
-      break;
-  }
-  if (NULL == dll_op)
+  if (GNUNET_TESTBED_ET_OPERATION_FINISHED != event->type)
     goto call_cc;
-  GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-  GNUNET_TESTBED_operation_done (dll_op->op);
-  GNUNET_free (dll_op);
+  if (NULL == (rcop = search_rcop (rc, event->op)))
+    goto call_cc;
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
   if ( (GNUNET_NO == rc->shutdown)
        && (NULL != event->details.operation_finished.emsg) )
   {
@@ -696,15 +857,11 @@ call_cc:
     rc->cc (rc->cc_cls, event);
   if (GNUNET_TESTBED_ET_PEER_START != event->type)
     return;
-  for (dll_op = rc->dll_op_head; NULL != dll_op; dll_op = dll_op->next)
-    if ((NULL != dll_op->cls) &&
-        (event->details.peer_start.peer == dll_op->cls))
-      break;
-  if (NULL == dll_op)           /* Not our operation */
+  if (NULL == (rcop = search_rcop (rc, event->op))) /* Not our operation */
     return;
-  GNUNET_CONTAINER_DLL_remove (rc->dll_op_head, rc->dll_op_tail, dll_op);
-  GNUNET_TESTBED_operation_done (dll_op->op);
-  GNUNET_free (dll_op);
+  remove_rcop (rc, rcop);
+  GNUNET_TESTBED_operation_done (rcop->op);
+  GNUNET_free (rcop);
   rc->peer_count++;
   if (rc->peer_count < rc->num_peers)
     return;
@@ -802,7 +959,7 @@ static void
 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct RunContext *rc = cls;
-  struct DLLOperation *dll_op;
+  struct RunContextOperation *rcop;
   unsigned int slave;
 
   rc->register_hosts_task = GNUNET_SCHEDULER_NO_TASK;
@@ -812,14 +969,13 @@ register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     /* Start slaves */
     for (slave = 0; slave < rc->num_hosts; slave++)
     {
-      dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
-      dll_op->rc = rc;
-      dll_op->op =
-          GNUNET_TESTBED_controller_link (dll_op, rc->c, rc->hosts[slave],
-                                          rc->h, rc->cfg, GNUNET_YES);
-      GNUNET_assert (NULL != dll_op->op);
-      GNUNET_CONTAINER_DLL_insert_tail (rc->dll_op_head, rc->dll_op_tail,
-                                        dll_op);
+      rcop = GNUNET_malloc (sizeof (struct RunContextOperation));
+      rcop->rc = rc;
+      rcop->op =
+          GNUNET_TESTBED_controller_link (rcop, rc->c, rc->hosts[slave],
+                                          rc->h, GNUNET_YES);
+      GNUNET_assert (NULL != rcop->op);
+      insert_rcop (rc, rcop);
     }
     rc->reg_hosts = 0;
     return;
@@ -939,17 +1095,16 @@ static void
 host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host,
                    int status)
 {
-  struct RunContext *rc = cls;
+  struct CompatibilityCheckContext *hc = cls;
+  struct RunContext *rc;
   struct GNUNET_TESTBED_Host **old_hosts;
   unsigned int nhost;
 
-  for (nhost = 0; nhost < rc->num_hosts; nhost++)
-  {
-    if (host == rc->hosts[nhost])
-      break;
-  }
-  GNUNET_assert (nhost != rc->num_hosts);
-  rc->hc_handles[nhost] = NULL;
+  GNUNET_assert (NULL != (rc = hc->rc));
+  nhost = hc->index;
+  GNUNET_assert (nhost <= rc->num_hosts);
+  GNUNET_assert (host == rc->hosts[nhost]);
+  hc->h = NULL;
   if (GNUNET_NO == status)
   {
     if ((NULL != host) && (NULL != GNUNET_TESTBED_host_get_hostname (host)))
@@ -964,8 +1119,8 @@ host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host,
   rc->reg_hosts++;
   if (rc->reg_hosts < rc->num_hosts)
     return;
-  GNUNET_free (rc->hc_handles);
-  rc->hc_handles = NULL;
+  GNUNET_free (rc->hclist);
+  rc->hclist = NULL;
   rc->h = rc->hosts[0];
   rc->num_hosts--;
   if (0 < rc->num_hosts)
@@ -982,6 +1137,9 @@ host_habitable_cb (void *cls, const struct GNUNET_TESTBED_Host *host,
     GNUNET_free (rc->hosts);
     rc->hosts = NULL;
   }
+  GNUNET_TESTBED_host_resolve_ (rc->h);
+  for (nhost = 0; nhost < rc->num_hosts; nhost++)
+    GNUNET_TESTBED_host_resolve_ (rc->hosts[nhost]);
   GNUNET_OS_network_interfaces_list (netint_proc, rc);
   if (NULL == rc->trusted_ip)
     rc->trusted_ip = GNUNET_strdup ("127.0.0.1");
@@ -1055,6 +1213,7 @@ GNUNET_TESTBED_run (const char *host_filename,
 {
   struct RunContext *rc;
   char *topology;
+  struct CompatibilityCheckContext *hc;      
   struct GNUNET_TIME_Relative timeout;
   unsigned long long random_links;
   unsigned int hid;
@@ -1153,22 +1312,26 @@ GNUNET_TESTBED_run (const char *host_filename,
   }
   if (0 != rc->num_hosts)
   {
-    rc->hc_handles =
-        GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostHabitableCheckHandle *)
-                       * rc->num_hosts);
+    rc->hclist = GNUNET_malloc (sizeof (struct CompatibilityCheckContext)
+                                * rc->num_hosts);
     for (nhost = 0; nhost < rc->num_hosts; nhost++)
     {
-      if (NULL ==
-          (rc->hc_handles[nhost] =
-           GNUNET_TESTBED_is_host_habitable (rc->hosts[nhost], rc->cfg,
-                                             &host_habitable_cb, rc)))
+      hc = &rc->hclist[nhost];
+      hc->index = nhost;
+      hc->rc = rc;
+      hc->h = GNUNET_TESTBED_is_host_habitable (rc->hosts[nhost], rc->cfg,
+                                                &host_habitable_cb, hc);
+      if (NULL == hc->h)
       {
         GNUNET_break (0);
         for (nhost = 0; nhost < rc->num_hosts; nhost++)
-          if (NULL != rc->hc_handles[nhost])
-            GNUNET_TESTBED_is_host_habitable_cancel (rc->hc_handles[nhost]);
-        GNUNET_free (rc->hc_handles);
-        rc->hc_handles = NULL;
+        {
+          hc = &rc->hclist[nhost];
+          if (NULL != hc->h)
+            GNUNET_TESTBED_is_host_habitable_cancel (hc->h);
+        }
+        GNUNET_free (rc->hclist);
+        rc->hclist = NULL;
         goto error_cleanup;
       }
     }
@@ -1184,6 +1347,7 @@ GNUNET_TESTBED_run (const char *host_filename,
     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
                                              DEFAULT_SETUP_TIMEOUT);
   }
+  rc->rcop_map = GNUNET_CONTAINER_multihashmap32_create (256);
   rc->timeout_task =
       GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task, rc);
   rc->interrupt_task =