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