- give out run handle through which master controller's handle can be retrieved
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_cache.c
index 46c7d4fce367c715447a84451d76775e8233fb53..6a141a2525727dc75adebd2c92ce99b84c674dff 100644 (file)
@@ -1,10 +1,10 @@
 /*
   This file is part of GNUnet.
-  (C) 2012 Christian Grothoff (and other contributing authors)
+  (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 2, or (at your
+  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
@@ -19,7 +19,7 @@
 */
 
 /**
- * @file testbed/gnunet-service-testbed_cache.h
+ * @file testbed/gnunet-service-testbed_cache.c
  * @brief testbed cache implementation
  * @author Sree Harsha Totakura
  */
   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
  */
@@ -162,12 +169,12 @@ struct CacheEntry
   /**
    * The transport handle to the peer corresponding to this entry; can be NULL
    */
-  struct GNUNET_TRANSPORT_Handle *transport_handle_;
+  struct GNUNET_TRANSPORT_Handle *transport_handle;
 
   /**
    * The operation handle for transport handle
    */
-  struct GNUNET_TESTBED_Operation *transport_op_;
+  struct GNUNET_TESTBED_Operation *transport_op;
 
   /**
    * The core handle to the peer corresponding to this entry; can be NULL
@@ -229,6 +236,12 @@ struct CacheEntry
    */
   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
    */
@@ -286,7 +299,8 @@ 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
+ * @param key 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 *
@@ -317,28 +331,35 @@ close_handles (struct CacheEntry *entry)
   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_)
+  if (NULL != entry->transport_handle)
+    GNUNET_assert (NULL != entry->transport_op);
+  if (NULL != entry->transport_op)
   {
-    GNUNET_assert (NULL != entry->transport_op_);
-    GNUNET_TESTBED_operation_done (entry->transport_op_);
-    entry->transport_op_ = NULL;
+    GNUNET_TESTBED_operation_done (entry->transport_op);
+    entry->transport_op = NULL;    
   }
-  if (NULL != entry->core_handle)
+  if (NULL != entry->core_op)
   {
-    GNUNET_assert (NULL != entry->core_op);
     GNUNET_TESTBED_operation_done (entry->core_op);
     entry->core_op = NULL;
   }
+  GNUNET_assert (NULL == entry->core_handle);
   if (NULL != entry->cfg)
   {
     GNUNET_CONFIGURATION_destroy (entry->cfg);
@@ -347,6 +368,24 @@ close_handles (struct CacheEntry *entry)
 }
 
 
+/**
+ * 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.
  *
@@ -394,12 +433,14 @@ search_suitable_cgh (const struct CacheEntry *entry,
     switch (cgh->type)
     {
     case CGT_TRANSPORT_HANDLE:
-      if (NULL == entry->transport_handle_)
+      if (NULL == entry->transport_handle)
         continue;
       break;
     case CGT_CORE_HANDLE:
       if (NULL == entry->core_handle)
         continue;
+      if (NULL == entry->peer_identity) /* Our CORE connection isn't ready yet */
+        continue;
       break;
     }
     break;
@@ -440,7 +481,7 @@ call_cgh_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                                       cgh->nctxt);
   }
   LOG_DEBUG ("Calling notify for handle type %u\n", cgh->type);
-  cgh->cb (cgh->cb_cls, entry->core_handle, entry->transport_handle_,
+  cgh->cb (cgh->cb_cls, entry->core_handle, entry->transport_handle,
            entry->peer_identity);
 }
 
@@ -499,14 +540,10 @@ peer_connect_notify_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
  *
  * @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)
+                                  const struct GNUNET_PeerIdentity *peer)
 {
   peer_connect_notify_cb (cls, peer, CGT_TRANSPORT_HANDLE);
 }
@@ -525,10 +562,10 @@ opstart_get_handle_transport (void *cls)
 
   GNUNET_assert (NULL != entry);
   LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->peer_id);
-  entry->transport_handle_ =
+  entry->transport_handle =
       GNUNET_TRANSPORT_connect (entry->cfg, NULL, entry, NULL,
                                 &transport_peer_connect_notify_cb, NULL);
-  if (NULL == entry->transport_handle_)
+  if (NULL == entry->transport_handle)
   {
     GNUNET_break (0);
     return;
@@ -553,10 +590,10 @@ oprelease_get_handle_transport (void *cls)
 {
   struct CacheEntry *entry = cls;
 
-  if (NULL == entry->transport_handle_)
+  if (NULL == entry->transport_handle)
     return;
-  GNUNET_TRANSPORT_disconnect (entry->transport_handle_);
-  entry->transport_handle_ = NULL;
+  GNUNET_TRANSPORT_disconnect (entry->transport_handle);
+  entry->transport_handle = NULL;
 }
 
 
@@ -570,11 +607,10 @@ oprelease_get_handle_transport (void *cls)
  * 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,
+core_startup_cb (void *cls, 
                  const struct GNUNET_PeerIdentity *my_identity)
 {
   struct CacheEntry *entry = cls;
@@ -585,8 +621,8 @@ core_startup_cb (void *cls, struct GNUNET_CORE_Handle *server,
     return;
   }
   GNUNET_assert (NULL == entry->peer_identity);
-  entry->core_handle = server;
-  entry->peer_identity = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
+  // FIXME: why is this dynamically allocated?
+  entry->peer_identity = GNUNET_new (struct GNUNET_PeerIdentity);
   memcpy (entry->peer_identity, my_identity,
           sizeof (struct GNUNET_PeerIdentity));
   if (0 == entry->demand)
@@ -603,13 +639,9 @@ core_startup_cb (void *cls, struct GNUNET_CORE_Handle *server,
  *
  * @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)
+core_peer_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
 {
   peer_connect_notify_cb (cls, peer, CGT_CORE_HANDLE);
 }
@@ -632,16 +664,16 @@ opstart_get_handle_core (void *cls)
 
   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);
+  entry->core_handle =
+      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);
 }
 
 
@@ -672,7 +704,19 @@ oprelease_get_handle_core (void *cls)
  * 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
+ * @param peer_id the index of the peer
+ * @param cgh the CacheGetHandle
+ * @param cfg the configuration with which the transport handle has to be
+ *          created if it was not present in the cache
+ * @param target the peer identify of the peer whose connection to
+ *          TRANSPORT/CORE (depending on the type of 'cgh') 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 to cancel or mark that the handle is no
+ *           longer being used
  */
 static struct GSTCacheGetHandle *
 cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh,
@@ -697,6 +741,11 @@ cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh,
     {
       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;
@@ -704,7 +753,7 @@ cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh,
     switch (cgh->type)
     {
     case CGT_TRANSPORT_HANDLE:
-      handle = entry->transport_handle_;
+      handle = entry->transport_handle;
       if (NULL != handle)
         LOG_DEBUG ("Found TRANSPORT handle in cache for peer %u\n",
                    entry->peer_id);
@@ -736,17 +785,21 @@ cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh,
   if (NULL != handle)
   {
     if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task)
-      entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
+    {
+      if (NULL != search_suitable_cgh (entry, entry->cgh_qhead))
+        entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
+    }
     return cgh;
   }
+  op = NULL;
   switch (cgh->type)
   {
   case CGT_TRANSPORT_HANDLE:
-    if (NULL != entry->transport_op_)
+    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;
+    entry->transport_op = op;
     break;
   case CGT_CORE_HANDLE:
     if (NULL != entry->core_op)
@@ -755,6 +808,8 @@ cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh,
                                            &oprelease_get_handle_core);
     entry->core_op = op;
     break;
+  default:
+    GNUNET_assert (0);
   }
   GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op);
   GNUNET_TESTBED_operation_begin_wait_ (op);
@@ -781,15 +836,16 @@ cache_clear_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
   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_assert (GNUNET_YES == 
+                 GNUNET_CONTAINER_multihashmap_remove (cache, key, value));
+  close_handles (entry);
   GNUNET_free_non_null (entry->hello);
-  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_break (GNUNET_SCHEDULER_NO_TASK == entry->expire_task);
+  GNUNET_assert (NULL == entry->transport_handle);
+  GNUNET_assert (NULL == entry->transport_op);
+  GNUNET_assert (NULL == entry->core_handle);
+  GNUNET_assert (NULL == entry->core_op);
+  GNUNET_assert (NULL == entry->cfg);
   GNUNET_assert (NULL == entry->cgh_qhead);
   GNUNET_assert (NULL == entry->cgh_qtail);
   GNUNET_assert (NULL == entry->nctxt_qhead);
@@ -808,6 +864,12 @@ 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);
+  cache = NULL;
+  lru_cache_size = 0;
+  lru_cache_threshold_size = 0;
+  cache_size = 0;
+  lru_cache_head = NULL;
+  lru_cache_tail = NULL;
 }
 
 
@@ -860,6 +922,8 @@ GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh)
   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;
@@ -868,9 +932,7 @@ GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh)
   }
   else
   {
-    struct GSTCacheGetHandle *cgh2;
-
-    if (NULL != (cgh2 = search_suitable_cgh (entry, entry->cgh_qhead)))
+    if (NULL != search_suitable_cgh (entry, entry->cgh_qhead))
       entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
   }
 }
@@ -893,7 +955,7 @@ GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh)
  *          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
+ * @return the handle which can be used to cancel or mark that the handle is no
  *           longer being used
  */
 struct GSTCacheGetHandle *
@@ -933,7 +995,7 @@ GST_cache_get_handle_transport (unsigned int peer_id,
  *          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
+ * @return the handle which can be used to cancel or mark that the handle is no
  *           longer being used
  */
 struct GSTCacheGetHandle *
@@ -982,7 +1044,7 @@ GST_cache_lookup_hello (const unsigned int peer_id)
  * 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 peer_id the peer identity of the peer whose HELLO has to be cached
  * @param hello the HELLO message
  */
 void