aboutsummaryrefslogtreecommitdiff
path: root/src/testbed/gnunet-service-testbed_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testbed/gnunet-service-testbed_cache.c')
-rw-r--r--src/testbed/gnunet-service-testbed_cache.c1048
1 files changed, 1048 insertions, 0 deletions
diff --git a/src/testbed/gnunet-service-testbed_cache.c b/src/testbed/gnunet-service-testbed_cache.c
new file mode 100644
index 0000000..112868e
--- /dev/null
+++ b/src/testbed/gnunet-service-testbed_cache.c
@@ -0,0 +1,1048 @@
+/*
+ This file is part of GNUnet.
+ (C) 2012 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 2, 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_cache.h
+ * @brief testbed cache implementation
+ * @author Sree Harsha Totakura
+ */
+#include "gnunet-service-testbed.h"
+
+/**
+ * Redefine LOG with a changed log component string
+ */
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(kind,...) \
+ GNUNET_log_from (kind, "testbed-cache", __VA_ARGS__)
+
+
+/**
+ * Time to expire a cache entry
+ */
+#define CACHE_EXPIRY \
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
+
+
+/**
+ * Type of cache-get requests
+ */
+enum CacheGetType
+{
+ /**
+ * Get transport handle
+ */
+ CGT_TRANSPORT_HANDLE = 1,
+
+ /**
+ * Get core handle
+ */
+ CGT_CORE_HANDLE
+};
+
+
+/**
+ * The cache-get request handle
+ */
+struct GSTCacheGetHandle;
+
+
+/**
+ * This context structure is used to maintain a queue of notifications to check
+ * which of them are to be notified when a peer is connected.
+ */
+struct ConnectNotifyContext
+{
+ /**
+ * The next ptr for the DLL
+ */
+ struct ConnectNotifyContext *next;
+
+ /**
+ * The prev ptr for the DLL
+ */
+ struct ConnectNotifyContext *prev;
+
+ /**
+ * The peer identity of the target peer. When this target peer is connected,
+ * call the notify callback
+ */
+ const struct GNUNET_PeerIdentity *target;
+
+ /**
+ * The notify callback to be called when the target peer is connected
+ */
+ GST_cache_peer_connect_notify cb;
+
+ /**
+ * The closure for the notify callback
+ */
+ void *cb_cls;
+
+ /**
+ * The GSTCacheGetHandle reposible for creating this context
+ */
+ struct GSTCacheGetHandle *cgh;
+
+};
+
+
+/**
+ * The cache-get request handle
+ */
+struct GSTCacheGetHandle
+{
+ /**
+ * The next ptr for the DLL. Used in struct CacheEntry
+ */
+ struct GSTCacheGetHandle *next;
+
+ /**
+ * The prev ptr for the DLL. Used in struct CacheEntry
+ */
+ struct GSTCacheGetHandle *prev;
+
+ /**
+ * The cache entry object this handle corresponds to
+ */
+ struct CacheEntry *entry;
+
+ /**
+ * The cache callback to call when a handle is available
+ */
+ GST_cache_handle_ready_cb cb;
+
+ /**
+ * The closure for the above callback
+ */
+ void *cb_cls;
+
+ /**
+ * The peer connect notify context created for this handle; can be NULL
+ */
+ struct ConnectNotifyContext *nctxt;
+
+ /**
+ * The type of this cache-get request
+ */
+ enum CacheGetType type;
+
+ /**
+ * Did we call the cache callback already?
+ */
+ int notify_called;
+};
+
+/**
+ * Cache entry
+ */
+struct CacheEntry
+{
+ /**
+ * DLL next ptr for least recently used cache entries
+ */
+ struct CacheEntry *next;
+
+ /**
+ * DLL prev ptr for least recently used cache entries
+ */
+ struct CacheEntry *prev;
+
+ /**
+ * The transport handle to the peer corresponding to this entry; can be NULL
+ */
+ struct GNUNET_TRANSPORT_Handle *transport_handle_;
+
+ /**
+ * The operation handle for transport handle
+ */
+ struct GNUNET_TESTBED_Operation *transport_op_;
+
+ /**
+ * The core handle to the peer corresponding to this entry; can be NULL
+ */
+ struct GNUNET_CORE_Handle *core_handle;
+
+ /**
+ * The operation handle for core handle
+ */
+ struct GNUNET_TESTBED_Operation *core_op;
+
+ /**
+ * 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;
+
+ /**
+ * The key for this entry
+ */
+ struct GNUNET_HashCode key;
+
+ /**
+ * The HELLO message
+ */
+ struct GNUNET_MessageHeader *hello;
+
+ /**
+ * the head of the CacheGetHandle queue
+ */
+ struct GSTCacheGetHandle *cgh_qhead;
+
+ /**
+ * the tail of the CacheGetHandle queue
+ */
+ struct GSTCacheGetHandle *cgh_qtail;
+
+ /**
+ * DLL head for the queue of notifications contexts to check which of them are to
+ * be notified when a peer is connected.
+ */
+ struct ConnectNotifyContext *nctxt_qhead;
+
+ /**
+ * DLL tail for the queue of notifications contexts to check which of them are to
+ * be notified when a peer is connected.
+ */
+ struct ConnectNotifyContext *nctxt_qtail;
+
+ /**
+ * The task that calls the cache callback
+ */
+ GNUNET_SCHEDULER_TaskIdentifier notify_task;
+
+ /**
+ * The task to expire this cache entry, free any handlers it has opened and
+ * mark their corresponding operations as done.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier expire_task;
+
+ /**
+ * Number of operations this cache entry is being used
+ */
+ unsigned int demand;
+
+ /**
+ * The id of the peer this entry corresponds to
+ */
+ unsigned int peer_id;
+
+ /**
+ * Is this entry in LRU cache queue?
+ */
+ unsigned int in_lru;
+};
+
+
+/**
+ * Hashmap to maintain cache
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *cache;
+
+/**
+ * DLL head for least recently used cache entries; least recently used
+ * cache items are at the head. The cache enties are added to this queue when
+ * their demand becomes zero. They are removed from the queue when they are
+ * needed by any operation.
+ */
+static struct CacheEntry *lru_cache_head;
+
+/**
+ * DLL tail for least recently used cache entries; recently used cache
+ * items are at the tail.The cache enties are added to this queue when
+ * their demand becomes zero. They are removed from the queue when they are
+ * needed by any operation.
+ */
+static struct CacheEntry *lru_cache_tail;
+
+/**
+ * the size of the LRU queue
+ */
+static unsigned int lru_cache_size;
+
+/**
+ * the threshold size for the LRU queue
+ */
+static unsigned int lru_cache_threshold_size;
+
+/**
+ * The total number of elements in cache
+ */
+static unsigned int cache_size;
+
+
+/**
+ * Looks up in the cache and returns the entry
+ *
+ * @param id the peer identity of the peer whose corresponding entry has to be looked up
+ * @return the HELLO message; NULL if not found
+ */
+static struct CacheEntry *
+cache_lookup (const struct GNUNET_HashCode *key)
+{
+ struct CacheEntry *entry;
+
+ if (NULL == cache)
+ return NULL;
+ entry = GNUNET_CONTAINER_multihashmap_get (cache, key);
+ return entry;
+}
+
+
+/**
+ * Function to disconnect the core and transport handles; free the existing
+ * configuration; and remove from the LRU cache list. The entry is left to be in
+ * the hash table so that the HELLO can still be found later
+ *
+ * @param entry the cache entry
+ */
+static void
+close_handles (struct CacheEntry *entry)
+{
+ struct ConnectNotifyContext *ctxt;
+
+ GNUNET_assert (0 == entry->demand);
+ if (GNUNET_YES == entry->in_lru)
+ {
+ GNUNET_assert (0 < lru_cache_size);
+ if (GNUNET_SCHEDULER_NO_TASK != entry->expire_task)
+ {
+ GNUNET_SCHEDULER_cancel (entry->expire_task);
+ entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry);
+ lru_cache_size--;
+ entry->in_lru = GNUNET_NO;
+ }
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->expire_task);
+ while (NULL != (ctxt = entry->nctxt_qhead))
+ {
+ GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
+ GNUNET_free (ctxt);
+ }
+ LOG_DEBUG ("Cleaning up handles from an entry in cache\n");
+ if (NULL != entry->transport_handle_)
+ {
+ GNUNET_assert (NULL != entry->transport_op_);
+ GNUNET_TESTBED_operation_done (entry->transport_op_);
+ entry->transport_op_ = NULL;
+ }
+ if (NULL != entry->core_handle)
+ {
+ GNUNET_assert (NULL != entry->core_op);
+ GNUNET_TESTBED_operation_done (entry->core_op);
+ entry->core_op = NULL;
+ }
+ if (NULL != entry->cfg)
+ {
+ GNUNET_CONFIGURATION_destroy (entry->cfg);
+ entry->cfg = NULL;
+ }
+}
+
+
+/**
+ * The task to expire this cache entry, free any handlers it has opened and
+ * mark their corresponding operations as done.
+ *
+ * @param cls the CacheEntry
+ * @param tc the scheduler task context
+ */
+static void
+expire_cache_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct CacheEntry *entry = cls;
+
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->expire_task);
+ entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
+ close_handles (entry);
+}
+
+
+/**
+ * Creates a new cache entry and then puts it into the cache's hashtable.
+ *
+ * @param key the hash code to use for inserting the newly created entry
+ * @param peer_id the index of the peer to tag the newly created entry
+ * @return the newly created entry
+ */
+static struct CacheEntry *
+add_entry (const struct GNUNET_HashCode *key, unsigned int peer_id)
+{
+ struct CacheEntry *entry;
+
+ entry = GNUNET_malloc (sizeof (struct CacheEntry));
+ entry->peer_id = peer_id;
+ memcpy (&entry->key, key, sizeof (struct GNUNET_HashCode));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (cache, &entry->key, entry,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
+ cache_size++;
+ return entry;
+}
+
+
+/**
+ * Function to find a suitable GSTCacheGetHandle which is waiting for one of the
+ * handles in given entry to be available.
+ *
+ * @param entry the cache entry whose GSTCacheGetHandle 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 GSTCacheGetHandle *
+search_suitable_cgh (const struct CacheEntry *entry,
+ const struct GSTCacheGetHandle *head)
+{
+ const struct GSTCacheGetHandle *cgh;
+
+ for (cgh = head; NULL != cgh; cgh = cgh->next)
+ {
+ if (GNUNET_YES == cgh->notify_called)
+ return NULL;
+ switch (cgh->type)
+ {
+ case CGT_TRANSPORT_HANDLE:
+ if (NULL == entry->transport_handle_)
+ continue;
+ break;
+ case CGT_CORE_HANDLE:
+ if (NULL == entry->core_handle)
+ continue;
+ break;
+ }
+ break;
+ }
+ return (struct GSTCacheGetHandle *) cgh;
+}
+
+
+/**
+ * Task to call the handle ready notify callback of a queued GSTCacheGetHandle
+ * of an entry when one or all of its handles are available.
+ *
+ * @param cls the cache entry
+ * @param tc the task context from scheduler
+ */
+static void
+call_cgh_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct CacheEntry *entry = cls;
+ struct GSTCacheGetHandle *cgh;
+ const struct GSTCacheGetHandle *cgh2;
+
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->notify_task);
+ entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
+ cgh = search_suitable_cgh (entry, entry->cgh_qhead);
+ GNUNET_assert (NULL != cgh);
+ cgh2 = NULL;
+ if (NULL != cgh->next)
+ cgh2 = search_suitable_cgh (entry, cgh->next);
+ GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh);
+ cgh->notify_called = GNUNET_YES;
+ GNUNET_CONTAINER_DLL_insert_tail (entry->cgh_qhead, entry->cgh_qtail, cgh);
+ if (NULL != cgh2)
+ entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
+ if (NULL != cgh->nctxt)
+ { /* Register the peer connect notify callback */
+ GNUNET_CONTAINER_DLL_insert_tail (entry->nctxt_qhead, entry->nctxt_qtail,
+ cgh->nctxt);
+ }
+ LOG_DEBUG ("Calling notify for handle type %u\n", cgh->type);
+ cgh->cb (cgh->cb_cls, entry->core_handle, entry->transport_handle_,
+ 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 cache entry
+ * @param peer the peer that connected
+ * @param type the type of the handle this notification corresponds to
+ */
+static void
+peer_connect_notify_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
+ const enum CacheGetType type)
+{
+ struct CacheEntry *entry = cls;
+ struct ConnectNotifyContext *ctxt;
+ struct ConnectNotifyContext *ctxt2;
+ GST_cache_peer_connect_notify cb;
+ void *cb_cls;
+
+
+ for (ctxt = entry->nctxt_qhead; NULL != ctxt;)
+ {
+ GNUNET_assert (NULL != ctxt->cgh);
+ if (type != ctxt->cgh->type)
+ {
+ ctxt = ctxt->next;
+ continue;
+ }
+ if (0 != memcmp (ctxt->target, peer, sizeof (struct GNUNET_PeerIdentity)))
+ {
+ ctxt = ctxt->next;
+ continue;
+ }
+ cb = ctxt->cb;
+ cb_cls = ctxt->cb_cls;
+ ctxt->cgh->nctxt = NULL;
+ ctxt2 = ctxt->next;
+ GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
+ GNUNET_free (ctxt);
+ ctxt = ctxt2;
+ cb (cb_cls, peer);
+ }
+ if (NULL == ctxt)
+ return;
+
+}
+
+
+/**
+ * Function called to notify transport users that another
+ * peer connected to us.
+ *
+ * @param cls closure
+ * @param peer the peer that connected
+ * @param ats performance data
+ * @param ats_count number of entries in ats (excluding 0-termination)
+ */
+static void
+transport_peer_connect_notify_cb (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ const struct GNUNET_ATS_Information *ats,
+ uint32_t ats_count)
+{
+ peer_connect_notify_cb (cls, peer, CGT_TRANSPORT_HANDLE);
+}
+
+
+/**
+ * Function called when resources for opening a connection to TRANSPORT are
+ * available.
+ *
+ * @param cls the cache entry
+ */
+static void
+opstart_get_handle_transport (void *cls)
+{
+ struct CacheEntry *entry = cls;
+
+ GNUNET_assert (NULL != entry);
+ LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->peer_id);
+ entry->transport_handle_ =
+ GNUNET_TRANSPORT_connect (entry->cfg, NULL, entry, NULL,
+ &transport_peer_connect_notify_cb, NULL);
+ if (NULL == entry->transport_handle_)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (0 == entry->demand)
+ return;
+ if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
+ return;
+ if (NULL != search_suitable_cgh (entry, entry->cgh_qhead))
+ entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
+}
+
+
+/**
+ * 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 CacheEntry *entry = cls;
+
+ if (NULL == entry->transport_handle_)
+ return;
+ GNUNET_TRANSPORT_disconnect (entry->transport_handle_);
+ entry->transport_handle_ = NULL;
+}
+
+
+/**
+ * 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 closure
+ * @param server handle to the server, NULL if we failed
+ * @param my_identity ID of this peer, NULL if we failed
+ */
+static void
+core_startup_cb (void *cls, struct GNUNET_CORE_Handle *server,
+ const struct GNUNET_PeerIdentity *my_identity)
+{
+ struct CacheEntry *entry = cls;
+
+ if (NULL == my_identity)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_assert (NULL == entry->peer_identity);
+ entry->core_handle = server;
+ entry->peer_identity = GNUNET_malloc (sizeof (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_suitable_cgh (entry, entry->cgh_qhead))
+ entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
+}
+
+
+/**
+ * Method called whenever a given peer connects at CORE level
+ *
+ * @param cls closure
+ * @param peer peer identity this notification is about
+ * @param atsi performance data for the connection
+ * @param atsi_count number of records in 'atsi'
+ */
+static void
+core_peer_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
+ const struct GNUNET_ATS_Information *atsi,
+ unsigned int atsi_count)
+{
+ peer_connect_notify_cb (cls, peer, CGT_CORE_HANDLE);
+}
+
+
+/**
+ * Function called when resources for opening a connection to CORE are
+ * available.
+ *
+ * @param cls the cache entry
+ */
+static void
+opstart_get_handle_core (void *cls)
+{
+ struct CacheEntry *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->peer_id);
+ /* void?: We also get the handle when the connection to CORE is successful */
+ (void) 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 cache entry
+ */
+static void
+oprelease_get_handle_core (void *cls)
+{
+ struct CacheEntry *entry = cls;
+
+ if (NULL == entry->core_handle)
+ return;
+ GNUNET_CORE_disconnect (entry->core_handle);
+ entry->core_handle = NULL;
+ GNUNET_free_non_null (entry->peer_identity);
+ entry->peer_identity = NULL;
+}
+
+
+/**
+ * Function to get a handle with given configuration. The type of the handle is
+ * implicitly provided in the GSTCacheGetHandle. If the handle is already cached
+ * before, it will be retured in the given callback; the peer_id is used to
+ * lookup in the cache; if not, a new operation is started to open the transport
+ * handle and will be given in the callback when it is available.
+ *
+ * @param cls the cache entry
+ */
+static struct GSTCacheGetHandle *
+cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const struct GNUNET_PeerIdentity *target,
+ GST_cache_peer_connect_notify connect_notify_cb,
+ void *connect_notify_cb_cls)
+{
+ struct GNUNET_HashCode key;
+ void *handle;
+ struct CacheEntry *entry;
+ struct ConnectNotifyContext *ctxt;
+ struct GNUNET_TESTBED_Operation *op;
+
+ GNUNET_assert (0 != cgh->type);
+ GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
+ handle = NULL;
+ entry = cache_lookup (&key);
+ if (NULL != entry)
+ {
+ if (GNUNET_YES == entry->in_lru)
+ {
+ GNUNET_assert (0 == entry->demand);
+ GNUNET_assert (0 < lru_cache_size);
+ if (GNUNET_SCHEDULER_NO_TASK != entry->expire_task)
+ {
+ GNUNET_SCHEDULER_cancel (entry->expire_task);
+ entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry);
+ lru_cache_size--;
+ entry->in_lru = GNUNET_NO;
+ }
+ switch (cgh->type)
+ {
+ case CGT_TRANSPORT_HANDLE:
+ handle = entry->transport_handle_;
+ if (NULL != handle)
+ LOG_DEBUG ("Found TRANSPORT handle in cache for peer %u\n",
+ entry->peer_id);
+ break;
+ case CGT_CORE_HANDLE:
+ handle = entry->core_handle;
+ if (NULL != handle)
+ LOG_DEBUG ("Found CORE handle in cache for peer %u\n", entry->peer_id);
+ break;
+ }
+ }
+ if (NULL == entry)
+ entry = add_entry (&key, peer_id);
+ if (NULL == entry->cfg)
+ entry->cfg = GNUNET_CONFIGURATION_dup (cfg);
+ entry->demand++;
+ cgh->entry = entry;
+ GNUNET_CONTAINER_DLL_insert (entry->cgh_qhead, entry->cgh_qtail, cgh);
+ if ((NULL != target) && (NULL != connect_notify_cb))
+ {
+ ctxt = GNUNET_malloc (sizeof (struct ConnectNotifyContext));
+ ctxt->target = target;
+ ctxt->cb = connect_notify_cb;
+ ctxt->cb_cls = connect_notify_cb_cls;
+ GNUNET_assert (NULL == cgh->nctxt);
+ cgh->nctxt = ctxt;
+ ctxt->cgh = cgh;
+ }
+ if (NULL != handle)
+ {
+ if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task)
+ entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
+ return cgh;
+ }
+ switch (cgh->type)
+ {
+ case CGT_TRANSPORT_HANDLE:
+ if (NULL != entry->transport_op_)
+ return cgh;
+ op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_transport,
+ &oprelease_get_handle_transport);
+ entry->transport_op_ = op;
+ break;
+ case CGT_CORE_HANDLE:
+ if (NULL != entry->core_op)
+ return cgh;
+ op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_core,
+ &oprelease_get_handle_core);
+ entry->core_op = op;
+ break;
+ }
+ GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op);
+ GNUNET_TESTBED_operation_begin_wait_ (op);
+ return cgh;
+}
+
+
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+static int
+cache_clear_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+ struct CacheEntry *entry = value;
+ static unsigned int ncleared;
+
+ GNUNET_assert (NULL != entry);
+ GNUNET_break (0 == entry->demand);
+ LOG_DEBUG ("Clearing entry %u of %u\n", ++ncleared, cache_size);
+ GNUNET_CONTAINER_multihashmap_remove (cache, key, value);
+ if (0 == entry->demand)
+ close_handles (entry);
+ GNUNET_free_non_null (entry->hello);
+ GNUNET_break (GNUNET_SCHEDULER_NO_TASK == entry->expire_task);
+ GNUNET_break (NULL == entry->transport_handle_);
+ GNUNET_break (NULL == entry->transport_op_);
+ GNUNET_break (NULL == entry->core_handle);
+ GNUNET_break (NULL == entry->core_op);
+ GNUNET_break (NULL == entry->cfg);
+ GNUNET_assert (NULL == entry->cgh_qhead);
+ GNUNET_assert (NULL == entry->cgh_qtail);
+ GNUNET_assert (NULL == entry->nctxt_qhead);
+ GNUNET_assert (NULL == entry->nctxt_qtail);
+ GNUNET_free (entry);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Clear cache
+ */
+void
+GST_cache_clear ()
+{
+ GNUNET_CONTAINER_multihashmap_iterate (cache, &cache_clear_iterator, NULL);
+ GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (cache));
+ GNUNET_CONTAINER_multihashmap_destroy (cache);
+}
+
+
+/**
+ * Initializes the cache
+ *
+ * @param size the size of the cache
+ */
+void
+GST_cache_init (unsigned int size)
+{
+ if (0 == size)
+ return;
+ lru_cache_threshold_size = size;
+ if (size > 1)
+ size = size / 2;
+ cache = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_YES);
+}
+
+
+/**
+ * Mark the GetCacheHandle as being done if a handle has been provided already
+ * or as being cancelled if the callback for the handle hasn't been called.
+ *
+ * @param cgh the CacheGetHandle handle
+ */
+void
+GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh)
+{
+ struct CacheEntry *entry;
+
+ entry = cgh->entry;
+ GNUNET_assert (NULL != entry);
+ GNUNET_assert (0 < entry->demand);
+ entry->demand--;
+ if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
+ {
+ GNUNET_SCHEDULER_cancel (entry->notify_task);
+ entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh);
+ if (NULL != cgh->nctxt)
+ {
+ GNUNET_assert (cgh == cgh->nctxt->cgh);
+ if (GNUNET_YES == cgh->notify_called)
+ GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail,
+ cgh->nctxt);
+ GNUNET_free (cgh->nctxt);
+ }
+ GNUNET_free (cgh);
+ if (0 == entry->demand)
+ {
+ entry->expire_task =
+ GNUNET_SCHEDULER_add_delayed (CACHE_EXPIRY, &expire_cache_entry, entry);
+ GNUNET_CONTAINER_DLL_insert_tail (lru_cache_head, lru_cache_tail, entry);
+ lru_cache_size++;
+ entry->in_lru = GNUNET_YES;
+ if (lru_cache_size > lru_cache_threshold_size)
+ close_handles (lru_cache_head);
+ }
+ else
+ {
+ struct GSTCacheGetHandle *cgh2;
+
+ if (NULL != (cgh2 = search_suitable_cgh (entry, entry->cgh_qhead)))
+ entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
+ }
+}
+
+
+/**
+ * Get a transport handle with the given configuration. If the handle is
+ * already cached before, it will be retured in the given callback; the peer_id
+ * is used to lookup in the cache; if not, a new operation is started to open the
+ * transport handle and will be given in the callback when it is available.
+ *
+ * @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 cb the callback to notify when the transport handle is available
+ * @param cb_cls the closure for the above callback
+ * @param target the peer identify of the peer whose connection to our TRANSPORT
+ * subsystem will be notified through the connect_notify_cb. Can be NULL
+ * @param connect_notify_cb the callback to call when the given 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 the above callback
+ * @return the handle which can be used cancel or mark that the handle is no
+ * longer being used
+ */
+struct GSTCacheGetHandle *
+GST_cache_get_handle_transport (unsigned int peer_id,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ GST_cache_handle_ready_cb cb, void *cb_cls,
+ const struct GNUNET_PeerIdentity *target,
+ GST_cache_peer_connect_notify connect_notify_cb,
+ void *connect_notify_cb_cls)
+{
+ struct GSTCacheGetHandle *cgh;
+
+ cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
+ cgh->cb = cb;
+ cgh->cb_cls = cb_cls;
+ cgh->type = CGT_TRANSPORT_HANDLE;
+ return cache_get_handle (peer_id, cgh, cfg, target, connect_notify_cb,
+ connect_notify_cb_cls);
+}
+
+
+/**
+ * Get a CORE handle with the given configuration. If the handle is already
+ * cached before, it will be retured in the given callback; the peer_id is used
+ * to lookup in the cache. If the handle is not cached before, a new operation
+ * is started to open the CORE handle and will be given in the callback when it
+ * is available along with the peer identity
+ *
+ * @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 cb the callback to notify when the transport handle is available
+ * @param cb_cls the closure for the above callback
+ * @param target the peer identify of the peer whose connection to our CORE
+ * subsystem will be notified through the connect_notify_cb. Can be NULL
+ * @param connect_notify_cb the callback to call when the given 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 the above callback
+ * @return the handle which can be used cancel or mark that the handle is no
+ * longer being used
+ */
+struct GSTCacheGetHandle *
+GST_cache_get_handle_core (unsigned int peer_id,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ GST_cache_handle_ready_cb cb, void *cb_cls,
+ const struct GNUNET_PeerIdentity *target,
+ GST_cache_peer_connect_notify connect_notify_cb,
+ void *connect_notify_cb_cls)
+{
+ struct GSTCacheGetHandle *cgh;
+
+ cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
+ cgh->cb = cb;
+ cgh->cb_cls = cb_cls;
+ cgh->type = CGT_CORE_HANDLE;
+ return cache_get_handle (peer_id, cgh, cfg, target, connect_notify_cb,
+ connect_notify_cb_cls);
+}
+
+
+/**
+ * Looks up in the hello cache and returns the HELLO of the given peer
+ *
+ * @param peer_id the index of the peer whose HELLO has to be looked up
+ * @return the HELLO message; NULL if not found
+ */
+const struct GNUNET_MessageHeader *
+GST_cache_lookup_hello (const unsigned int peer_id)
+{
+ struct CacheEntry *entry;
+ struct GNUNET_HashCode key;
+
+ LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
+ GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
+ entry = cache_lookup (&key);
+ if (NULL == entry)
+ return NULL;
+ if (NULL != entry->hello)
+ LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
+ return entry->hello;
+}
+
+
+/**
+ * Caches the HELLO of the given peer. Updates the HELLO if it was already
+ * cached before
+ *
+ * @param id the peer identity of the peer whose HELLO has to be cached
+ * @param hello the HELLO message
+ */
+void
+GST_cache_add_hello (const unsigned int peer_id,
+ const struct GNUNET_MessageHeader *hello)
+{
+ struct CacheEntry *entry;
+ struct GNUNET_HashCode key;
+
+ GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
+ entry = GNUNET_CONTAINER_multihashmap_get (cache, &key);
+ if (NULL == entry)
+ entry = add_entry (&key, peer_id);
+ GNUNET_free_non_null (entry->hello);
+ entry->hello = GNUNET_copy_message (hello);
+}
+
+/* end of gnunet-service-testbed_hc.c */