- unique constraint
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_connectionpool.c
index 80d02011196efc82667298165ededb29fd1f4808..6d9a279727f2e818f3bee11fc707db542085d567 100644 (file)
@@ -21,7 +21,7 @@
 /**
  * @file testbed/gnunet-service-testbed_connectionpool.c
  * @brief connection pooling for connections to peers' services
- * @author Sree Harsha Totakura <sreeharsha@totakura.in> 
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
  */
 
 #include "gnunet-service-testbed.h"
@@ -214,6 +214,11 @@ struct GST_ConnectionPool_GetHandle
    * Did we call the pool_connection_ready_cb already?
    */
   int connection_ready_called;
+
+  /**
+   * Are we waiting for any peer connect notifications?
+   */
+  int notify_waiting;
 };
 
 
@@ -252,6 +257,15 @@ static struct PooledConnection *tail_not_pooled;
 static unsigned int max_size;
 
 
+/**
+ * Cancel the expiration task of the give #PooledConnection object
+ *
+ * @param entry the #PooledConnection object
+ */
+static void
+expire_task_cancel (struct PooledConnection *entry);
+
+
 /**
  * Destroy a #PooledConnection object
  *
@@ -264,14 +278,19 @@ destroy_pooled_connection (struct PooledConnection *entry)
   GNUNET_assert ((NULL == entry->head_waiting) && (NULL ==
                                                    entry->tail_waiting));
   GNUNET_assert (0 == entry->demand);
-  GNUNET_free_non_null (entry->peer_identity);
+  expire_task_cancel (entry);
   if (entry->in_lru)
     GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
   if (entry->in_pool)
-    GNUNET_assert (GNUNET_OK == 
+    GNUNET_assert (GNUNET_OK ==
                    GNUNET_CONTAINER_multihashmap32_remove (map,
                                                            entry->index,
                                                            entry));
+  if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
+  {
+    GNUNET_SCHEDULER_cancel (entry->notify_task);
+    entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   LOG_DEBUG ("Cleaning up handles of a pooled connection\n");
   if (NULL != entry->handle_transport)
     GNUNET_assert (NULL != entry->op_transport);
@@ -308,6 +327,11 @@ expire (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 }
 
 
+/**
+ * Cancel the expiration task of the give #PooledConnection object
+ *
+ * @param entry the #PooledConnection object
+ */
 static void
 expire_task_cancel (struct PooledConnection *entry)
 {
@@ -399,12 +423,16 @@ connection_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   if (NULL != gh->next)
     gh_next = search_waiting (entry, gh->next);
   GNUNET_CONTAINER_DLL_remove (entry->head_waiting, entry->tail_waiting, gh);
-  gh->connection_ready_called = GNUNET_YES;
+  gh->connection_ready_called = 1;
   if (NULL != gh_next)
     entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready, entry);
   if ( (NULL != gh->target) && (NULL != gh->connect_notify_cb) )
-    GNUNET_CONTAINER_DLL_insert_tail (entry->head_notify, entry->tail_notify, gh);
-  LOG_DEBUG ("Calling notify for handle type %u\n", gh->service);
+  {
+    GNUNET_CONTAINER_DLL_insert_tail (entry->head_notify, entry->tail_notify,
+                                      gh);
+    gh->notify_waiting = 1;
+  }
+  LOG_DEBUG ("Connection ready for handle type %u\n", gh->service);
   gh->cb (gh->cb_cls, entry->handle_core, entry->handle_transport,
           entry->peer_identity);
 }
@@ -412,7 +440,7 @@ connection_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 
 /**
  * Function called from peer connect notify callbacks from CORE and TRANSPORT
- * connections. This function calls the pendning peer connect notify callbacks
+ * connections. This function calls the pending peer connect notify callbacks
  * which are queued in an entry.
  *
  * @param cls the #PooledConnection object
@@ -448,6 +476,8 @@ peer_connect_notify_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
     cb_cls = gh->connect_notify_cb_cls;
     gh_next = gh->next;
     GNUNET_CONTAINER_DLL_remove (entry->head_notify, entry->tail_notify, gh);
+    gh->notify_waiting = 0;
+    LOG_DEBUG ("Peer connected to peer %u at service %u\n", entry->index, gh->service);
     gh = gh_next;
     cb (cb_cls, peer);
   }
@@ -466,7 +496,7 @@ transport_peer_connect_notify_cb (void *cls,
                                   const struct GNUNET_PeerIdentity *peer)
 {
   struct PooledConnection *entry = cls;
-  
+
   peer_connect_notify_cb (entry, peer, GST_CONNECTIONPOOL_SERVICE_TRANSPORT);
 }
 
@@ -532,7 +562,7 @@ static void
 core_peer_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
 {
   struct PooledConnection *entry = cls;
-  
+
   peer_connect_notify_cb (entry, peer, GST_CONNECTIONPOOL_SERVICE_CORE);
 }
 
@@ -550,7 +580,7 @@ core_peer_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
  * @param my_identity ID of this peer, NULL if we failed
  */
 static void
-core_startup_cb (void *cls, 
+core_startup_cb (void *cls,
                  const struct GNUNET_PeerIdentity *my_identity)
 {
   struct PooledConnection *entry = cls;
@@ -642,12 +672,8 @@ cleanup_iterator (void *cls,
                   void *value)
 {
   struct PooledConnection *entry = value;
-  
+
   GNUNET_assert (NULL != entry);
-  GNUNET_assert (GNUNET_OK == 
-                 GNUNET_CONTAINER_multihashmap32_remove (map, key, entry));
-  if (entry->in_lru)
-    GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
   destroy_pooled_connection (entry);
   return GNUNET_YES;
 }
@@ -678,7 +704,7 @@ void
 GST_connection_pool_destroy ()
 {
   struct PooledConnection *entry;
-  
+
   if (NULL != map)
   {
     GNUNET_assert (GNUNET_SYSERR !=
@@ -693,6 +719,7 @@ GST_connection_pool_destroy ()
     GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
     destroy_pooled_connection (entry);
   }
+  GNUNET_assert (NULL == head_not_pooled);
 }
 
 
@@ -707,7 +734,7 @@ GST_connection_pool_destroy ()
  *
  * @note @a connect_notify_cb will not be called if @a target is
  * already connected @a service level. Use
- * GNUNET_TRANSPORT_check_neighbour_connected() or a similar function from the
+ * GNUNET_TRANSPORT_check_peer_connected() or a similar function from the
  * respective @a service's API to check if the target peer is already connected or
  * not. @a connect_notify_cb will be called only once or never (in case @a target
  * cannot be connected or is already connected).
@@ -744,6 +771,7 @@ GST_connection_pool_get_handle (unsigned int peer_id,
   uint32_t peer_id32;
 
   peer_id32 = (uint32_t) peer_id;
+  handle = NULL;
   entry = NULL;
   if (NULL != map)
     entry = GNUNET_CONTAINER_multihashmap32_get (map, peer_id32);
@@ -776,7 +804,7 @@ GST_connection_pool_get_handle (unsigned int peer_id,
   {
     entry = GNUNET_new (struct PooledConnection);
     entry->index = peer_id32;
-    if ((NULL != map) 
+    if ((NULL != map)
         && (GNUNET_CONTAINER_multihashmap32_size (map) < max_size))
     {
       GNUNET_assert (GNUNET_OK ==
@@ -853,30 +881,48 @@ GST_connection_pool_get_handle_done (struct GST_ConnectionPool_GetHandle *gh)
   struct PooledConnection *entry;
 
   entry = gh->entry;
+  LOG_DEBUG ("Cleaning up get handle %p for service %u, peer %u\n",
+             gh,
+             gh->service, entry->index);
   if (!gh->connection_ready_called)
+  {
     GNUNET_CONTAINER_DLL_remove (entry->head_waiting, entry->tail_waiting, gh);
-  else if ((NULL != gh->next) || (NULL != gh->prev))
-    GNUNET_CONTAINER_DLL_remove (entry->head_notify, entry->head_notify, gh);
+    if ( (NULL == search_waiting (entry, entry->head_waiting))
+         && (GNUNET_SCHEDULER_NO_TASK != entry->notify_task) )
+    {
+      GNUNET_SCHEDULER_cancel (entry->notify_task);
+      entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
+    }
+  }
+  if (gh->notify_waiting)
+  {
+    GNUNET_CONTAINER_DLL_remove (entry->head_notify, entry->tail_notify, gh);
+    gh->notify_waiting = 0;
+  }
   GNUNET_free (gh);
   gh = NULL;
-  GNUNET_assert (!entry->in_lru);  
-  if ( (!entry->in_pool) && (NULL != map) )
+  GNUNET_assert (!entry->in_lru);
+  if (!entry->in_pool)
+    GNUNET_CONTAINER_DLL_remove (head_not_pooled, tail_not_pooled, entry);
+  if (NULL != map)
   {
     if (GNUNET_YES == GNUNET_CONTAINER_multihashmap32_contains (map,
                                                                 entry->index))
       goto unallocate;
-    if ((GNUNET_CONTAINER_multihashmap32_size (map) == max_size)
-        && (NULL == head_lru))
-      goto unallocate;
-    destroy_pooled_connection (head_lru);
-    GNUNET_CONTAINER_DLL_remove (head_not_pooled, tail_not_pooled, entry);
+    if (GNUNET_CONTAINER_multihashmap32_size (map) == max_size)
+    {
+      if (NULL == head_lru)
+        goto unallocate;
+      destroy_pooled_connection (head_lru);
+    }
     GNUNET_assert (GNUNET_OK ==
-                   GNUNET_CONTAINER_multihashmap32_put (map, 
-                                                        entry->index, 
+                   GNUNET_CONTAINER_multihashmap32_put (map,
+                                                        entry->index,
                                                         entry,
                                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     entry->in_pool = GNUNET_YES;
   }
+
  unallocate:
   GNUNET_assert (0 < entry->demand);
   entry->demand--;