72754121f467269a3b00cd1b467c5e9709640238
[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   }
323   while (NULL != (ctxt = entry->nctxt_qhead))
324   {
325     GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
326     GNUNET_free (ctxt);
327   }
328   LOG_DEBUG ("Cleaning up handles from an entry in cache\n");
329   if (NULL != entry->transport_handle_)
330   {
331     GNUNET_assert (NULL != entry->transport_op_);
332     GNUNET_TESTBED_operation_done (entry->transport_op_);
333     entry->transport_op_ = NULL;
334   }
335   if (NULL != entry->core_handle)
336   {
337     GNUNET_assert (NULL != entry->core_op);
338     GNUNET_TESTBED_operation_done (entry->core_op);
339     entry->core_op = NULL;
340   }
341   if (NULL != entry->cfg)
342   {
343     GNUNET_CONFIGURATION_destroy (entry->cfg);
344     entry->cfg = NULL;
345   }
346 }
347
348
349 /**
350  * Creates a new cache entry and then puts it into the cache's hashtable.
351  *
352  * @param key the hash code to use for inserting the newly created entry
353  * @param peer_id the index of the peer to tag the newly created entry
354  * @return the newly created entry
355  */
356 static struct CacheEntry *
357 add_entry (const struct GNUNET_HashCode *key, unsigned int peer_id)
358 {
359   struct CacheEntry *entry;
360
361   entry = GNUNET_malloc (sizeof (struct CacheEntry));
362   entry->peer_id = peer_id;
363   memcpy (&entry->key, key, sizeof (struct GNUNET_HashCode));
364   GNUNET_assert (GNUNET_OK ==
365                  GNUNET_CONTAINER_multihashmap_put (cache, &entry->key,
366                                                     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, 
444            entry->transport_handle_, 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,
459                         const struct GNUNET_PeerIdentity *peer,
460                         const enum CacheGetType type)
461 {
462   struct CacheEntry *entry = cls;
463   struct ConnectNotifyContext *ctxt;
464   struct ConnectNotifyContext *ctxt2;
465   GST_cache_peer_connect_notify cb;
466   void *cb_cls;
467
468   
469   for (ctxt=entry->nctxt_qhead; NULL != ctxt;)
470   {
471     GNUNET_assert (NULL != ctxt->cgh);
472     if (type != ctxt->cgh->type)
473     {
474       ctxt = ctxt->next;
475       continue;
476     }
477     if (0 != memcmp (ctxt->target, peer, sizeof (struct GNUNET_PeerIdentity)))
478     {
479       ctxt = ctxt->next;
480       continue;
481     }
482     cb = ctxt->cb;
483     cb_cls = ctxt->cb_cls;
484     ctxt->cgh->nctxt = NULL;
485     ctxt2 = ctxt->next;
486     GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
487     GNUNET_free (ctxt);
488     ctxt = ctxt2;
489     cb (cb_cls, peer);  
490   }
491   if (NULL == ctxt)
492     return;
493   
494 }
495
496
497 /**
498  * Function called to notify transport users that another
499  * peer connected to us.
500  *
501  * @param cls closure
502  * @param peer the peer that connected
503  * @param ats performance data
504  * @param ats_count number of entries in ats (excluding 0-termination)
505  */
506 static void 
507 transport_peer_connect_notify_cb (void *cls,
508                                   const struct GNUNET_PeerIdentity *peer,
509                                   const struct GNUNET_ATS_Information *ats,
510                                   uint32_t ats_count)
511 {
512   peer_connect_notify_cb (cls, peer, CGT_TRANSPORT_HANDLE);
513 }
514
515
516 /**
517  * Function called when resources for opening a connection to TRANSPORT are
518  * available.
519  *
520  * @param cls the cache entry
521  */
522 static void
523 opstart_get_handle_transport (void *cls)
524 {
525   struct CacheEntry *entry = cls;
526
527   GNUNET_assert (NULL != entry);
528   LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->peer_id);
529   entry->transport_handle_ = 
530       GNUNET_TRANSPORT_connect (entry->cfg,
531                                 NULL, entry,
532                                 NULL,
533                                 &transport_peer_connect_notify_cb, NULL);
534   if (NULL == entry->transport_handle_)
535   {
536     GNUNET_break (0);
537     return;
538   }
539   if (0 == entry->demand)
540     return;
541   if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
542     return;
543   if (NULL != search_suitable_cgh (entry, entry->cgh_qhead))
544     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
545 }
546
547
548 /**
549  * Function called when the operation responsible for opening a TRANSPORT
550  * connection is marked as done.
551  *
552  * @param cls the cache entry
553  */
554 static void
555 oprelease_get_handle_transport (void *cls)
556 {
557   struct CacheEntry *entry = cls;
558   
559   if (NULL == entry->transport_handle_)
560     return;
561   GNUNET_TRANSPORT_disconnect (entry->transport_handle_);
562   entry->transport_handle_ = NULL;
563 }
564
565
566 /**
567  * Function called after GNUNET_CORE_connect has succeeded (or failed
568  * for good).  Note that the private key of the peer is intentionally
569  * not exposed here; if you need it, your process should try to read
570  * the private key file directly (which should work if you are
571  * authorized...).  Implementations of this function must not call
572  * GNUNET_CORE_disconnect (other than by scheduling a new task to
573  * do this later).
574  *
575  * @param cls closure
576  * @param server handle to the server, NULL if we failed
577  * @param my_identity ID of this peer, NULL if we failed
578  */
579 static void 
580 core_startup_cb (void *cls,
581                  struct GNUNET_CORE_Handle * server,
582                  const struct GNUNET_PeerIdentity *my_identity)
583 {
584   struct CacheEntry *entry = cls;
585
586   if (NULL == my_identity)
587   {
588     GNUNET_break (0);
589     return;
590   }
591   GNUNET_assert (NULL == entry->peer_identity);
592   entry->core_handle = server;
593   entry->peer_identity = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
594   memcpy (entry->peer_identity, my_identity,
595           sizeof (struct GNUNET_PeerIdentity));
596   if (0 == entry->demand)
597     return;  
598   if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
599     return;
600   if (NULL != search_suitable_cgh (entry, entry->cgh_qhead))
601     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
602 }
603
604
605 /**
606  * Method called whenever a given peer connects at CORE level
607  *
608  * @param cls closure
609  * @param peer peer identity this notification is about
610  * @param atsi performance data for the connection
611  * @param atsi_count number of records in 'atsi'
612  */
613 static void 
614 core_peer_connect_cb (void *cls,
615                       const struct GNUNET_PeerIdentity * peer,
616                       const struct GNUNET_ATS_Information * atsi,
617                       unsigned int atsi_count)
618 {  
619   peer_connect_notify_cb (cls, peer, CGT_CORE_HANDLE);
620 }
621
622
623 /**
624  * Function called when resources for opening a connection to CORE are
625  * available.
626  *
627  * @param cls the cache entry
628  */
629 static void
630 opstart_get_handle_core (void *cls)
631 {
632   struct CacheEntry *entry = cls;
633   const struct GNUNET_CORE_MessageHandler no_handlers[] = {
634     {NULL, 0, 0}
635   };
636
637   GNUNET_assert (NULL != entry);
638   LOG_DEBUG ("Opening a CORE connection to peer %u\n", entry->peer_id);
639   /* void?: We also get the handle when the connection to CORE is successful */
640   (void) GNUNET_CORE_connect (entry->cfg,
641                               entry,
642                               &core_startup_cb,
643                               &core_peer_connect_cb,
644                               NULL, /* disconnect cb */
645                               NULL, /* inbound notify */
646                               GNUNET_NO,
647                               NULL, /* outbound notify */
648                               GNUNET_NO,
649                               no_handlers);
650   //GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->notify_task);
651 }
652
653
654 /**
655  * Function called when the operation responsible for opening a TRANSPORT
656  * connection is marked as done.
657  *
658  * @param cls the cache entry
659  */
660 static void
661 oprelease_get_handle_core (void *cls)
662 {
663   struct CacheEntry *entry = cls;
664   
665   if (NULL == entry->core_handle)
666     return;
667   GNUNET_CORE_disconnect (entry->core_handle);
668   entry->core_handle = NULL;
669   GNUNET_free_non_null (entry->peer_identity);
670   entry->peer_identity = NULL;
671 }
672
673
674 /**
675  * Function to get a handle with given configuration. The type of the handle is
676  * implicitly provided in the GSTCacheGetHandle. If the handle is already cached
677  * before, it will be retured in the given callback; the peer_id is used to
678  * lookup in the cache; if not, a new operation is started to open the transport
679  * handle and will be given in the callback when it is available.
680  *
681  * @param cls the cache entry
682  */
683 static struct GSTCacheGetHandle *
684 cache_get_handle (unsigned int peer_id,
685                   struct GSTCacheGetHandle *cgh,
686                   const struct GNUNET_CONFIGURATION_Handle *cfg,
687                   const struct GNUNET_PeerIdentity *target,
688                   GST_cache_peer_connect_notify connect_notify_cb,
689                   void *connect_notify_cb_cls)
690 {
691   struct GNUNET_HashCode key;
692   void *handle;
693   struct CacheEntry *entry;
694   struct ConnectNotifyContext *ctxt;
695   struct GNUNET_TESTBED_Operation *op;
696
697   GNUNET_assert (0 != cgh->type);
698   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
699   handle = NULL;
700   entry = cache_lookup (&key);
701   if (NULL != entry)
702   {
703     if (GNUNET_YES == entry->in_lru)
704     {
705       GNUNET_assert (0 == entry->demand);
706       GNUNET_assert (0 < lru_cache_size);
707       GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry);
708       lru_cache_size--;
709       entry->in_lru = GNUNET_NO;
710     }
711     switch (cgh->type)
712     {
713     case CGT_TRANSPORT_HANDLE:
714       handle = entry->transport_handle_;
715       if (NULL != handle)
716         LOG_DEBUG ("Found TRANSPORT handle in cache for peer %u\n", entry->peer_id);
717       break;
718     case CGT_CORE_HANDLE:
719       handle = entry->core_handle;
720       if (NULL != handle)
721         LOG_DEBUG ("Found CORE handle in cache for peer %u\n", entry->peer_id);
722       break;
723     }
724   }
725   if (NULL == entry)
726     entry = add_entry (&key, peer_id);
727   if (NULL == entry->cfg)
728     entry->cfg = GNUNET_CONFIGURATION_dup (cfg);
729   entry->demand++;
730   cgh->entry = entry;
731   GNUNET_CONTAINER_DLL_insert (entry->cgh_qhead, entry->cgh_qtail, cgh);
732   if ((NULL != target) && (NULL != connect_notify_cb))
733   {
734     ctxt = GNUNET_malloc (sizeof (struct ConnectNotifyContext));
735     ctxt->target = target;
736     ctxt->cb = connect_notify_cb;
737     ctxt->cb_cls = connect_notify_cb_cls;
738     GNUNET_assert (NULL == cgh->nctxt);
739     cgh->nctxt = ctxt;
740     ctxt->cgh = cgh;
741   }
742   if (NULL != handle)
743   {
744     if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task)
745       entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
746     return cgh;
747   }
748   switch (cgh->type)
749   {
750   case CGT_TRANSPORT_HANDLE:
751     if (NULL != entry->transport_op_)
752       return cgh;
753     op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_transport,
754                                            &oprelease_get_handle_transport);
755     entry->transport_op_ = op;
756     break;
757   case CGT_CORE_HANDLE:
758     if (NULL != entry->core_op)
759       return cgh;
760     op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_core,
761                                            &oprelease_get_handle_core);
762     entry->core_op = op;
763     break;
764   }
765   GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op);
766   GNUNET_TESTBED_operation_begin_wait_ (op);
767   return cgh;
768 }
769
770
771 /**
772  * Iterator over hash map entries.
773  *
774  * @param cls closure
775  * @param key current key code
776  * @param value value in the hash map
777  * @return GNUNET_YES if we should continue to
778  *         iterate,
779  *         GNUNET_NO if not.
780  */
781 static int
782 cache_clear_iterator (void *cls,
783                       const struct GNUNET_HashCode * key,
784                       void *value)
785 {
786   struct CacheEntry *entry = value;
787   static unsigned int ncleared;
788
789   GNUNET_assert (NULL != entry);
790   GNUNET_break (0 == entry->demand);
791   LOG_DEBUG ("Clearing entry %u of %u\n", ++ncleared, cache_size);
792   GNUNET_CONTAINER_multihashmap_remove (cache, key, value);
793   if (0 == entry->demand)
794     close_handles (entry);
795   GNUNET_free_non_null (entry->hello);
796   GNUNET_break (NULL == entry->transport_handle_);
797   GNUNET_break (NULL == entry->transport_op_);
798   GNUNET_break (NULL == entry->core_handle);
799   GNUNET_break (NULL == entry->core_op);
800   GNUNET_break (NULL == entry->cfg);
801   GNUNET_assert (NULL == entry->cgh_qhead);
802   GNUNET_assert (NULL == entry->cgh_qtail);
803   GNUNET_assert (NULL == entry->nctxt_qhead);
804   GNUNET_assert (NULL == entry->nctxt_qtail);
805   GNUNET_free (entry);
806   return GNUNET_YES;
807 }
808
809
810 /**
811  * Clear cache
812  */
813 void
814 GST_cache_clear ()
815 {
816   GNUNET_CONTAINER_multihashmap_iterate (cache, &cache_clear_iterator, NULL);
817   GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (cache));
818   GNUNET_CONTAINER_multihashmap_destroy (cache);
819 }
820
821
822 /**
823  * Initializes the cache
824  *
825  * @param size the size of the cache
826  */
827 void
828 GST_cache_init (unsigned int size)
829 {
830   if (0 == size)
831     return;
832   lru_cache_threshold_size = size;
833   if (size > 1)
834     size = size / 2;
835   cache = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_YES);
836 }
837
838
839 /**
840  * Mark the GetCacheHandle as being done if a handle has been provided already
841  * or as being cancelled if the callback for the handle hasn't been called.
842  *
843  * @param cgh the CacheGetHandle handle
844  */
845 void
846 GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh)
847 {
848   struct CacheEntry *entry;
849
850   entry = cgh->entry;
851   GNUNET_assert (NULL != entry);
852   GNUNET_assert (0 < entry->demand);
853   entry->demand--;
854   if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
855   {
856     GNUNET_SCHEDULER_cancel (entry->notify_task);
857     entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
858   }
859   GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh);
860   if (NULL != cgh->nctxt)
861   {
862     GNUNET_assert (cgh == cgh->nctxt->cgh);
863     if (GNUNET_YES == cgh->notify_called)
864       GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, cgh->nctxt);
865     GNUNET_free (cgh->nctxt);
866   }
867   GNUNET_free (cgh);  
868   if (0 == entry->demand)
869   {    
870     GNUNET_CONTAINER_DLL_insert_tail (lru_cache_head, lru_cache_tail, entry);
871     lru_cache_size++;
872     entry->in_lru = GNUNET_YES;
873     if (lru_cache_size > lru_cache_threshold_size)
874       close_handles (lru_cache_head);
875   }
876   else
877   {
878     struct GSTCacheGetHandle *cgh2;
879
880     if (NULL != (cgh2 = search_suitable_cgh (entry, entry->cgh_qhead)))
881       entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
882   }
883 }
884
885
886 /**
887  * Get a transport handle with the given configuration.  If the handle is
888  * already cached before, it will be retured in the given callback; the peer_id
889  * is used to lookup in the cache; if not, a new operation is started to open the
890  * transport handle and will be given in the callback when it is available.
891  *
892  * @param peer_id the index of the peer
893  * @param cfg the configuration with which the transport handle has to be
894  *          created if it was not present in the cache
895  * @param cb the callback to notify when the transport handle is available
896  * @param cb_cls the closure for the above callback
897  * @param target the peer identify of the peer whose connection to our TRANSPORT
898  *          subsystem will be notified through the connect_notify_cb. Can be NULL
899  * @param connect_notify_cb the callback to call when the given target peer is
900  *          connected. This callback will only be called once or never again (in
901  *          case the target peer cannot be connected). Can be NULL
902  * @param connect_notify_cb_cls the closure for the above callback
903  * @return the handle which can be used cancel or mark that the handle is no
904  *           longer being used
905  */
906 struct GSTCacheGetHandle *
907 GST_cache_get_handle_transport (unsigned int peer_id,
908                                 const struct GNUNET_CONFIGURATION_Handle *cfg,
909                                 GST_cache_handle_ready_cb cb,
910                                 void *cb_cls,
911                                 const struct GNUNET_PeerIdentity *target,
912                                 GST_cache_peer_connect_notify connect_notify_cb,
913                                 void *connect_notify_cb_cls)
914 {
915   struct GSTCacheGetHandle *cgh;
916
917   cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
918   cgh->cb = cb;
919   cgh->cb_cls = cb_cls;
920   cgh->type = CGT_TRANSPORT_HANDLE;
921   return cache_get_handle (peer_id, cgh, cfg,
922                            target, connect_notify_cb, connect_notify_cb_cls);
923 }
924
925
926 /**
927  * Get a CORE handle with the given configuration. If the handle is already
928  * cached before, it will be retured in the given callback; the peer_id is used
929  * to lookup in the cache. If the handle is not cached before, a new operation
930  * is started to open the CORE handle and will be given in the callback when it
931  * is available along with the peer identity
932  *
933  * @param peer_id the index of the peer
934  * @param cfg the configuration with which the transport handle has to be
935  *          created if it was not present in the cache
936  * @param cb the callback to notify when the transport handle is available
937  * @param cb_cls the closure for the above callback
938  * @param target the peer identify of the peer whose connection to our CORE
939  *          subsystem will be notified through the connect_notify_cb. Can be NULL
940  * @param connect_notify_cb the callback to call when the given target peer is
941  *          connected. This callback will only be called once or never again (in
942  *          case the target peer cannot be connected). Can be NULL
943  * @param connect_notify_cb_cls the closure for the above callback
944  * @return the handle which can be used cancel or mark that the handle is no
945  *           longer being used
946  */
947 struct GSTCacheGetHandle *
948 GST_cache_get_handle_core (unsigned int peer_id,
949                            const struct GNUNET_CONFIGURATION_Handle *cfg,
950                            GST_cache_handle_ready_cb cb,
951                            void *cb_cls,
952                            const struct GNUNET_PeerIdentity *target,
953                            GST_cache_peer_connect_notify connect_notify_cb,
954                            void *connect_notify_cb_cls)
955 {
956   struct GSTCacheGetHandle *cgh;
957
958   cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
959   cgh->cb = cb;
960   cgh->cb_cls = cb_cls;
961   cgh->type = CGT_CORE_HANDLE;
962   return cache_get_handle (peer_id, cgh, cfg,
963                            target, connect_notify_cb, connect_notify_cb_cls);
964 }
965
966
967 /**
968  * Looks up in the hello cache and returns the HELLO of the given peer
969  *
970  * @param peer_id the index of the peer whose HELLO has to be looked up
971  * @return the HELLO message; NULL if not found
972  */
973 const struct GNUNET_MessageHeader *
974 GST_cache_lookup_hello (const unsigned int peer_id)
975 {
976   struct CacheEntry *entry;
977   struct GNUNET_HashCode key;
978   
979   LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
980   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
981   entry = cache_lookup (&key);
982   if (NULL == entry)
983     return NULL;
984   if (NULL != entry->hello)
985     LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
986   return entry->hello;
987 }
988
989
990 /**
991  * Caches the HELLO of the given peer. Updates the HELLO if it was already
992  * cached before
993  *
994  * @param id the peer identity of the peer whose HELLO has to be cached
995  * @param hello the HELLO message
996  */
997 void
998 GST_cache_add_hello (const unsigned int peer_id,
999                      const struct GNUNET_MessageHeader *hello)
1000 {
1001   struct CacheEntry *entry;
1002   struct GNUNET_HashCode key;
1003
1004   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
1005   entry = GNUNET_CONTAINER_multihashmap_get (cache, &key);
1006   if (NULL == entry)
1007     entry = add_entry (&key, peer_id);
1008   GNUNET_free_non_null (entry->hello);
1009   entry->hello = GNUNET_copy_message (hello);
1010 }
1011
1012 /* end of gnunet-service-testbed_hc.c */