gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r29623 - gnunet/src/testbed


From: gnunet
Subject: [GNUnet-SVN] r29623 - gnunet/src/testbed
Date: Thu, 26 Sep 2013 17:03:13 +0200

Author: harsha
Date: 2013-09-26 17:03:13 +0200 (Thu, 26 Sep 2013)
New Revision: 29623

Added:
   gnunet/src/testbed/gnunet-service-testbed_connectionpool.c
   gnunet/src/testbed/gnunet-service-testbed_connectionpool.h
Modified:
   gnunet/src/testbed/Makefile.am
   gnunet/src/testbed/gnunet-daemon-testbed-blacklist.c
   gnunet/src/testbed/gnunet-service-test-barriers.c
   gnunet/src/testbed/gnunet-service-testbed.c
Log:
- towards seperating connection pooling from cache


Modified: gnunet/src/testbed/Makefile.am
===================================================================
--- gnunet/src/testbed/Makefile.am      2013-09-26 14:54:54 UTC (rev 29622)
+++ gnunet/src/testbed/Makefile.am      2013-09-26 15:03:13 UTC (rev 29623)
@@ -42,7 +42,9 @@
   gnunet-service-testbed_meminfo.c \
   gnunet-service-testbed_meminfo.h \
   gnunet-service-testbed_barriers.c \
-  gnunet-service-testbed_barriers.h
+  gnunet-service-testbed_barriers.h \
+  gnunet-service-testbed_connectionpool.c \
+  gnunet-service-testbed_connectionpool.h
 gnunet_service_testbed_LDADD = $(XLIB) \
  $(top_builddir)/src/util/libgnunetutil.la \
  $(top_builddir)/src/core/libgnunetcore.la \

Modified: gnunet/src/testbed/gnunet-daemon-testbed-blacklist.c
===================================================================
--- gnunet/src/testbed/gnunet-daemon-testbed-blacklist.c        2013-09-26 
14:54:54 UTC (rev 29622)
+++ gnunet/src/testbed/gnunet-daemon-testbed-blacklist.c        2013-09-26 
15:03:13 UTC (rev 29623)
@@ -20,7 +20,7 @@
 
 
 /**
- * @file testbed/gnunet-daemon-testbed-blacklist
+ * @file testbed/gnunet-daemon-testbed-blacklist.c
  * @brief daemon to restrict incoming connections from other peers at the
  *          transport layer of a peer 
  * @author Sree Harsha Totakura <address@hidden> 

Modified: gnunet/src/testbed/gnunet-service-test-barriers.c
===================================================================
--- gnunet/src/testbed/gnunet-service-test-barriers.c   2013-09-26 14:54:54 UTC 
(rev 29622)
+++ gnunet/src/testbed/gnunet-service-test-barriers.c   2013-09-26 15:03:13 UTC 
(rev 29623)
@@ -99,7 +99,7 @@
  * @param cls NULL
  * @param args arguments passed to GNUNET_PROGRAM_run
  * @param cfgfile the path to configuration file
- * @param cfg the configuration file handle
+ * @param config the configuration file handle
  */
 static void
 run (void *cls, char *const *args, const char *cfgfile,

Modified: gnunet/src/testbed/gnunet-service-testbed.c
===================================================================
--- gnunet/src/testbed/gnunet-service-testbed.c 2013-09-26 14:54:54 UTC (rev 
29622)
+++ gnunet/src/testbed/gnunet-service-testbed.c 2013-09-26 15:03:13 UTC (rev 
29623)
@@ -26,6 +26,7 @@
 
 #include "gnunet-service-testbed.h"
 #include "gnunet-service-testbed_barriers.h"
+#include "gnunet-service-testbed_connectionpool.h"
 
 /***********/
 /* Globals */
@@ -826,6 +827,7 @@
   GNUNET_free_non_null (hostname);
   /* Free hello cache */
   GST_cache_clear ();
+  GST_connection_pool_destroy ();
   GNUNET_TESTBED_operation_queue_destroy_ (GST_opq_openfds);
   GST_opq_openfds = NULL;
   GST_stats_destroy ();
@@ -922,6 +924,7 @@
                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
                                                         "CACHE_SIZE", &num));
   GST_cache_init ((unsigned int) num);
+  GST_connection_pool_init ((unsigned int) num);
   GNUNET_assert (GNUNET_OK ==
                  GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
                                                         "MAX_OPEN_FDS", &num));

Added: gnunet/src/testbed/gnunet-service-testbed_connectionpool.c
===================================================================
--- gnunet/src/testbed/gnunet-service-testbed_connectionpool.c                  
        (rev 0)
+++ gnunet/src/testbed/gnunet-service-testbed_connectionpool.c  2013-09-26 
15:03:13 UTC (rev 29623)
@@ -0,0 +1,891 @@
+/*
+  This file is part of GNUnet.
+  (C) 2008--2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file testbed/gnunet-service-testbed_connectionpool.c
+ * @brief connection pooling for connections to peers' services
+ * @author Sree Harsha Totakura <address@hidden> 
+ */
+
+#include "gnunet-service-testbed.h"
+#include "gnunet-service-testbed_connectionpool.h"
+#include "testbed_api_operations.h"
+
+/**
+ * Redefine LOG with a changed log component string
+ */
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(kind,...)                                   \
+  GNUNET_log_from (kind, "testbed-connectionpool", __VA_ARGS__)
+
+
+/**
+ * Time to expire a cache entry
+ */
+#define CACHE_EXPIRY                            \
+  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
+
+
+/**
+ * The request handle for obtaining a pooled connection
+ */
+struct GST_ConnectionPool_GetHandle;
+
+
+/**
+ * A pooled connection
+ */
+struct PooledConnection
+{
+  /**
+   * Next ptr for placing this object in the DLL of least recently used pooled
+   * connections
+   */
+  struct PooledConnection *next;
+
+  /**
+   * Prev ptr for placing this object in the DLL of the least recently used
+   * pooled connections
+   */
+  struct PooledConnection *prev;
+
+  /**
+   * The transport handle to the peer corresponding to this entry; can be NULL
+   */
+  struct GNUNET_TRANSPORT_Handle *handle_transport;
+
+  /**
+   * The core handle to the peer corresponding to this entry; can be NULL
+   */
+  struct GNUNET_CORE_Handle *handle_core;
+
+  /**
+   * The operation handle for transport handle
+   */
+  struct GNUNET_TESTBED_Operation *op_transport;
+
+  /**
+   * The operation handle for core handle
+   */
+  struct GNUNET_TESTBED_Operation *op_core;
+
+  /**
+   * The peer identity of this peer. Will be set upon opening a connection to
+   * the peers CORE service. Will be NULL until then and after the CORE
+   * connection is closed
+   */
+  struct GNUNET_PeerIdentity *peer_identity;
+
+  /**
+   * The configuration of the peer. Should be not NULL as long as the 
core_handle
+   * or transport_handle are valid
+   */
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * DLL head for the queue to serve notifications when a peer is connected
+   */
+  struct GST_ConnectionPool_GetHandle *head_notify;
+
+  /**
+   * DLL tail for the queue to serve notifications when a peer is connected
+   */
+  struct GST_ConnectionPool_GetHandle *tail_notify;
+
+  /**
+   * DLL head for the queue of #GST_ConnectionPool_GetHandle requests that are
+   * waiting for this connection to be opened
+   */
+  struct GST_ConnectionPool_GetHandle *head_waiting;
+
+  /**
+   * DLL tail for the queue of #GST_ConnectionPool_GetHandle requests that are
+   * waiting for this connection to be opened
+   */
+  struct GST_ConnectionPool_GetHandle *tail_waiting;
+
+  /**
+   * The task to expire this connection from the connection pool
+   */
+  GNUNET_SCHEDULER_TaskIdentifier expire_task;
+
+  /**
+   * The task to notify a waiting #GST_ConnectionPool_GetHandle object
+   */
+  GNUNET_SCHEDULER_TaskIdentifier notify_task;
+
+  /**
+   * Number of active requests using this pooled connection
+   */
+  unsigned int demand;
+
+  /**
+   * Is this entry in LRU
+   */
+  int in_lru;
+
+  /**
+   * Is this entry present in the connection pool
+   */
+  int in_pool;
+
+  /**
+   * The index of this peer
+   */
+  uint32_t index;
+};
+
+
+/**
+ * The request handle for obtaining a pooled connection
+ */
+struct GST_ConnectionPool_GetHandle
+{
+  /**
+   * The next ptr for inclusion in the notification DLLs.  At first the object
+   * is placed in the waiting DLL of the corresponding #PooledConnection
+   * object.  After the handle is opened it is moved to the notification DLL if
+   * @p connect_notify_cb and @p target are not NULL
+   */
+  struct GST_ConnectionPool_GetHandle *next;
+
+  /**
+   * The prev ptr for inclusion in the notification DLLs
+   */
+  struct GST_ConnectionPool_GetHandle *prev;
+
+  /**
+   * The pooled connection object this handle corresponds to
+   */
+  struct PooledConnection *entry;
+
+  /**
+   * The cache callback to call when a handle is available
+   */
+  GST_connection_pool_connection_ready_cb cb;
+
+  /**
+   * The closure for the above callback
+   */
+  void *cb_cls;
+
+  /**
+   * The peer identity of the target peer. When this target peer is connected,
+   * call the notify callback
+   */
+  const struct GNUNET_PeerIdentity *target;
+
+  /**
+   * The callback to be called for serving notification that the target peer is
+   * connected
+   */
+  GST_connection_pool_peer_connect_notify connect_notify_cb;
+
+  /**
+   * The closure for the notify callback
+   */
+  void *connect_notify_cb_cls;
+
+  /**
+   * The service we want to connect to
+   */
+  enum GST_ConnectionPool_Service service;
+
+  /**
+   * Did we call the pool_connection_ready_cb already?
+   */
+  int connection_ready_called;
+};
+
+
+/**
+ * A hashmap for quickly finding connections in the connection pool
+ */
+static struct GNUNET_CONTAINER_MultiHashMap32 *map;
+
+/**
+ * DLL head for maitaining the least recently used #PooledConnection objects.
+ * The head is the least recently used object.
+ */
+static struct PooledConnection *head_lru;
+
+/**
+ * DLL tail for maitaining the least recently used #PooledConnection objects
+ */
+static struct PooledConnection *tail_lru;
+
+/**
+ * DLL head for maintaining #PooledConnection objects that are not added into
+ * the connection pool as it was full at the time the object's creation
+ * FIXME
+ */
+static struct PooledConnection *head_not_pooled;
+
+/**
+ * DLL tail for maintaining #PooledConnection objects that are not added into
+ * the connection pool as it was full at the time the object's creation
+ */
+static struct PooledConnection *tail_not_pooled;
+
+/**
+ * The maximum number of entries that can be present in the connection pool
+ */
+static unsigned int max_size;
+
+
+/**
+ * Destroy a #PooledConnection object
+ *
+ * @param entry the #PooledConnection object
+ */
+static void
+destroy_pooled_connection (struct PooledConnection *entry)
+{
+  GNUNET_assert ((NULL == entry->head_notify) && (NULL == entry->tail_notify));
+  GNUNET_assert ((NULL == entry->head_waiting) && (NULL ==
+                                                   entry->tail_waiting));
+  GNUNET_assert (0 == entry->demand);
+  GNUNET_free_non_null (entry->peer_identity);
+  if (entry->in_lru)
+    GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
+  if (entry->in_pool)
+    GNUNET_assert (GNUNET_OK == 
+                   GNUNET_CONTAINER_multihashmap32_remove (map,
+                                                           entry->index,
+                                                           entry));
+  LOG_DEBUG ("Cleaning up handles of a pooled connection\n");
+  if (NULL != entry->handle_transport)
+    GNUNET_assert (NULL != entry->op_transport);
+  if (NULL != entry->op_transport)
+  {
+    GNUNET_TESTBED_operation_done (entry->op_transport);
+    entry->op_transport = NULL;
+  }
+  if (NULL != entry->op_core)
+  {
+    GNUNET_TESTBED_operation_done (entry->op_core);
+    entry->op_core = NULL;
+  }
+  GNUNET_assert (NULL == entry->handle_core);
+  GNUNET_assert (NULL == entry->handle_transport);
+  GNUNET_CONFIGURATION_destroy (entry->cfg);
+  GNUNET_free (entry);
+}
+
+
+/**
+ * Expire a #PooledConnection object
+ *
+ * @param cls the #PooledConnection object
+ * @param tc scheduler task context
+ */
+static void
+expire (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct PooledConnection *entry = cls;
+
+  entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
+  destroy_pooled_connection (entry);
+}
+
+
+static void
+expire_task_cancel (struct PooledConnection *entry)
+{
+  if (GNUNET_SCHEDULER_NO_TASK != entry->expire_task)
+  {
+    GNUNET_SCHEDULER_cancel (entry->expire_task);
+    entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+}
+
+
+/**
+ * Function to add a #PooledConnection object into LRU and begin the expiry 
task
+ *
+ * @param entry the #PooledConnection object
+ */
+static void
+add_to_lru (struct PooledConnection *entry)
+{
+  GNUNET_assert (0 == entry->demand);
+  GNUNET_assert (!entry->in_lru);
+  GNUNET_CONTAINER_DLL_insert_tail (head_lru, tail_lru, entry);
+  entry->in_lru = GNUNET_YES;
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->expire_task);
+  entry->expire_task = GNUNET_SCHEDULER_add_delayed (CACHE_EXPIRY,
+                                                     &expire, entry);
+}
+
+
+/**
+ * Function to find a #GST_ConnectionPool_GetHandle which is waiting for one of
+ * the handles in given entry which are now available.
+ *
+ * @param entry the pooled connection whose active list has to be searched
+ * @param head the starting list element in the GSTCacheGetHandle where the
+ *          search has to be begin
+ * @return a suitable GSTCacheGetHandle whose handle ready notify callback
+ *           hasn't been called yet. NULL if no such suitable GSTCacheGetHandle
+ *           is found
+ */
+static struct GST_ConnectionPool_GetHandle *
+search_waiting (const struct PooledConnection *entry,
+                struct GST_ConnectionPool_GetHandle *head)
+{
+  struct GST_ConnectionPool_GetHandle *gh;
+
+  for (gh = head; NULL != gh; gh = gh->next)
+  {
+    switch (gh->service)
+    {
+    case GST_CONNECTIONPOOL_SERVICE_CORE:
+      if (NULL == entry->handle_core)
+        continue;
+      if (NULL == entry->peer_identity)
+        continue;               /* CORE connection isn't ready yet */
+      break;
+    case GST_CONNECTIONPOOL_SERVICE_TRANSPORT:
+      if (NULL == entry->handle_transport)
+        continue;
+      break;
+    }
+    break;
+  }
+  return gh;
+}
+
+
+/**
+ * A handle in the #PooledConnection object pointed by @a cls is ready and 
there
+ * is a #GST_ConnectionPool_GetHandle object waiting in the waiting list.  This
+ * function retrieves that object and calls the handle ready callback.  It
+ * further schedules itself if there are similar waiting objects which can be 
notified.
+ *
+ * @param cls the #PooledConnection object
+ * @param tc the task context from scheduler
+ */
+static void
+connection_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct PooledConnection *entry = cls;
+  struct GST_ConnectionPool_GetHandle *gh;
+  struct GST_ConnectionPool_GetHandle *gh_next;
+
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->notify_task);
+  entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
+  gh = search_waiting (entry, entry->head_waiting);
+  GNUNET_assert (NULL != gh);
+  gh_next = NULL;
+  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;
+  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);
+  gh->cb (gh->cb_cls, entry->handle_core, entry->handle_transport,
+          entry->peer_identity);
+}
+
+
+/**
+ * Function called from peer connect notify callbacks from CORE and TRANSPORT
+ * connections. This function calls the pendning peer connect notify callbacks
+ * which are queued in an entry.
+ *
+ * @param cls the #PooledConnection object
+ * @param peer the peer that connected
+ * @param service the service where this notification has originated
+ */
+static void
+peer_connect_notify_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
+                        const enum GST_ConnectionPool_Service service)
+{
+  struct PooledConnection *entry = cls;
+  struct GST_ConnectionPool_GetHandle *gh;
+  struct GST_ConnectionPool_GetHandle *gh_next;
+  GST_connection_pool_peer_connect_notify cb;
+  void *cb_cls;
+
+  for (gh = entry->head_notify; NULL != gh;)
+  {
+    GNUNET_assert (NULL != gh->target);
+    GNUNET_assert (NULL != gh->connect_notify_cb);
+    GNUNET_assert (gh->connection_ready_called);
+    if (service != gh->service)
+    {
+      gh = gh->next;
+      continue;
+    }
+    if (0 != memcmp (gh->target, peer, sizeof (struct GNUNET_PeerIdentity)))
+    {
+      gh = gh->next;
+      continue;
+    }
+    cb = gh->connect_notify_cb;
+    cb_cls = gh->connect_notify_cb_cls;
+    gh_next = gh->next;
+    GNUNET_CONTAINER_DLL_remove (entry->head_notify, entry->tail_notify, gh);
+    gh = gh_next;
+    cb (cb_cls, peer);
+  }
+}
+
+
+/**
+ * Function called to notify transport users that another
+ * peer connected to us.
+ *
+ * @param cls the #PooledConnection object
+ * @param peer the peer that connected
+ */
+static void
+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);
+}
+
+
+/**
+ * Function called when resources for opening a connection to TRANSPORT are
+ * available.
+ *
+ * @param cls the #PooledConnection object
+ */
+static void
+opstart_get_handle_transport (void *cls)
+{
+  struct PooledConnection *entry = cls;
+
+  GNUNET_assert (NULL != entry);
+  LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->index);
+  entry->handle_transport =
+      GNUNET_TRANSPORT_connect (entry->cfg, NULL, entry, NULL,
+                                &transport_peer_connect_notify_cb, NULL);
+  if (NULL == entry->handle_transport)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  if (0 == entry->demand)
+    return;
+  if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
+    return;
+  if (NULL != search_waiting (entry, entry->head_waiting))
+  {
+    entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready, entry);
+    return;
+  }
+}
+
+
+/**
+ * Function called when the operation responsible for opening a TRANSPORT
+ * connection is marked as done.
+ *
+ * @param cls the cache entry
+ */
+static void
+oprelease_get_handle_transport (void *cls)
+{
+  struct PooledConnection *entry = cls;
+
+  if (NULL == entry->handle_transport)
+    return;
+  GNUNET_TRANSPORT_disconnect (entry->handle_transport);
+  entry->handle_transport = NULL;
+}
+
+
+/**
+ * Method called whenever a given peer connects at CORE level
+ *
+ * @param cls the #PooledConnection object
+ * @param peer peer identity this notification is about
+ */
+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);
+}
+
+
+/**
+ * Function called after GNUNET_CORE_connect has succeeded (or failed
+ * for good).  Note that the private key of the peer is intentionally
+ * not exposed here; if you need it, your process should try to read
+ * the private key file directly (which should work if you are
+ * authorized...).  Implementations of this function must not call
+ * GNUNET_CORE_disconnect (other than by scheduling a new task to
+ * do this later).
+ *
+ * @param cls the #PooledConnection object
+ * @param my_identity ID of this peer, NULL if we failed
+ */
+static void
+core_startup_cb (void *cls, 
+                 const struct GNUNET_PeerIdentity *my_identity)
+{
+  struct PooledConnection *entry = cls;
+
+  if (NULL == my_identity)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  GNUNET_assert (NULL == entry->peer_identity);
+  entry->peer_identity = GNUNET_new (struct GNUNET_PeerIdentity);
+  memcpy (entry->peer_identity,
+          my_identity,
+          sizeof (struct GNUNET_PeerIdentity));
+  if (0 == entry->demand)
+    return;
+  if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
+    return;
+  if (NULL != search_waiting (entry, entry->head_waiting))
+  {
+    entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready, entry);
+    return;
+  }
+}
+
+
+/**
+ * Function called when resources for opening a connection to CORE are
+ * available.
+ *
+ * @param cls the #PooledConnection object
+ */
+static void
+opstart_get_handle_core (void *cls)
+{
+  struct PooledConnection *entry = cls;
+  const struct GNUNET_CORE_MessageHandler no_handlers[] = {
+    {NULL, 0, 0}
+  };
+
+  GNUNET_assert (NULL != entry);
+  LOG_DEBUG ("Opening a CORE connection to peer %u\n", entry->index);
+  entry->handle_core =
+      GNUNET_CORE_connect (entry->cfg, entry,        /* closure */
+                           &core_startup_cb, /* core startup notify */
+                           &core_peer_connect_cb,    /* peer connect notify */
+                           NULL,     /* peer disconnect notify */
+                           NULL,     /* inbound notify */
+                           GNUNET_NO,        /* inbound header only? */
+                           NULL,     /* outbound notify */
+                           GNUNET_NO,        /* outbound header only? */
+                           no_handlers);
+}
+
+
+/**
+ * Function called when the operation responsible for opening a TRANSPORT
+ * connection is marked as done.
+ *
+ * @param cls the #PooledConnection object
+ */
+static void
+oprelease_get_handle_core (void *cls)
+{
+  struct PooledConnection *entry = cls;
+
+  if (NULL == entry->handle_core)
+    return;
+  GNUNET_CORE_disconnect (entry->handle_core);
+  entry->handle_core = NULL;
+  GNUNET_free_non_null (entry->peer_identity);
+  entry->peer_identity = NULL;
+}
+
+
+/**
+ * This function will be called for every #PooledConnection object in @p map
+ *
+ * @param cls NULL
+ * @param key current key code
+ * @param value the #PooledConnection object
+ * @return #GNUNET_YES if we should continue to
+ *         iterate,
+ *         #GNUNET_NO if not.
+ */
+static int
+cleanup_iterator (void *cls,
+                  uint32_t key,
+                  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;
+}
+
+
+/**
+ * Initialise the connection pool.
+ *
+ * @param size the size of the connection pool.  Each entry in the connection
+ *   pool can handle a connection to each of the services enumerated in
+ *   #GST_ConnectionPool_Service
+ */
+void
+GST_connection_pool_init (unsigned int size)
+{
+  max_size = size;
+  if (0 == max_size)
+    return;
+  GNUNET_assert (NULL == map);
+  map = GNUNET_CONTAINER_multihashmap32_create (((size * 3) / 4) + 1);
+}
+
+
+/**
+ * Cleanup the connection pool
+ */
+void
+GST_connection_pool_destroy ()
+{
+  struct PooledConnection *entry;
+  
+  if (NULL != map)
+  {
+    GNUNET_assert (GNUNET_SYSERR !=
+                   GNUNET_CONTAINER_multihashmap32_iterate (map,
+                                                            &cleanup_iterator,
+                                                            NULL));
+    GNUNET_CONTAINER_multihashmap32_destroy (map);
+    map = NULL;
+  }
+  while (NULL != (entry = head_lru))
+  {
+    GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
+    destroy_pooled_connection (entry);
+  }
+}
+
+
+/**
+ * Get a connection handle to @a service.  If the connection is opened before
+ * and the connection handle is present in the connection pool, it is returned
+ * through @a cb.  @a peer_id is used for the lookup in the connection pool.  
If
+ * the connection handle is not present in the connection pool, a new 
connection
+ * handle is opened for the @a service using @a cfg.  Additionally, @a target,
+ * @a connect_notify_cb can be specified to get notified when @a target is
+ * connected at @a service.
+ *
+ * @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
+ * 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).
+ *
+ * @param peer_id the index of the peer
+ * @param cfg the configuration with which the transport handle has to be
+ *          created if it was not present in the cache
+ * @param service the service of interest
+ * @param cb the callback to notify when the transport handle is available
+ * @param cb_cls the closure for @a cb
+ * @param target the peer identify of the peer whose connection to our 
TRANSPORT
+ *          subsystem will be notified through the @a connect_notify_cb. Can 
be NULL
+ * @param connect_notify_cb the callback to call when the @a target peer is
+ *          connected. This callback will only be called once or never again 
(in
+ *          case the target peer cannot be connected). Can be NULL
+ * @param connect_notify_cb_cls the closure for @a connect_notify_cb
+ * @return the handle which can be used cancel or mark that the handle is no
+ *           longer being used
+ */
+struct GST_ConnectionPool_GetHandle *
+GST_connection_pool_get_handle (unsigned int peer_id,
+                                const struct GNUNET_CONFIGURATION_Handle *cfg,
+                                enum GST_ConnectionPool_Service service,
+                                GST_connection_pool_connection_ready_cb cb,
+                                void *cb_cls,
+                                const struct GNUNET_PeerIdentity *target,
+                                GST_connection_pool_peer_connect_notify 
connect_notify_cb,
+                                void *connect_notify_cb_cls)
+{
+  struct GST_ConnectionPool_GetHandle *gh;
+  struct PooledConnection *entry;
+  struct GNUNET_TESTBED_Operation *op;
+  void *handle;
+  uint32_t peer_id32;
+
+  peer_id32 = (uint32_t) peer_id;
+  entry = NULL;
+  if (NULL != map)
+    entry = GNUNET_CONTAINER_multihashmap32_get (map, peer_id32);
+  if (NULL != entry)
+  {
+    if (entry->in_lru)
+    {
+      GNUNET_assert (0 == entry->demand);
+      expire_task_cancel (entry);
+      GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
+      entry->in_lru = GNUNET_NO;
+    }
+    switch (service)
+    {
+    case GST_CONNECTIONPOOL_SERVICE_TRANSPORT:
+      handle = entry->handle_transport;
+      if (NULL != handle)
+        LOG_DEBUG ("Found TRANSPORT handle for peer %u\n",
+                   entry->index);
+      break;
+    case GST_CONNECTIONPOOL_SERVICE_CORE:
+      handle = entry->handle_core;
+      if (NULL != handle)
+        LOG_DEBUG ("Found CORE handle for peer %u\n",
+                   entry->index);
+      break;
+    }
+  }
+  else
+  {
+    entry = GNUNET_new (struct PooledConnection);
+    entry->index = peer_id32;
+    if ((NULL != map) 
+        && (GNUNET_CONTAINER_multihashmap32_size (map) < max_size))
+    {
+      GNUNET_assert (GNUNET_OK ==
+                     GNUNET_CONTAINER_multihashmap32_put (map,
+                                                          entry->index,
+                                                          entry,
+                                                          
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
+      entry->in_pool = GNUNET_YES;
+    }
+    else
+    {
+      GNUNET_CONTAINER_DLL_insert_tail (head_not_pooled, tail_not_pooled, 
entry);
+    }
+    entry->cfg = GNUNET_CONFIGURATION_dup (cfg);
+  }
+  entry->demand++;
+  gh = GNUNET_new (struct GST_ConnectionPool_GetHandle);
+  gh->entry = entry;
+  gh->cb = cb;
+  gh->cb_cls = cb_cls;
+  gh->target = target;
+  gh->connect_notify_cb = connect_notify_cb;
+  gh->connect_notify_cb_cls = connect_notify_cb_cls;
+  gh->service = service;
+  GNUNET_CONTAINER_DLL_insert (entry->head_waiting, entry->tail_waiting, gh);
+  if (NULL != handle)
+  {
+    if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task)
+    {
+      if (NULL != search_waiting (entry, entry->head_waiting))
+        entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready, 
entry);
+    }
+    return gh;
+  }
+  op = NULL;
+  switch (gh->service)
+  {
+  case GST_CONNECTIONPOOL_SERVICE_TRANSPORT:
+    if (NULL != entry->op_transport)
+      return gh;                /* Operation pending */
+    op = GNUNET_TESTBED_operation_create_ (entry, 
&opstart_get_handle_transport,
+                                           &oprelease_get_handle_transport);
+    entry->op_transport = op;
+    break;
+  case GST_CONNECTIONPOOL_SERVICE_CORE:
+    if (NULL != entry->op_core)
+      return gh;                /* Operation pending */
+    op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_core,
+                                           &oprelease_get_handle_core);
+    entry->op_core = op;
+    break;
+  }
+  GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op);
+  GNUNET_TESTBED_operation_begin_wait_ (op);
+  return gh;
+}
+
+
+/**
+ * Relinquish a #GST_ConnectionPool_GetHandle object.  If the connection
+ * associated with the object is currently being used by other
+ * #GST_ConnectionPool_GetHandle objects, it is left in the connection pool.  
If
+ * no other objects are using the connection and the connection pool is not 
full
+ * then it is placed in a LRU queue.  If the connection pool is full, then
+ * connections from the LRU queue are evicted and closed to create place for 
this
+ * connection.  If the connection pool if full and the LRU queue is empty, then
+ * the connection is closed.
+ *
+ * @param gh the handle
+ */
+void
+GST_connection_pool_get_handle_done (struct GST_ConnectionPool_GetHandle *gh)
+{
+  struct PooledConnection *entry;
+
+  entry = gh->entry;
+  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);
+  GNUNET_free (gh);
+  gh = NULL;
+  GNUNET_assert (!entry->in_lru);  
+  if ( (!entry->in_pool) && (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);
+    GNUNET_assert (GNUNET_OK ==
+                   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--;
+  if (0 != entry->demand)
+    return;
+  if (entry->in_pool)
+  {
+    add_to_lru (entry);
+    return;
+  }
+  destroy_pooled_connection (entry);
+}

Added: gnunet/src/testbed/gnunet-service-testbed_connectionpool.h
===================================================================
--- gnunet/src/testbed/gnunet-service-testbed_connectionpool.h                  
        (rev 0)
+++ gnunet/src/testbed/gnunet-service-testbed_connectionpool.h  2013-09-26 
15:03:13 UTC (rev 29623)
@@ -0,0 +1,161 @@
+/*
+  This file is part of GNUnet.
+  (C) 2008--2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file testbed/gnunet-service-testbed_connectionpool.h
+ * @brief Interface for connection pooling subroutines
+ * @author Sree Harsha Totakura <address@hidden> 
+ */
+
+
+/**
+ * The request handle for obtaining a pooled connection
+ */
+struct GST_ConnectionPool_GetHandle;
+
+
+/**
+ * The type of service
+ */
+enum GST_ConnectionPool_Service
+{
+  /**
+   * Transport service
+   */
+  GST_CONNECTIONPOOL_SERVICE_TRANSPORT = 1,
+
+  /**
+   * Core service
+   */
+  GST_CONNECTIONPOOL_SERVICE_CORE
+};
+
+
+/**
+ * Initialise the connection pool.
+ *
+ * @param size the size of the connection pool.  Each entry in the connection
+ *   pool can handle a connection to each of the services enumerated in
+ *   #GST_ConnectionPool_Service
+ */
+void
+GST_connection_pool_init (unsigned int size);
+
+
+/**
+ * Cleanup the connection pool
+ */
+void
+GST_connection_pool_destroy ();
+
+/**
+ * Functions of this type are called when the needed handle is available for
+ * usage. These functions are to be registered with the function
+ * GST_connection_pool_get_handle(). The corresponding handles will be set upon
+ * success.  If they are not set, then it signals an error while opening the
+ * handles.
+ *
+ * @param cls the closure passed to GST_connection_pool_get_handle()
+ * @param ch the handle to CORE. Can be NULL if it is not requested
+ * @param th the handle to TRANSPORT. Can be NULL if it is not requested
+ * @param peer_id the identity of the peer. Will be NULL if ch is NULL. In 
other
+ *          cases, its value being NULL means that CORE connection has failed.
+ */
+typedef void 
+(*GST_connection_pool_connection_ready_cb) (void *cls,
+                                            struct GNUNET_CORE_Handle * ch,
+                                            struct GNUNET_TRANSPORT_Handle * 
th,
+                                            const struct GNUNET_PeerIdentity *
+                                            peer_id);
+
+
+/**
+ * Callback to notify when the target peer given to
+ * GST_connection_pool_get_handle() is connected.
+ *
+ * @param cls the closure given to GST_connection_pool_get_handle() for this
+ *   callback
+ * @param target the peer identity of the target peer
+ */
+typedef void
+(*GST_connection_pool_peer_connect_notify) (void *cls,
+                                            const struct GNUNET_PeerIdentity
+                                            *target);
+
+
+/**
+ * Get a connection handle to @a service.  If the connection is opened before
+ * and the connection handle is present in the connection pool, it is returned
+ * through @a cb.  @a peer_id is used for the lookup in the connection pool.  
If
+ * the connection handle is not present in the connection pool, a new 
connection
+ * handle is opened for the @a service using @a cfg.  Additionally, @a target,
+ * @a connect_notify_cb can be specified to get notified when @a target is
+ * connected at @a service.
+ *
+ * @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
+ * 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).
+ *
+ * @param peer_id the index of the peer
+ * @param cfg the configuration with which the transport handle has to be
+ *          created if it was not present in the cache
+ * @param service the service of interest
+ * @param cb the callback to notify when the transport handle is available
+ * @param cb_cls the closure for @a cb
+ * @param target the peer identify of the peer whose connection to our 
TRANSPORT
+ *          subsystem will be notified through the @a connect_notify_cb. Can 
be NULL
+ * @param connect_notify_cb the callback to call when the @a target peer is
+ *          connected. This callback will only be called once or never again 
(in
+ *          case the target peer cannot be connected). Can be NULL
+ * @param connect_notify_cb_cls the closure for @a connect_notify_cb
+ * @return the handle which can be used cancel or mark that the handle is no
+ *           longer being used
+ */
+struct GST_ConnectionPool_GetHandle *
+GST_connection_pool_get_handle (unsigned int peer_id,
+                                const struct GNUNET_CONFIGURATION_Handle *cfg,
+                                enum GST_ConnectionPool_Service service,
+                                GST_connection_pool_connection_ready_cb cb,
+                                void *cb_cls,
+                                const struct GNUNET_PeerIdentity *target,
+                                GST_connection_pool_peer_connect_notify 
connect_notify_cb,
+                                void *connect_notify_cb_cls);
+
+
+/**
+ * Relinquish a #GST_ConnectionPool_GetHandle object.  If the connection
+ * associated with the object is currently being used by other
+ * #GST_ConnectionPool_GetHandle objects, it is left in the connection pool.  
If
+ * no other objects are using the connection and the connection pool is not 
full
+ * then it is placed in a LRU queue.  If the connection pool is full, then
+ * connections from the LRU queue are evicted and closed to create place for 
this
+ * connection.  If the connection pool if full and the LRU queue is empty, then
+ * the connection is closed.
+ *
+ * @param gh the handle
+ */
+void
+GST_connection_pool_get_handle_done (struct GST_ConnectionPool_GetHandle *gh);
+
+
+/* End of gnunet-service-testbed_connectionpool.h */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]