- verboser log, faster start
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_cache.c
1 /*
2   This file is part of GNUnet.
3   (C) 2012 Christian Grothoff (and other contributing authors)
4
5   GNUnet is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published
7   by the Free Software Foundation; either version 2, or (at your
8   option) any later version.
9
10   GNUnet is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with GNUnet; see the file COPYING.  If not, write to the
17   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18   Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file testbed/gnunet-service-testbed_cache.h
23  * @brief testbed cache implementation
24  * @author Sree Harsha Totakura
25  */
26 #include "gnunet-service-testbed.h"
27
28 /**
29  * Redefine LOG with a changed log component string
30  */
31 #ifdef LOG
32 #undef LOG
33 #endif
34 #define LOG(kind,...)                                   \
35   GNUNET_log_from (kind, "testbed-cache", __VA_ARGS__)
36
37
38 /**
39  * Time to expire a cache entry
40  */
41 #define CACHE_EXPIRY                            \
42   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
43
44
45 /**
46  * Type of cache-get requests
47  */
48 enum CacheGetType
49 {
50   /**
51    * Get transport handle
52    */
53   CGT_TRANSPORT_HANDLE = 1,
54
55   /**
56    * Get core handle
57    */
58   CGT_CORE_HANDLE
59 };
60
61
62 /**
63  * The cache-get request handle
64  */
65 struct GSTCacheGetHandle;
66
67
68 /**
69  * This context structure is used to maintain a queue of notifications to check
70  * which of them are to be notified when a peer is connected.
71  */
72 struct ConnectNotifyContext
73 {
74   /**
75    * The next ptr for the DLL
76    */
77   struct ConnectNotifyContext *next;
78
79   /**
80    * The prev ptr for the DLL
81    */
82   struct ConnectNotifyContext *prev;
83
84   /**
85    * The peer identity of the target peer. When this target peer is connected,
86    * call the notify callback
87    */
88   const struct GNUNET_PeerIdentity *target;
89
90   /**
91    * The notify callback to be called when the target peer is connected
92    */
93   GST_cache_peer_connect_notify cb;
94
95   /**
96    * The closure for the notify callback
97    */
98   void *cb_cls;
99
100   /**
101    * The GSTCacheGetHandle reposible for creating this context
102    */
103   struct GSTCacheGetHandle *cgh;
104
105 };
106
107
108 /**
109  * The cache-get request handle
110  */
111 struct GSTCacheGetHandle
112 {
113   /**
114    * The next ptr for the DLL. Used in struct CacheEntry
115    */
116   struct GSTCacheGetHandle *next;
117
118   /**
119    * The prev ptr for the DLL. Used in struct CacheEntry
120    */
121   struct GSTCacheGetHandle *prev;
122
123   /**
124    * The cache entry object this handle corresponds to
125    */
126   struct CacheEntry *entry;
127
128   /**
129    * The cache callback to call when a handle is available
130    */
131   GST_cache_handle_ready_cb cb;
132
133   /**
134    * The closure for the above callback
135    */
136   void *cb_cls;
137
138   /**
139    * The peer connect notify context created for this handle; can be NULL
140    */
141   struct ConnectNotifyContext *nctxt;
142
143   /**
144    * The type of this cache-get request
145    */
146   enum CacheGetType type;
147
148   /**
149    * Did we call the cache callback already?
150    */
151   int notify_called;
152 };
153
154 /**
155  * Cache entry
156  */
157 struct CacheEntry
158 {
159   /**
160    * DLL next ptr for least recently used cache entries
161    */
162   struct CacheEntry *next;
163
164   /**
165    * DLL prev ptr for least recently used cache entries
166    */
167   struct CacheEntry *prev;
168
169   /**
170    * The transport handle to the peer corresponding to this entry; can be NULL
171    */
172   struct GNUNET_TRANSPORT_Handle *transport_handle_;
173
174   /**
175    * The operation handle for transport handle
176    */
177   struct GNUNET_TESTBED_Operation *transport_op_;
178
179   /**
180    * The core handle to the peer corresponding to this entry; can be NULL
181    */
182   struct GNUNET_CORE_Handle *core_handle;
183
184   /**
185    * The operation handle for core handle
186    */
187   struct GNUNET_TESTBED_Operation *core_op;
188
189   /**
190    * The peer identity of this peer. Will be set upon opening a connection to
191    * the peers CORE service. Will be NULL until then and after the CORE
192    * connection is closed
193    */
194   struct GNUNET_PeerIdentity *peer_identity;
195
196   /**
197    * The configuration of the peer. Should be not NULL as long as the core_handle
198    * or transport_handle are valid
199    */
200   struct GNUNET_CONFIGURATION_Handle *cfg;
201
202   /**
203    * The key for this entry
204    */
205   struct GNUNET_HashCode key;
206
207   /**
208    * The HELLO message
209    */
210   struct GNUNET_MessageHeader *hello;
211
212   /**
213    * the head of the CacheGetHandle queue
214    */
215   struct GSTCacheGetHandle *cgh_qhead;
216
217   /**
218    * the tail of the CacheGetHandle queue
219    */
220   struct GSTCacheGetHandle *cgh_qtail;
221
222   /**
223    * DLL head for the queue of notifications contexts to check which of them are to
224    * be notified when a peer is connected.
225    */
226   struct ConnectNotifyContext *nctxt_qhead;
227
228   /**
229    * DLL tail for the queue of notifications contexts to check which of them are to
230    * be notified when a peer is connected.
231    */
232   struct ConnectNotifyContext *nctxt_qtail;
233
234   /**
235    * The task that calls the cache callback
236    */
237   GNUNET_SCHEDULER_TaskIdentifier notify_task;
238
239   /**
240    * The task to expire this cache entry, free any handlers it has opened and
241    * mark their corresponding operations as done.
242    */
243   GNUNET_SCHEDULER_TaskIdentifier expire_task;
244
245   /**
246    * Number of operations this cache entry is being used
247    */
248   unsigned int demand;
249
250   /**
251    * The id of the peer this entry corresponds to
252    */
253   unsigned int peer_id;
254
255   /**
256    * Is this entry in LRU cache queue?
257    */
258   unsigned int in_lru;
259 };
260
261
262 /**
263  * Hashmap to maintain cache
264  */
265 static struct GNUNET_CONTAINER_MultiHashMap *cache;
266
267 /**
268  * DLL head for least recently used cache entries; least recently used
269  * cache items are at the head. The cache enties are added to this queue when
270  * their demand becomes zero. They are removed from the queue when they are
271  * needed by any operation.
272  */
273 static struct CacheEntry *lru_cache_head;
274
275 /**
276  * DLL tail for least recently used cache entries; recently used cache
277  * items are at the tail.The cache enties are added to this queue when
278  * their demand becomes zero. They are removed from the queue when they are
279  * needed by any operation.
280  */
281 static struct CacheEntry *lru_cache_tail;
282
283 /**
284  * the size of the LRU queue
285  */
286 static unsigned int lru_cache_size;
287
288 /**
289  * the threshold size for the LRU queue
290  */
291 static unsigned int lru_cache_threshold_size;
292
293 /**
294  * The total number of elements in cache
295  */
296 static unsigned int cache_size;
297
298
299 /**
300  * Looks up in the cache and returns the entry
301  *
302  * @param id the peer identity of the peer whose corresponding entry has to be looked up
303  * @return the HELLO message; NULL if not found
304  */
305 static struct CacheEntry *
306 cache_lookup (const struct GNUNET_HashCode *key)
307 {
308   struct CacheEntry *entry;
309
310   if (NULL == cache)
311     return NULL;
312   entry = GNUNET_CONTAINER_multihashmap_get (cache, key);
313   return entry;
314 }
315
316
317 /**
318  * Function to disconnect the core and transport handles; free the existing
319  * configuration; and remove from the LRU cache list. The entry is left to be in
320  * the hash table so that the HELLO can still be found later
321  *
322  * @param entry the cache entry
323  */
324 static void
325 close_handles (struct CacheEntry *entry)
326 {
327   struct ConnectNotifyContext *ctxt;
328
329   GNUNET_assert (0 == entry->demand);
330   if (GNUNET_YES == entry->in_lru)
331   {
332     GNUNET_assert (0 < lru_cache_size);
333     if (GNUNET_SCHEDULER_NO_TASK != entry->expire_task)
334     {
335       GNUNET_SCHEDULER_cancel (entry->expire_task);
336       entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
337     }
338     GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry);
339     lru_cache_size--;
340     entry->in_lru = GNUNET_NO;
341   }
342   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->expire_task);
343   while (NULL != (ctxt = entry->nctxt_qhead))
344   {
345     GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
346     GNUNET_free (ctxt);
347   }
348   LOG_DEBUG ("Cleaning up handles from an entry in cache\n");
349   if (NULL != entry->transport_handle_)
350   {
351     GNUNET_assert (NULL != entry->transport_op_);
352     GNUNET_TESTBED_operation_done (entry->transport_op_);
353     entry->transport_op_ = NULL;
354   }
355   if (NULL != entry->core_op)
356   {
357     GNUNET_TESTBED_operation_done (entry->core_op);
358     entry->core_op = NULL;
359   }
360   GNUNET_assert (NULL == entry->core_handle);
361   if (NULL != entry->cfg)
362   {
363     GNUNET_CONFIGURATION_destroy (entry->cfg);
364     entry->cfg = NULL;
365   }
366 }
367
368
369 /**
370  * The task to expire this cache entry, free any handlers it has opened and
371  * mark their corresponding operations as done.
372  *
373  * @param cls the CacheEntry
374  * @param tc the scheduler task context
375  */
376 static void
377 expire_cache_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
378 {
379   struct CacheEntry *entry = cls;
380
381   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->expire_task);
382   entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
383   close_handles (entry);
384 }
385
386
387 /**
388  * Creates a new cache entry and then puts it into the cache's hashtable.
389  *
390  * @param key the hash code to use for inserting the newly created entry
391  * @param peer_id the index of the peer to tag the newly created entry
392  * @return the newly created entry
393  */
394 static struct CacheEntry *
395 add_entry (const struct GNUNET_HashCode *key, unsigned int peer_id)
396 {
397   struct CacheEntry *entry;
398
399   entry = GNUNET_malloc (sizeof (struct CacheEntry));
400   entry->peer_id = peer_id;
401   memcpy (&entry->key, key, sizeof (struct GNUNET_HashCode));
402   GNUNET_assert (GNUNET_OK ==
403                  GNUNET_CONTAINER_multihashmap_put (cache, &entry->key, entry,
404                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
405   cache_size++;
406   return entry;
407 }
408
409
410 /**
411  * Function to find a suitable GSTCacheGetHandle which is waiting for one of the
412  * handles in given entry to be available.
413  *
414  * @param entry the cache entry whose GSTCacheGetHandle list has to be searched
415  * @param head the starting list element in the GSTCacheGetHandle where the
416  *          search has to be begin
417  * @return a suitable GSTCacheGetHandle whose handle ready notify callback
418  *           hasn't been called yet. NULL if no such suitable GSTCacheGetHandle
419  *           is found
420  */
421 static struct GSTCacheGetHandle *
422 search_suitable_cgh (const struct CacheEntry *entry,
423                      const struct GSTCacheGetHandle *head)
424 {
425   const struct GSTCacheGetHandle *cgh;
426
427   for (cgh = head; NULL != cgh; cgh = cgh->next)
428   {
429     if (GNUNET_YES == cgh->notify_called)
430       return NULL;
431     switch (cgh->type)
432     {
433     case CGT_TRANSPORT_HANDLE:
434       if (NULL == entry->transport_handle_)
435         continue;
436       break;
437     case CGT_CORE_HANDLE:
438       if (NULL == entry->core_handle)
439         continue;
440       break;
441     }
442     break;
443   }
444   return (struct GSTCacheGetHandle *) cgh;
445 }
446
447
448 /**
449  * Task to call the handle ready notify callback of a queued GSTCacheGetHandle
450  * of an entry when one or all of its handles are available.
451  *
452  * @param cls the cache entry
453  * @param tc the task context from scheduler
454  */
455 static void
456 call_cgh_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
457 {
458   struct CacheEntry *entry = cls;
459   struct GSTCacheGetHandle *cgh;
460   const struct GSTCacheGetHandle *cgh2;
461
462   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->notify_task);
463   entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
464   cgh = search_suitable_cgh (entry, entry->cgh_qhead);
465   GNUNET_assert (NULL != cgh);
466   cgh2 = NULL;
467   if (NULL != cgh->next)
468     cgh2 = search_suitable_cgh (entry, cgh->next);
469   GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh);
470   cgh->notify_called = GNUNET_YES;
471   GNUNET_CONTAINER_DLL_insert_tail (entry->cgh_qhead, entry->cgh_qtail, cgh);
472   if (NULL != cgh2)
473     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
474   if (NULL != cgh->nctxt)
475   {                             /* Register the peer connect notify callback */
476     GNUNET_CONTAINER_DLL_insert_tail (entry->nctxt_qhead, entry->nctxt_qtail,
477                                       cgh->nctxt);
478   }
479   LOG_DEBUG ("Calling notify for handle type %u\n", cgh->type);
480   cgh->cb (cgh->cb_cls, entry->core_handle, entry->transport_handle_,
481            entry->peer_identity);
482 }
483
484
485 /**
486  * Function called from peer connect notify callbacks from CORE and TRANSPORT
487  * connections. This function calls the pendning peer connect notify callbacks
488  * which are queued in an entry.
489  *
490  * @param cls the cache entry
491  * @param peer the peer that connected
492  * @param type the type of the handle this notification corresponds to
493  */
494 static void
495 peer_connect_notify_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
496                         const enum CacheGetType type)
497 {
498   struct CacheEntry *entry = cls;
499   struct ConnectNotifyContext *ctxt;
500   struct ConnectNotifyContext *ctxt2;
501   GST_cache_peer_connect_notify cb;
502   void *cb_cls;
503
504
505   for (ctxt = entry->nctxt_qhead; NULL != ctxt;)
506   {
507     GNUNET_assert (NULL != ctxt->cgh);
508     if (type != ctxt->cgh->type)
509     {
510       ctxt = ctxt->next;
511       continue;
512     }
513     if (0 != memcmp (ctxt->target, peer, sizeof (struct GNUNET_PeerIdentity)))
514     {
515       ctxt = ctxt->next;
516       continue;
517     }
518     cb = ctxt->cb;
519     cb_cls = ctxt->cb_cls;
520     ctxt->cgh->nctxt = NULL;
521     ctxt2 = ctxt->next;
522     GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
523     GNUNET_free (ctxt);
524     ctxt = ctxt2;
525     cb (cb_cls, peer);
526   }
527   if (NULL == ctxt)
528     return;
529
530 }
531
532
533 /**
534  * Function called to notify transport users that another
535  * peer connected to us.
536  *
537  * @param cls closure
538  * @param peer the peer that connected
539  * @param ats performance data
540  * @param ats_count number of entries in ats (excluding 0-termination)
541  */
542 static void
543 transport_peer_connect_notify_cb (void *cls,
544                                   const struct GNUNET_PeerIdentity *peer,
545                                   const struct GNUNET_ATS_Information *ats,
546                                   uint32_t ats_count)
547 {
548   peer_connect_notify_cb (cls, peer, CGT_TRANSPORT_HANDLE);
549 }
550
551
552 /**
553  * Function called when resources for opening a connection to TRANSPORT are
554  * available.
555  *
556  * @param cls the cache entry
557  */
558 static void
559 opstart_get_handle_transport (void *cls)
560 {
561   struct CacheEntry *entry = cls;
562
563   GNUNET_assert (NULL != entry);
564   LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->peer_id);
565   entry->transport_handle_ =
566       GNUNET_TRANSPORT_connect (entry->cfg, NULL, entry, NULL,
567                                 &transport_peer_connect_notify_cb, NULL);
568   if (NULL == entry->transport_handle_)
569   {
570     GNUNET_break (0);
571     return;
572   }
573   if (0 == entry->demand)
574     return;
575   if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
576     return;
577   if (NULL != search_suitable_cgh (entry, entry->cgh_qhead))
578     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
579 }
580
581
582 /**
583  * Function called when the operation responsible for opening a TRANSPORT
584  * connection is marked as done.
585  *
586  * @param cls the cache entry
587  */
588 static void
589 oprelease_get_handle_transport (void *cls)
590 {
591   struct CacheEntry *entry = cls;
592
593   if (NULL == entry->transport_handle_)
594     return;
595   GNUNET_TRANSPORT_disconnect (entry->transport_handle_);
596   entry->transport_handle_ = NULL;
597 }
598
599
600 /**
601  * Function called after GNUNET_CORE_connect has succeeded (or failed
602  * for good).  Note that the private key of the peer is intentionally
603  * not exposed here; if you need it, your process should try to read
604  * the private key file directly (which should work if you are
605  * authorized...).  Implementations of this function must not call
606  * GNUNET_CORE_disconnect (other than by scheduling a new task to
607  * do this later).
608  *
609  * @param cls closure
610  * @param server handle to the server, NULL if we failed
611  * @param my_identity ID of this peer, NULL if we failed
612  */
613 static void
614 core_startup_cb (void *cls, struct GNUNET_CORE_Handle *server,
615                  const struct GNUNET_PeerIdentity *my_identity)
616 {
617   struct CacheEntry *entry = cls;
618
619   if (NULL == my_identity)
620   {
621     GNUNET_break (0);
622     return;
623   }
624   GNUNET_assert (NULL == entry->peer_identity);
625   entry->core_handle = server;
626   entry->peer_identity = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
627   memcpy (entry->peer_identity, my_identity,
628           sizeof (struct GNUNET_PeerIdentity));
629   if (0 == entry->demand)
630     return;
631   if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
632     return;
633   if (NULL != search_suitable_cgh (entry, entry->cgh_qhead))
634     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
635 }
636
637
638 /**
639  * Method called whenever a given peer connects at CORE level
640  *
641  * @param cls closure
642  * @param peer peer identity this notification is about
643  * @param atsi performance data for the connection
644  * @param atsi_count number of records in 'atsi'
645  */
646 static void
647 core_peer_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
648                       const struct GNUNET_ATS_Information *atsi,
649                       unsigned int atsi_count)
650 {
651   peer_connect_notify_cb (cls, peer, CGT_CORE_HANDLE);
652 }
653
654
655 /**
656  * Function called when resources for opening a connection to CORE are
657  * available.
658  *
659  * @param cls the cache entry
660  */
661 static void
662 opstart_get_handle_core (void *cls)
663 {
664   struct CacheEntry *entry = cls;
665
666   const struct GNUNET_CORE_MessageHandler no_handlers[] = {
667     {NULL, 0, 0}
668   };
669
670   GNUNET_assert (NULL != entry);
671   LOG_DEBUG ("Opening a CORE connection to peer %u\n", entry->peer_id);
672   entry->core_handle =
673       GNUNET_CORE_connect (entry->cfg, entry,        /* closure */
674                            &core_startup_cb, /* core startup notify */
675                            &core_peer_connect_cb,    /* peer connect notify */
676                            NULL,     /* peer disconnect notify */
677                            NULL,     /* inbound notify */
678                            GNUNET_NO,        /* inbound header only? */
679                            NULL,     /* outbound notify */
680                            GNUNET_NO,        /* outbound header only? */
681                            no_handlers);
682 }
683
684
685 /**
686  * Function called when the operation responsible for opening a TRANSPORT
687  * connection is marked as done.
688  *
689  * @param cls the cache entry
690  */
691 static void
692 oprelease_get_handle_core (void *cls)
693 {
694   struct CacheEntry *entry = cls;
695
696   if (NULL == entry->core_handle)
697     return;
698   GNUNET_CORE_disconnect (entry->core_handle);
699   entry->core_handle = NULL;
700   GNUNET_free_non_null (entry->peer_identity);
701   entry->peer_identity = NULL;
702 }
703
704
705 /**
706  * Function to get a handle with given configuration. The type of the handle is
707  * implicitly provided in the GSTCacheGetHandle. If the handle is already cached
708  * before, it will be retured in the given callback; the peer_id is used to
709  * lookup in the cache; if not, a new operation is started to open the transport
710  * handle and will be given in the callback when it is available.
711  *
712  * @param cls the cache entry
713  */
714 static struct GSTCacheGetHandle *
715 cache_get_handle (unsigned int peer_id, struct GSTCacheGetHandle *cgh,
716                   const struct GNUNET_CONFIGURATION_Handle *cfg,
717                   const struct GNUNET_PeerIdentity *target,
718                   GST_cache_peer_connect_notify connect_notify_cb,
719                   void *connect_notify_cb_cls)
720 {
721   struct GNUNET_HashCode key;
722   void *handle;
723   struct CacheEntry *entry;
724   struct ConnectNotifyContext *ctxt;
725   struct GNUNET_TESTBED_Operation *op;
726
727   GNUNET_assert (0 != cgh->type);
728   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
729   handle = NULL;
730   entry = cache_lookup (&key);
731   if (NULL != entry)
732   {
733     if (GNUNET_YES == entry->in_lru)
734     {
735       GNUNET_assert (0 == entry->demand);
736       GNUNET_assert (0 < lru_cache_size);
737       if (GNUNET_SCHEDULER_NO_TASK != entry->expire_task)
738       {
739         GNUNET_SCHEDULER_cancel (entry->expire_task);
740         entry->expire_task = GNUNET_SCHEDULER_NO_TASK;
741       }
742       GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry);
743       lru_cache_size--;
744       entry->in_lru = GNUNET_NO;
745     }
746     switch (cgh->type)
747     {
748     case CGT_TRANSPORT_HANDLE:
749       handle = entry->transport_handle_;
750       if (NULL != handle)
751         LOG_DEBUG ("Found TRANSPORT handle in cache for peer %u\n",
752                    entry->peer_id);
753       break;
754     case CGT_CORE_HANDLE:
755       handle = entry->core_handle;
756       if (NULL != handle)
757         LOG_DEBUG ("Found CORE handle in cache for peer %u\n", entry->peer_id);
758       break;
759     }
760   }
761   if (NULL == entry)
762     entry = add_entry (&key, peer_id);
763   if (NULL == entry->cfg)
764     entry->cfg = GNUNET_CONFIGURATION_dup (cfg);
765   entry->demand++;
766   cgh->entry = entry;
767   GNUNET_CONTAINER_DLL_insert (entry->cgh_qhead, entry->cgh_qtail, cgh);
768   if ((NULL != target) && (NULL != connect_notify_cb))
769   {
770     ctxt = GNUNET_malloc (sizeof (struct ConnectNotifyContext));
771     ctxt->target = target;
772     ctxt->cb = connect_notify_cb;
773     ctxt->cb_cls = connect_notify_cb_cls;
774     GNUNET_assert (NULL == cgh->nctxt);
775     cgh->nctxt = ctxt;
776     ctxt->cgh = cgh;
777   }
778   if (NULL != handle)
779   {
780     if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task)
781       entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
782     return cgh;
783   }
784   switch (cgh->type)
785   {
786   case CGT_TRANSPORT_HANDLE:
787     if (NULL != entry->transport_op_)
788       return cgh;
789     op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_transport,
790                                            &oprelease_get_handle_transport);
791     entry->transport_op_ = op;
792     break;
793   case CGT_CORE_HANDLE:
794     if (NULL != entry->core_op)
795       return cgh;
796     op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_core,
797                                            &oprelease_get_handle_core);
798     entry->core_op = op;
799     break;
800   }
801   GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op);
802   GNUNET_TESTBED_operation_begin_wait_ (op);
803   return cgh;
804 }
805
806
807 /**
808  * Iterator over hash map entries.
809  *
810  * @param cls closure
811  * @param key current key code
812  * @param value value in the hash map
813  * @return GNUNET_YES if we should continue to
814  *         iterate,
815  *         GNUNET_NO if not.
816  */
817 static int
818 cache_clear_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
819 {
820   struct CacheEntry *entry = value;
821   static unsigned int ncleared;
822
823   GNUNET_assert (NULL != entry);
824   GNUNET_break (0 == entry->demand);
825   LOG_DEBUG ("Clearing entry %u of %u\n", ++ncleared, cache_size);
826   GNUNET_CONTAINER_multihashmap_remove (cache, key, value);
827   close_handles (entry);
828   GNUNET_free_non_null (entry->hello);
829   GNUNET_break (GNUNET_SCHEDULER_NO_TASK == entry->expire_task);
830   GNUNET_break (NULL == entry->transport_handle_);
831   GNUNET_break (NULL == entry->transport_op_);
832   GNUNET_break (NULL == entry->core_handle);
833   GNUNET_break (NULL == entry->core_op);
834   GNUNET_break (NULL == entry->cfg);
835   GNUNET_assert (NULL == entry->cgh_qhead);
836   GNUNET_assert (NULL == entry->cgh_qtail);
837   GNUNET_assert (NULL == entry->nctxt_qhead);
838   GNUNET_assert (NULL == entry->nctxt_qtail);
839   GNUNET_free (entry);
840   return GNUNET_YES;
841 }
842
843
844 /**
845  * Clear cache
846  */
847 void
848 GST_cache_clear ()
849 {
850   GNUNET_CONTAINER_multihashmap_iterate (cache, &cache_clear_iterator, NULL);
851   GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (cache));
852   GNUNET_CONTAINER_multihashmap_destroy (cache);
853 }
854
855
856 /**
857  * Initializes the cache
858  *
859  * @param size the size of the cache
860  */
861 void
862 GST_cache_init (unsigned int size)
863 {
864   if (0 == size)
865     return;
866   lru_cache_threshold_size = size;
867   if (size > 1)
868     size = size / 2;
869   cache = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_YES);
870 }
871
872
873 /**
874  * Mark the GetCacheHandle as being done if a handle has been provided already
875  * or as being cancelled if the callback for the handle hasn't been called.
876  *
877  * @param cgh the CacheGetHandle handle
878  */
879 void
880 GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh)
881 {
882   struct CacheEntry *entry;
883
884   entry = cgh->entry;
885   GNUNET_assert (NULL != entry);
886   GNUNET_assert (0 < entry->demand);
887   entry->demand--;
888   if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
889   {
890     GNUNET_SCHEDULER_cancel (entry->notify_task);
891     entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
892   }
893   GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh);
894   if (NULL != cgh->nctxt)
895   {
896     GNUNET_assert (cgh == cgh->nctxt->cgh);
897     if (GNUNET_YES == cgh->notify_called)
898       GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail,
899                                    cgh->nctxt);
900     GNUNET_free (cgh->nctxt);
901   }
902   GNUNET_free (cgh);
903   if (0 == entry->demand)
904   {
905     entry->expire_task =
906         GNUNET_SCHEDULER_add_delayed (CACHE_EXPIRY, &expire_cache_entry, entry);
907     GNUNET_CONTAINER_DLL_insert_tail (lru_cache_head, lru_cache_tail, entry);
908     lru_cache_size++;
909     entry->in_lru = GNUNET_YES;
910     if (lru_cache_size > lru_cache_threshold_size)
911       close_handles (lru_cache_head);
912   }
913   else
914   {
915     struct GSTCacheGetHandle *cgh2;
916
917     if (NULL != (cgh2 = search_suitable_cgh (entry, entry->cgh_qhead)))
918       entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
919   }
920 }
921
922
923 /**
924  * Get a transport handle with the given configuration.  If the handle is
925  * already cached before, it will be retured in the given callback; the peer_id
926  * is used to lookup in the cache; if not, a new operation is started to open the
927  * transport handle and will be given in the callback when it is available.
928  *
929  * @param peer_id the index of the peer
930  * @param cfg the configuration with which the transport handle has to be
931  *          created if it was not present in the cache
932  * @param cb the callback to notify when the transport handle is available
933  * @param cb_cls the closure for the above callback
934  * @param target the peer identify of the peer whose connection to our TRANSPORT
935  *          subsystem will be notified through the connect_notify_cb. Can be NULL
936  * @param connect_notify_cb the callback to call when the given target peer is
937  *          connected. This callback will only be called once or never again (in
938  *          case the target peer cannot be connected). Can be NULL
939  * @param connect_notify_cb_cls the closure for the above callback
940  * @return the handle which can be used cancel or mark that the handle is no
941  *           longer being used
942  */
943 struct GSTCacheGetHandle *
944 GST_cache_get_handle_transport (unsigned int peer_id,
945                                 const struct GNUNET_CONFIGURATION_Handle *cfg,
946                                 GST_cache_handle_ready_cb cb, void *cb_cls,
947                                 const struct GNUNET_PeerIdentity *target,
948                                 GST_cache_peer_connect_notify connect_notify_cb,
949                                 void *connect_notify_cb_cls)
950 {
951   struct GSTCacheGetHandle *cgh;
952
953   cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
954   cgh->cb = cb;
955   cgh->cb_cls = cb_cls;
956   cgh->type = CGT_TRANSPORT_HANDLE;
957   return cache_get_handle (peer_id, cgh, cfg, target, connect_notify_cb,
958                            connect_notify_cb_cls);
959 }
960
961
962 /**
963  * Get a CORE handle with the given configuration. If the handle is already
964  * cached before, it will be retured in the given callback; the peer_id is used
965  * to lookup in the cache. If the handle is not cached before, a new operation
966  * is started to open the CORE handle and will be given in the callback when it
967  * is available along with the peer identity
968  *
969  * @param peer_id the index of the peer
970  * @param cfg the configuration with which the transport handle has to be
971  *          created if it was not present in the cache
972  * @param cb the callback to notify when the transport handle is available
973  * @param cb_cls the closure for the above callback
974  * @param target the peer identify of the peer whose connection to our CORE
975  *          subsystem will be notified through the connect_notify_cb. Can be NULL
976  * @param connect_notify_cb the callback to call when the given target peer is
977  *          connected. This callback will only be called once or never again (in
978  *          case the target peer cannot be connected). Can be NULL
979  * @param connect_notify_cb_cls the closure for the above callback
980  * @return the handle which can be used cancel or mark that the handle is no
981  *           longer being used
982  */
983 struct GSTCacheGetHandle *
984 GST_cache_get_handle_core (unsigned int peer_id,
985                            const struct GNUNET_CONFIGURATION_Handle *cfg,
986                            GST_cache_handle_ready_cb cb, void *cb_cls,
987                            const struct GNUNET_PeerIdentity *target,
988                            GST_cache_peer_connect_notify connect_notify_cb,
989                            void *connect_notify_cb_cls)
990 {
991   struct GSTCacheGetHandle *cgh;
992
993   cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
994   cgh->cb = cb;
995   cgh->cb_cls = cb_cls;
996   cgh->type = CGT_CORE_HANDLE;
997   return cache_get_handle (peer_id, cgh, cfg, target, connect_notify_cb,
998                            connect_notify_cb_cls);
999 }
1000
1001
1002 /**
1003  * Looks up in the hello cache and returns the HELLO of the given peer
1004  *
1005  * @param peer_id the index of the peer whose HELLO has to be looked up
1006  * @return the HELLO message; NULL if not found
1007  */
1008 const struct GNUNET_MessageHeader *
1009 GST_cache_lookup_hello (const unsigned int peer_id)
1010 {
1011   struct CacheEntry *entry;
1012   struct GNUNET_HashCode key;
1013
1014   LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
1015   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
1016   entry = cache_lookup (&key);
1017   if (NULL == entry)
1018     return NULL;
1019   if (NULL != entry->hello)
1020     LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
1021   return entry->hello;
1022 }
1023
1024
1025 /**
1026  * Caches the HELLO of the given peer. Updates the HELLO if it was already
1027  * cached before
1028  *
1029  * @param id the peer identity of the peer whose HELLO has to be cached
1030  * @param hello the HELLO message
1031  */
1032 void
1033 GST_cache_add_hello (const unsigned int peer_id,
1034                      const struct GNUNET_MessageHeader *hello)
1035 {
1036   struct CacheEntry *entry;
1037   struct GNUNET_HashCode key;
1038
1039   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
1040   entry = GNUNET_CONTAINER_multihashmap_get (cache, &key);
1041   if (NULL == entry)
1042     entry = add_entry (&key, peer_id);
1043   GNUNET_free_non_null (entry->hello);
1044   entry->hello = GNUNET_copy_message (hello);
1045 }
1046
1047 /* end of gnunet-service-testbed_hc.c */